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