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