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