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