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