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