1 /* 2 * Copyright 2012, 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 "MediaExtractor-JNI" 19 #include <utils/Log.h> 20 21 #include "android_media_MediaExtractor.h" 22 23 #include "android_media_Utils.h" 24 #include "android_runtime/AndroidRuntime.h" 25 #include "jni.h" 26 #include "JNIHelp.h" 27 28 #include <media/hardware/CryptoAPI.h> 29 #include <media/stagefright/foundation/ABuffer.h> 30 #include <media/stagefright/foundation/ADebug.h> 31 #include <media/stagefright/foundation/AMessage.h> 32 #include <media/stagefright/DataSource.h> 33 #include <media/stagefright/MediaErrors.h> 34 #include <media/stagefright/MetaData.h> 35 #include <media/stagefright/NuMediaExtractor.h> 36 37 namespace android { 38 39 struct fields_t { 40 jfieldID context; 41 42 jmethodID cryptoInfoSetID; 43 }; 44 45 static fields_t gFields; 46 47 //////////////////////////////////////////////////////////////////////////////// 48 49 JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz) 50 : mClass(NULL), 51 mObject(NULL) { 52 jclass clazz = env->GetObjectClass(thiz); 53 CHECK(clazz != NULL); 54 55 mClass = (jclass)env->NewGlobalRef(clazz); 56 mObject = env->NewWeakGlobalRef(thiz); 57 58 mImpl = new NuMediaExtractor; 59 } 60 61 JMediaExtractor::~JMediaExtractor() { 62 JNIEnv *env = AndroidRuntime::getJNIEnv(); 63 64 env->DeleteWeakGlobalRef(mObject); 65 mObject = NULL; 66 env->DeleteGlobalRef(mClass); 67 mClass = NULL; 68 } 69 70 status_t JMediaExtractor::setDataSource( 71 const char *path, const KeyedVector<String8, String8> *headers) { 72 return mImpl->setDataSource(path, headers); 73 } 74 75 status_t JMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) { 76 return mImpl->setDataSource(fd, offset, size); 77 } 78 79 size_t JMediaExtractor::countTracks() const { 80 return mImpl->countTracks(); 81 } 82 83 status_t JMediaExtractor::getTrackFormat(size_t index, jobject *format) const { 84 sp<AMessage> msg; 85 status_t err; 86 if ((err = mImpl->getTrackFormat(index, &msg)) != OK) { 87 return err; 88 } 89 90 JNIEnv *env = AndroidRuntime::getJNIEnv(); 91 92 return ConvertMessageToMap(env, msg, format); 93 } 94 95 status_t JMediaExtractor::selectTrack(size_t index) { 96 return mImpl->selectTrack(index); 97 } 98 99 status_t JMediaExtractor::unselectTrack(size_t index) { 100 return mImpl->unselectTrack(index); 101 } 102 103 status_t JMediaExtractor::seekTo( 104 int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) { 105 return mImpl->seekTo(timeUs, mode); 106 } 107 108 status_t JMediaExtractor::advance() { 109 return mImpl->advance(); 110 } 111 112 status_t JMediaExtractor::readSampleData( 113 jobject byteBuf, size_t offset, size_t *sampleSize) { 114 JNIEnv *env = AndroidRuntime::getJNIEnv(); 115 116 void *dst = env->GetDirectBufferAddress(byteBuf); 117 118 jlong dstSize; 119 jbyteArray byteArray = NULL; 120 121 if (dst == NULL) { 122 jclass byteBufClass = env->FindClass("java/nio/ByteBuffer"); 123 CHECK(byteBufClass != NULL); 124 125 jmethodID arrayID = 126 env->GetMethodID(byteBufClass, "array", "()[B"); 127 CHECK(arrayID != NULL); 128 129 byteArray = 130 (jbyteArray)env->CallObjectMethod(byteBuf, arrayID); 131 132 if (byteArray == NULL) { 133 return INVALID_OPERATION; 134 } 135 136 jboolean isCopy; 137 dst = env->GetByteArrayElements(byteArray, &isCopy); 138 139 dstSize = env->GetArrayLength(byteArray); 140 } else { 141 dstSize = env->GetDirectBufferCapacity(byteBuf); 142 } 143 144 if (dstSize < offset) { 145 if (byteArray != NULL) { 146 env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0); 147 } 148 149 return -ERANGE; 150 } 151 152 sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset); 153 154 status_t err = mImpl->readSampleData(buffer); 155 156 if (byteArray != NULL) { 157 env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0); 158 } 159 160 if (err != OK) { 161 return err; 162 } 163 164 *sampleSize = buffer->size(); 165 166 return OK; 167 } 168 169 status_t JMediaExtractor::getSampleTrackIndex(size_t *trackIndex) { 170 return mImpl->getSampleTrackIndex(trackIndex); 171 } 172 173 status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { 174 return mImpl->getSampleTime(sampleTimeUs); 175 } 176 177 status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) { 178 *sampleFlags = 0; 179 180 sp<MetaData> meta; 181 status_t err = mImpl->getSampleMeta(&meta); 182 183 if (err != OK) { 184 return err; 185 } 186 187 int32_t val; 188 if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) { 189 (*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_SYNC; 190 } 191 192 uint32_t type; 193 const void *data; 194 size_t size; 195 if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) { 196 (*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED; 197 } 198 199 return OK; 200 } 201 202 status_t JMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) { 203 return mImpl->getSampleMeta(sampleMeta); 204 } 205 206 bool JMediaExtractor::getCachedDuration(int64_t *durationUs, bool *eos) const { 207 return mImpl->getCachedDuration(durationUs, eos); 208 } 209 210 } // namespace android 211 212 //////////////////////////////////////////////////////////////////////////////// 213 214 using namespace android; 215 216 static sp<JMediaExtractor> setMediaExtractor( 217 JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) { 218 sp<JMediaExtractor> old = 219 (JMediaExtractor *)env->GetIntField(thiz, gFields.context); 220 221 if (extractor != NULL) { 222 extractor->incStrong(thiz); 223 } 224 if (old != NULL) { 225 old->decStrong(thiz); 226 } 227 env->SetIntField(thiz, gFields.context, (int)extractor.get()); 228 229 return old; 230 } 231 232 static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) { 233 return (JMediaExtractor *)env->GetIntField(thiz, gFields.context); 234 } 235 236 static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) { 237 setMediaExtractor(env, thiz, NULL); 238 } 239 240 static jint android_media_MediaExtractor_getTrackCount( 241 JNIEnv *env, jobject thiz) { 242 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 243 244 if (extractor == NULL) { 245 jniThrowException(env, "java/lang/IllegalStateException", NULL); 246 return -1; 247 } 248 249 return extractor->countTracks(); 250 } 251 252 static jobject android_media_MediaExtractor_getTrackFormatNative( 253 JNIEnv *env, jobject thiz, jint index) { 254 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 255 256 if (extractor == NULL) { 257 jniThrowException(env, "java/lang/IllegalStateException", NULL); 258 return NULL; 259 } 260 261 jobject format; 262 status_t err = extractor->getTrackFormat(index, &format); 263 264 if (err != OK) { 265 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 266 return NULL; 267 } 268 269 return format; 270 } 271 272 static void android_media_MediaExtractor_selectTrack( 273 JNIEnv *env, jobject thiz, jint index) { 274 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 275 276 if (extractor == NULL) { 277 jniThrowException(env, "java/lang/IllegalStateException", NULL); 278 return; 279 } 280 281 status_t err = extractor->selectTrack(index); 282 283 if (err != OK) { 284 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 285 return; 286 } 287 } 288 289 static void android_media_MediaExtractor_unselectTrack( 290 JNIEnv *env, jobject thiz, jint index) { 291 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 292 293 if (extractor == NULL) { 294 jniThrowException(env, "java/lang/IllegalStateException", NULL); 295 return; 296 } 297 298 status_t err = extractor->unselectTrack(index); 299 300 if (err != OK) { 301 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 302 return; 303 } 304 } 305 306 static void android_media_MediaExtractor_seekTo( 307 JNIEnv *env, jobject thiz, jlong timeUs, jint mode) { 308 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 309 310 if (extractor == NULL) { 311 jniThrowException(env, "java/lang/IllegalStateException", NULL); 312 return; 313 } 314 315 if (mode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC 316 || mode >= MediaSource::ReadOptions::SEEK_CLOSEST) { 317 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 318 return; 319 } 320 321 extractor->seekTo(timeUs, (MediaSource::ReadOptions::SeekMode)mode); 322 } 323 324 static jboolean android_media_MediaExtractor_advance( 325 JNIEnv *env, jobject thiz) { 326 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 327 328 if (extractor == NULL) { 329 jniThrowException(env, "java/lang/IllegalStateException", NULL); 330 return false; 331 } 332 333 status_t err = extractor->advance(); 334 335 if (err == ERROR_END_OF_STREAM) { 336 return false; 337 } else if (err != OK) { 338 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 339 return false; 340 } 341 342 return true; 343 } 344 345 static jint android_media_MediaExtractor_readSampleData( 346 JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) { 347 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 348 349 if (extractor == NULL) { 350 jniThrowException(env, "java/lang/IllegalStateException", NULL); 351 return -1; 352 } 353 354 size_t sampleSize; 355 status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize); 356 357 if (err == ERROR_END_OF_STREAM) { 358 return -1; 359 } else if (err != OK) { 360 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 361 return false; 362 } 363 364 return sampleSize; 365 } 366 367 static jint android_media_MediaExtractor_getSampleTrackIndex( 368 JNIEnv *env, jobject thiz) { 369 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 370 371 if (extractor == NULL) { 372 jniThrowException(env, "java/lang/IllegalStateException", NULL); 373 return -1; 374 } 375 376 size_t trackIndex; 377 status_t err = extractor->getSampleTrackIndex(&trackIndex); 378 379 if (err == ERROR_END_OF_STREAM) { 380 return -1; 381 } else if (err != OK) { 382 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 383 return false; 384 } 385 386 return trackIndex; 387 } 388 389 static jlong android_media_MediaExtractor_getSampleTime( 390 JNIEnv *env, jobject thiz) { 391 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 392 393 if (extractor == NULL) { 394 jniThrowException(env, "java/lang/IllegalStateException", NULL); 395 return -1ll; 396 } 397 398 int64_t sampleTimeUs; 399 status_t err = extractor->getSampleTime(&sampleTimeUs); 400 401 if (err == ERROR_END_OF_STREAM) { 402 return -1ll; 403 } else if (err != OK) { 404 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 405 return false; 406 } 407 408 return sampleTimeUs; 409 } 410 411 static jint android_media_MediaExtractor_getSampleFlags( 412 JNIEnv *env, jobject thiz) { 413 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 414 415 if (extractor == NULL) { 416 jniThrowException(env, "java/lang/IllegalStateException", NULL); 417 return -1ll; 418 } 419 420 uint32_t sampleFlags; 421 status_t err = extractor->getSampleFlags(&sampleFlags); 422 423 if (err == ERROR_END_OF_STREAM) { 424 return -1ll; 425 } else if (err != OK) { 426 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 427 return false; 428 } 429 430 return sampleFlags; 431 } 432 433 static jboolean android_media_MediaExtractor_getSampleCryptoInfo( 434 JNIEnv *env, jobject thiz, jobject cryptoInfoObj) { 435 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 436 437 if (extractor == NULL) { 438 jniThrowException(env, "java/lang/IllegalStateException", NULL); 439 return -1ll; 440 } 441 442 sp<MetaData> meta; 443 status_t err = extractor->getSampleMeta(&meta); 444 445 if (err != OK) { 446 return false; 447 } 448 449 uint32_t type; 450 const void *data; 451 size_t size; 452 if (!meta->findData(kKeyEncryptedSizes, &type, &data, &size)) { 453 return false; 454 } 455 456 size_t numSubSamples = size / sizeof(size_t); 457 458 if (numSubSamples == 0) { 459 return false; 460 } 461 462 jintArray numBytesOfEncryptedDataObj = env->NewIntArray(numSubSamples); 463 jboolean isCopy; 464 jint *dst = env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy); 465 for (size_t i = 0; i < numSubSamples; ++i) { 466 dst[i] = ((const size_t *)data)[i]; 467 } 468 env->ReleaseIntArrayElements(numBytesOfEncryptedDataObj, dst, 0); 469 dst = NULL; 470 471 size_t encSize = size; 472 jintArray numBytesOfPlainDataObj = NULL; 473 if (meta->findData(kKeyPlainSizes, &type, &data, &size)) { 474 if (size != encSize) { 475 // The two must be of the same length. 476 return false; 477 } 478 479 numBytesOfPlainDataObj = env->NewIntArray(numSubSamples); 480 jboolean isCopy; 481 jint *dst = env->GetIntArrayElements(numBytesOfPlainDataObj, &isCopy); 482 for (size_t i = 0; i < numSubSamples; ++i) { 483 dst[i] = ((const size_t *)data)[i]; 484 } 485 env->ReleaseIntArrayElements(numBytesOfPlainDataObj, dst, 0); 486 dst = NULL; 487 } 488 489 jbyteArray keyObj = NULL; 490 if (meta->findData(kKeyCryptoKey, &type, &data, &size)) { 491 if (size != 16) { 492 // Keys must be 16 bytes in length. 493 return false; 494 } 495 496 keyObj = env->NewByteArray(size); 497 jboolean isCopy; 498 jbyte *dst = env->GetByteArrayElements(keyObj, &isCopy); 499 memcpy(dst, data, size); 500 env->ReleaseByteArrayElements(keyObj, dst, 0); 501 dst = NULL; 502 } 503 504 jbyteArray ivObj = NULL; 505 if (meta->findData(kKeyCryptoIV, &type, &data, &size)) { 506 if (size != 16) { 507 // IVs must be 16 bytes in length. 508 return false; 509 } 510 511 ivObj = env->NewByteArray(size); 512 jboolean isCopy; 513 jbyte *dst = env->GetByteArrayElements(ivObj, &isCopy); 514 memcpy(dst, data, size); 515 env->ReleaseByteArrayElements(ivObj, dst, 0); 516 dst = NULL; 517 } 518 519 int32_t mode; 520 if (!meta->findInt32(kKeyCryptoMode, &mode)) { 521 mode = CryptoPlugin::kMode_AES_CTR; 522 } 523 524 env->CallVoidMethod( 525 cryptoInfoObj, 526 gFields.cryptoInfoSetID, 527 numSubSamples, 528 numBytesOfPlainDataObj, 529 numBytesOfEncryptedDataObj, 530 keyObj, 531 ivObj, 532 mode); 533 534 return true; 535 } 536 537 static void android_media_MediaExtractor_native_init(JNIEnv *env) { 538 jclass clazz = env->FindClass("android/media/MediaExtractor"); 539 CHECK(clazz != NULL); 540 541 gFields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 542 CHECK(gFields.context != NULL); 543 544 clazz = env->FindClass("android/media/MediaCodec$CryptoInfo"); 545 CHECK(clazz != NULL); 546 547 gFields.cryptoInfoSetID = 548 env->GetMethodID(clazz, "set", "(I[I[I[B[BI)V"); 549 550 DataSource::RegisterDefaultSniffers(); 551 } 552 553 static void android_media_MediaExtractor_native_setup( 554 JNIEnv *env, jobject thiz) { 555 sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz); 556 setMediaExtractor(env,thiz, extractor); 557 } 558 559 static void android_media_MediaExtractor_setDataSource( 560 JNIEnv *env, jobject thiz, 561 jstring pathObj, jobjectArray keysArray, jobjectArray valuesArray) { 562 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 563 564 if (extractor == NULL) { 565 jniThrowException(env, "java/lang/IllegalStateException", NULL); 566 return; 567 } 568 569 if (pathObj == NULL) { 570 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 571 return; 572 } 573 574 KeyedVector<String8, String8> headers; 575 if (!ConvertKeyValueArraysToKeyedVector( 576 env, keysArray, valuesArray, &headers)) { 577 return; 578 } 579 580 const char *path = env->GetStringUTFChars(pathObj, NULL); 581 582 if (path == NULL) { 583 return; 584 } 585 586 status_t err = extractor->setDataSource(path, &headers); 587 588 env->ReleaseStringUTFChars(pathObj, path); 589 path = NULL; 590 591 if (err != OK) { 592 jniThrowException( 593 env, 594 "java/io/IOException", 595 "Failed to instantiate extractor."); 596 return; 597 } 598 } 599 600 static void android_media_MediaExtractor_setDataSourceFd( 601 JNIEnv *env, jobject thiz, 602 jobject fileDescObj, jlong offset, jlong length) { 603 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 604 605 if (extractor == NULL) { 606 jniThrowException(env, "java/lang/IllegalStateException", NULL); 607 return; 608 } 609 610 if (fileDescObj == NULL) { 611 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 612 return; 613 } 614 615 int fd = jniGetFDFromFileDescriptor(env, fileDescObj); 616 617 status_t err = extractor->setDataSource(fd, offset, length); 618 619 if (err != OK) { 620 jniThrowException( 621 env, 622 "java/io/IOException", 623 "Failed to instantiate extractor."); 624 return; 625 } 626 } 627 628 static jlong android_media_MediaExtractor_getCachedDurationUs( 629 JNIEnv *env, jobject thiz) { 630 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 631 632 if (extractor == NULL) { 633 jniThrowException(env, "java/lang/IllegalStateException", NULL); 634 return -1ll; 635 } 636 637 int64_t cachedDurationUs; 638 bool eos; 639 if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) { 640 return -1ll; 641 } 642 643 return cachedDurationUs; 644 } 645 646 static jboolean android_media_MediaExtractor_hasCacheReachedEOS( 647 JNIEnv *env, jobject thiz) { 648 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 649 650 if (extractor == NULL) { 651 jniThrowException(env, "java/lang/IllegalStateException", NULL); 652 return true; 653 } 654 655 int64_t cachedDurationUs; 656 bool eos; 657 if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) { 658 return true; 659 } 660 661 return eos; 662 } 663 664 static void android_media_MediaExtractor_native_finalize( 665 JNIEnv *env, jobject thiz) { 666 android_media_MediaExtractor_release(env, thiz); 667 } 668 669 static JNINativeMethod gMethods[] = { 670 { "release", "()V", (void *)android_media_MediaExtractor_release }, 671 672 { "getTrackCount", "()I", (void *)android_media_MediaExtractor_getTrackCount }, 673 674 { "getTrackFormatNative", "(I)Ljava/util/Map;", 675 (void *)android_media_MediaExtractor_getTrackFormatNative }, 676 677 { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack }, 678 679 { "unselectTrack", "(I)V", 680 (void *)android_media_MediaExtractor_unselectTrack }, 681 682 { "seekTo", "(JI)V", (void *)android_media_MediaExtractor_seekTo }, 683 684 { "advance", "()Z", (void *)android_media_MediaExtractor_advance }, 685 686 { "readSampleData", "(Ljava/nio/ByteBuffer;I)I", 687 (void *)android_media_MediaExtractor_readSampleData }, 688 689 { "getSampleTrackIndex", "()I", 690 (void *)android_media_MediaExtractor_getSampleTrackIndex }, 691 692 { "getSampleTime", "()J", 693 (void *)android_media_MediaExtractor_getSampleTime }, 694 695 { "getSampleFlags", "()I", 696 (void *)android_media_MediaExtractor_getSampleFlags }, 697 698 { "getSampleCryptoInfo", "(Landroid/media/MediaCodec$CryptoInfo;)Z", 699 (void *)android_media_MediaExtractor_getSampleCryptoInfo }, 700 701 { "native_init", "()V", (void *)android_media_MediaExtractor_native_init }, 702 703 { "native_setup", "()V", 704 (void *)android_media_MediaExtractor_native_setup }, 705 706 { "native_finalize", "()V", 707 (void *)android_media_MediaExtractor_native_finalize }, 708 709 { "setDataSource", "(Ljava/lang/String;[Ljava/lang/String;" 710 "[Ljava/lang/String;)V", 711 (void *)android_media_MediaExtractor_setDataSource }, 712 713 { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V", 714 (void *)android_media_MediaExtractor_setDataSourceFd }, 715 716 { "getCachedDuration", "()J", 717 (void *)android_media_MediaExtractor_getCachedDurationUs }, 718 719 { "hasCacheReachedEndOfStream", "()Z", 720 (void *)android_media_MediaExtractor_hasCacheReachedEOS }, 721 }; 722 723 int register_android_media_MediaExtractor(JNIEnv *env) { 724 return AndroidRuntime::registerNativeMethods(env, 725 "android/media/MediaExtractor", gMethods, NELEM(gMethods)); 726 } 727