android_media_Utils.cpp revision d4c505c9b4d5b0e743301e3f385895efdb9785d4
1/* 2 * Copyright 2011, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17// #define LOG_NDEBUG 0 18#define LOG_TAG "AndroidMediaUtils" 19 20#include <utils/Log.h> 21#include "android_media_Utils.h" 22 23#include <media/stagefright/foundation/ADebug.h> 24#include <media/stagefright/foundation/ABuffer.h> 25#include <media/stagefright/foundation/AMessage.h> 26 27#include <nativehelper/ScopedLocalRef.h> 28 29namespace android { 30 31FileStream::FileStream(const String8 filename) 32 : mPosition(0), 33 mSize(0) { 34 mFile = fopen(filename.string(), "r"); 35 if (mFile == NULL) { 36 return; 37 } 38 // Get the size. 39 fseek(mFile, 0l, SEEK_END); 40 mSize = ftell(mFile); 41 fseek(mFile, 0l, SEEK_SET); 42} 43 44FileStream::~FileStream() { 45 if (mFile != NULL) { 46 fclose(mFile); 47 mFile = NULL; 48 } 49} 50 51piex::Error FileStream::GetData( 52 const size_t offset, const size_t length, std::uint8_t* data) { 53 if (mFile == NULL) { 54 return piex::Error::kFail; 55 } 56 57 // Seek first. 58 if (mPosition != offset) { 59 fseek(mFile, offset, SEEK_SET); 60 } 61 62 // Read bytes. 63 size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile); 64 mPosition += size; 65 66 // Handle errors. 67 if (ferror(mFile) || (size == 0 && feof(mFile))) { 68 ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length); 69 return piex::Error::kFail; 70 } 71 return piex::Error::kOk; 72} 73 74bool FileStream::exists() const { 75 return mFile != NULL; 76} 77 78size_t FileStream::size() const { 79 return mSize; 80} 81 82bool GetExifFromRawImage( 83 FileStream* stream, const String8& filename, piex::PreviewImageData& image_data) { 84 memset(&image_data, 0, sizeof(image_data)); 85 86 if (!stream->exists()) { 87 // File is not exists. 88 ALOGV("File is not exists: %s", filename.string()); 89 return false; 90 } 91 92 if (!piex::IsRaw(stream)) { 93 // Format not supported. 94 ALOGV("Format not supported: %s", filename.string()); 95 return false; 96 } 97 98 piex::Error err = piex::GetPreviewImageData(stream, &image_data); 99 100 if (err != piex::Error::kOk) { 101 // The input data seems to be broken. 102 ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err); 103 return false; 104 } 105 106 if (image_data.thumbnail_offset + image_data.thumbnail_length > stream->size()) { 107 // Corrupted image. 108 ALOGV("Corrupted file: %s", filename.string()); 109 return false; 110 } 111 112 return true; 113} 114 115bool ConvertKeyValueArraysToKeyedVector( 116 JNIEnv *env, jobjectArray keys, jobjectArray values, 117 KeyedVector<String8, String8>* keyedVector) { 118 119 int nKeyValuePairs = 0; 120 bool failed = false; 121 if (keys != NULL && values != NULL) { 122 nKeyValuePairs = env->GetArrayLength(keys); 123 failed = (nKeyValuePairs != env->GetArrayLength(values)); 124 } 125 126 if (!failed) { 127 failed = ((keys != NULL && values == NULL) || 128 (keys == NULL && values != NULL)); 129 } 130 131 if (failed) { 132 ALOGE("keys and values arrays have different length"); 133 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 134 return false; 135 } 136 137 for (int i = 0; i < nKeyValuePairs; ++i) { 138 // No need to check on the ArrayIndexOutOfBoundsException, since 139 // it won't happen here. 140 jstring key = (jstring) env->GetObjectArrayElement(keys, i); 141 jstring value = (jstring) env->GetObjectArrayElement(values, i); 142 143 const char* keyStr = env->GetStringUTFChars(key, NULL); 144 if (!keyStr) { // OutOfMemoryError 145 return false; 146 } 147 148 const char* valueStr = env->GetStringUTFChars(value, NULL); 149 if (!valueStr) { // OutOfMemoryError 150 env->ReleaseStringUTFChars(key, keyStr); 151 return false; 152 } 153 154 keyedVector->add(String8(keyStr), String8(valueStr)); 155 156 env->ReleaseStringUTFChars(key, keyStr); 157 env->ReleaseStringUTFChars(value, valueStr); 158 env->DeleteLocalRef(key); 159 env->DeleteLocalRef(value); 160 } 161 return true; 162} 163 164static jobject makeIntegerObject(JNIEnv *env, int32_t value) { 165 ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer")); 166 CHECK(clazz.get() != NULL); 167 168 jmethodID integerConstructID = 169 env->GetMethodID(clazz.get(), "<init>", "(I)V"); 170 CHECK(integerConstructID != NULL); 171 172 return env->NewObject(clazz.get(), integerConstructID, value); 173} 174 175static jobject makeLongObject(JNIEnv *env, int64_t value) { 176 ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long")); 177 CHECK(clazz.get() != NULL); 178 179 jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V"); 180 CHECK(longConstructID != NULL); 181 182 return env->NewObject(clazz.get(), longConstructID, value); 183} 184 185static jobject makeFloatObject(JNIEnv *env, float value) { 186 ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float")); 187 CHECK(clazz.get() != NULL); 188 189 jmethodID floatConstructID = 190 env->GetMethodID(clazz.get(), "<init>", "(F)V"); 191 CHECK(floatConstructID != NULL); 192 193 return env->NewObject(clazz.get(), floatConstructID, value); 194} 195 196static jobject makeByteBufferObject( 197 JNIEnv *env, const void *data, size_t size) { 198 jbyteArray byteArrayObj = env->NewByteArray(size); 199 env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data); 200 201 ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer")); 202 CHECK(clazz.get() != NULL); 203 204 jmethodID byteBufWrapID = 205 env->GetStaticMethodID( 206 clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;"); 207 CHECK(byteBufWrapID != NULL); 208 209 jobject byteBufObj = env->CallStaticObjectMethod( 210 clazz.get(), byteBufWrapID, byteArrayObj); 211 212 env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL; 213 214 return byteBufObj; 215} 216 217static void SetMapInt32( 218 JNIEnv *env, jobject hashMapObj, jmethodID hashMapPutID, 219 const char *key, int32_t value) { 220 jstring keyObj = env->NewStringUTF(key); 221 jobject valueObj = makeIntegerObject(env, value); 222 223 env->CallObjectMethod(hashMapObj, hashMapPutID, keyObj, valueObj); 224 225 env->DeleteLocalRef(valueObj); valueObj = NULL; 226 env->DeleteLocalRef(keyObj); keyObj = NULL; 227} 228 229status_t ConvertMessageToMap( 230 JNIEnv *env, const sp<AMessage> &msg, jobject *map) { 231 ScopedLocalRef<jclass> hashMapClazz( 232 env, env->FindClass("java/util/HashMap")); 233 234 if (hashMapClazz.get() == NULL) { 235 return -EINVAL; 236 } 237 238 jmethodID hashMapConstructID = 239 env->GetMethodID(hashMapClazz.get(), "<init>", "()V"); 240 241 if (hashMapConstructID == NULL) { 242 return -EINVAL; 243 } 244 245 jmethodID hashMapPutID = 246 env->GetMethodID( 247 hashMapClazz.get(), 248 "put", 249 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); 250 251 if (hashMapPutID == NULL) { 252 return -EINVAL; 253 } 254 255 jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID); 256 257 for (size_t i = 0; i < msg->countEntries(); ++i) { 258 AMessage::Type valueType; 259 const char *key = msg->getEntryNameAt(i, &valueType); 260 261 jobject valueObj = NULL; 262 263 switch (valueType) { 264 case AMessage::kTypeInt32: 265 { 266 int32_t val; 267 CHECK(msg->findInt32(key, &val)); 268 269 valueObj = makeIntegerObject(env, val); 270 break; 271 } 272 273 case AMessage::kTypeInt64: 274 { 275 int64_t val; 276 CHECK(msg->findInt64(key, &val)); 277 278 valueObj = makeLongObject(env, val); 279 break; 280 } 281 282 case AMessage::kTypeFloat: 283 { 284 float val; 285 CHECK(msg->findFloat(key, &val)); 286 287 valueObj = makeFloatObject(env, val); 288 break; 289 } 290 291 case AMessage::kTypeString: 292 { 293 AString val; 294 CHECK(msg->findString(key, &val)); 295 296 valueObj = env->NewStringUTF(val.c_str()); 297 break; 298 } 299 300 case AMessage::kTypeBuffer: 301 { 302 sp<ABuffer> buffer; 303 CHECK(msg->findBuffer(key, &buffer)); 304 305 valueObj = makeByteBufferObject( 306 env, buffer->data(), buffer->size()); 307 break; 308 } 309 310 case AMessage::kTypeRect: 311 { 312 int32_t left, top, right, bottom; 313 CHECK(msg->findRect(key, &left, &top, &right, &bottom)); 314 315 SetMapInt32( 316 env, 317 hashMap, 318 hashMapPutID, 319 AStringPrintf("%s-left", key).c_str(), 320 left); 321 322 SetMapInt32( 323 env, 324 hashMap, 325 hashMapPutID, 326 AStringPrintf("%s-top", key).c_str(), 327 top); 328 329 SetMapInt32( 330 env, 331 hashMap, 332 hashMapPutID, 333 AStringPrintf("%s-right", key).c_str(), 334 right); 335 336 SetMapInt32( 337 env, 338 hashMap, 339 hashMapPutID, 340 AStringPrintf("%s-bottom", key).c_str(), 341 bottom); 342 break; 343 } 344 345 default: 346 break; 347 } 348 349 if (valueObj != NULL) { 350 jstring keyObj = env->NewStringUTF(key); 351 352 env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj); 353 354 env->DeleteLocalRef(keyObj); keyObj = NULL; 355 env->DeleteLocalRef(valueObj); valueObj = NULL; 356 } 357 } 358 359 *map = hashMap; 360 361 return OK; 362} 363 364status_t ConvertKeyValueArraysToMessage( 365 JNIEnv *env, jobjectArray keys, jobjectArray values, 366 sp<AMessage> *out) { 367 ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String")); 368 CHECK(stringClass.get() != NULL); 369 ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer")); 370 CHECK(integerClass.get() != NULL); 371 ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long")); 372 CHECK(longClass.get() != NULL); 373 ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float")); 374 CHECK(floatClass.get() != NULL); 375 ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer")); 376 CHECK(byteBufClass.get() != NULL); 377 378 sp<AMessage> msg = new AMessage; 379 380 jsize numEntries = 0; 381 382 if (keys != NULL) { 383 if (values == NULL) { 384 return -EINVAL; 385 } 386 387 numEntries = env->GetArrayLength(keys); 388 389 if (numEntries != env->GetArrayLength(values)) { 390 return -EINVAL; 391 } 392 } else if (values != NULL) { 393 return -EINVAL; 394 } 395 396 for (jsize i = 0; i < numEntries; ++i) { 397 jobject keyObj = env->GetObjectArrayElement(keys, i); 398 399 if (!env->IsInstanceOf(keyObj, stringClass.get())) { 400 return -EINVAL; 401 } 402 403 const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL); 404 405 if (tmp == NULL) { 406 return -ENOMEM; 407 } 408 409 AString key = tmp; 410 411 env->ReleaseStringUTFChars((jstring)keyObj, tmp); 412 tmp = NULL; 413 414 jobject valueObj = env->GetObjectArrayElement(values, i); 415 416 if (env->IsInstanceOf(valueObj, stringClass.get())) { 417 const char *value = env->GetStringUTFChars((jstring)valueObj, NULL); 418 419 if (value == NULL) { 420 return -ENOMEM; 421 } 422 423 msg->setString(key.c_str(), value); 424 425 env->ReleaseStringUTFChars((jstring)valueObj, value); 426 value = NULL; 427 } else if (env->IsInstanceOf(valueObj, integerClass.get())) { 428 jmethodID intValueID = 429 env->GetMethodID(integerClass.get(), "intValue", "()I"); 430 CHECK(intValueID != NULL); 431 432 jint value = env->CallIntMethod(valueObj, intValueID); 433 434 msg->setInt32(key.c_str(), value); 435 } else if (env->IsInstanceOf(valueObj, longClass.get())) { 436 jmethodID longValueID = 437 env->GetMethodID(longClass.get(), "longValue", "()J"); 438 CHECK(longValueID != NULL); 439 440 jlong value = env->CallLongMethod(valueObj, longValueID); 441 442 msg->setInt64(key.c_str(), value); 443 } else if (env->IsInstanceOf(valueObj, floatClass.get())) { 444 jmethodID floatValueID = 445 env->GetMethodID(floatClass.get(), "floatValue", "()F"); 446 CHECK(floatValueID != NULL); 447 448 jfloat value = env->CallFloatMethod(valueObj, floatValueID); 449 450 msg->setFloat(key.c_str(), value); 451 } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) { 452 jmethodID positionID = 453 env->GetMethodID(byteBufClass.get(), "position", "()I"); 454 CHECK(positionID != NULL); 455 456 jmethodID limitID = 457 env->GetMethodID(byteBufClass.get(), "limit", "()I"); 458 CHECK(limitID != NULL); 459 460 jint position = env->CallIntMethod(valueObj, positionID); 461 jint limit = env->CallIntMethod(valueObj, limitID); 462 463 sp<ABuffer> buffer = new ABuffer(limit - position); 464 465 void *data = env->GetDirectBufferAddress(valueObj); 466 467 if (data != NULL) { 468 memcpy(buffer->data(), 469 (const uint8_t *)data + position, 470 buffer->size()); 471 } else { 472 jmethodID arrayID = 473 env->GetMethodID(byteBufClass.get(), "array", "()[B"); 474 CHECK(arrayID != NULL); 475 476 jbyteArray byteArray = 477 (jbyteArray)env->CallObjectMethod(valueObj, arrayID); 478 CHECK(byteArray != NULL); 479 480 env->GetByteArrayRegion( 481 byteArray, 482 position, 483 buffer->size(), 484 (jbyte *)buffer->data()); 485 486 env->DeleteLocalRef(byteArray); byteArray = NULL; 487 } 488 489 msg->setBuffer(key.c_str(), buffer); 490 } 491 } 492 493 *out = msg; 494 495 return OK; 496} 497 498} // namespace android 499 500