android_media_Utils.cpp revision 3c233ee291cfe1b765184920b6b7a69cb1bb82d9
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 // Reset the PreviewImageData to its default. 85 image_data = piex::PreviewImageData(); 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