android_media_MediaExtractor.cpp revision ab57d03bdb3010d4957fb88c8acb30575ca02ec9
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 201} // namespace android 202 203//////////////////////////////////////////////////////////////////////////////// 204 205using namespace android; 206 207static sp<JMediaExtractor> setMediaExtractor( 208 JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) { 209 sp<JMediaExtractor> old = 210 (JMediaExtractor *)env->GetIntField(thiz, gFields.context); 211 212 if (extractor != NULL) { 213 extractor->incStrong(thiz); 214 } 215 if (old != NULL) { 216 old->decStrong(thiz); 217 } 218 env->SetIntField(thiz, gFields.context, (int)extractor.get()); 219 220 return old; 221} 222 223static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) { 224 return (JMediaExtractor *)env->GetIntField(thiz, gFields.context); 225} 226 227static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) { 228 setMediaExtractor(env, thiz, NULL); 229} 230 231static jint android_media_MediaExtractor_countTracks( 232 JNIEnv *env, jobject thiz) { 233 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 234 235 if (extractor == NULL) { 236 jniThrowException(env, "java/lang/IllegalStateException", NULL); 237 return -1; 238 } 239 240 return extractor->countTracks(); 241} 242 243static jobject android_media_MediaExtractor_getTrackFormat( 244 JNIEnv *env, jobject thiz, jint index) { 245 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 246 247 if (extractor == NULL) { 248 jniThrowException(env, "java/lang/IllegalStateException", NULL); 249 return NULL; 250 } 251 252 jobject format; 253 status_t err = extractor->getTrackFormat(index, &format); 254 255 if (err != OK) { 256 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 257 return NULL; 258 } 259 260 return format; 261} 262 263static void android_media_MediaExtractor_selectTrack( 264 JNIEnv *env, jobject thiz, jint index) { 265 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 266 267 if (extractor == NULL) { 268 jniThrowException(env, "java/lang/IllegalStateException", NULL); 269 return; 270 } 271 272 status_t err = extractor->selectTrack(index); 273 274 if (err != OK) { 275 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 276 return; 277 } 278} 279 280static void android_media_MediaExtractor_seekTo( 281 JNIEnv *env, jobject thiz, jlong timeUs) { 282 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 283 284 if (extractor == NULL) { 285 jniThrowException(env, "java/lang/IllegalStateException", NULL); 286 return; 287 } 288 289 extractor->seekTo(timeUs); 290} 291 292static jboolean android_media_MediaExtractor_advance( 293 JNIEnv *env, jobject thiz) { 294 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 295 296 if (extractor == NULL) { 297 jniThrowException(env, "java/lang/IllegalStateException", NULL); 298 return false; 299 } 300 301 status_t err = extractor->advance(); 302 303 if (err == ERROR_END_OF_STREAM) { 304 return false; 305 } else if (err != OK) { 306 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 307 return false; 308 } 309 310 return true; 311} 312 313static jint android_media_MediaExtractor_readSampleData( 314 JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) { 315 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 316 317 if (extractor == NULL) { 318 jniThrowException(env, "java/lang/IllegalStateException", NULL); 319 return -1; 320 } 321 322 size_t sampleSize; 323 status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize); 324 325 if (err == ERROR_END_OF_STREAM) { 326 return -1; 327 } else if (err != OK) { 328 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 329 return false; 330 } 331 332 return sampleSize; 333} 334 335static jint android_media_MediaExtractor_getSampleTrackIndex( 336 JNIEnv *env, jobject thiz) { 337 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 338 339 if (extractor == NULL) { 340 jniThrowException(env, "java/lang/IllegalStateException", NULL); 341 return -1; 342 } 343 344 size_t trackIndex; 345 status_t err = extractor->getSampleTrackIndex(&trackIndex); 346 347 if (err == ERROR_END_OF_STREAM) { 348 return -1; 349 } else if (err != OK) { 350 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 351 return false; 352 } 353 354 return trackIndex; 355} 356 357static jlong android_media_MediaExtractor_getSampleTime( 358 JNIEnv *env, jobject thiz) { 359 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 360 361 if (extractor == NULL) { 362 jniThrowException(env, "java/lang/IllegalStateException", NULL); 363 return -1ll; 364 } 365 366 int64_t sampleTimeUs; 367 status_t err = extractor->getSampleTime(&sampleTimeUs); 368 369 if (err == ERROR_END_OF_STREAM) { 370 return -1ll; 371 } else if (err != OK) { 372 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 373 return false; 374 } 375 376 return sampleTimeUs; 377} 378 379static jint android_media_MediaExtractor_getSampleFlags( 380 JNIEnv *env, jobject thiz) { 381 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 382 383 if (extractor == NULL) { 384 jniThrowException(env, "java/lang/IllegalStateException", NULL); 385 return -1ll; 386 } 387 388 uint32_t sampleFlags; 389 status_t err = extractor->getSampleFlags(&sampleFlags); 390 391 if (err == ERROR_END_OF_STREAM) { 392 return -1ll; 393 } else if (err != OK) { 394 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 395 return false; 396 } 397 398 return sampleFlags; 399} 400 401static jboolean android_media_MediaExtractor_getSampleCryptoInfo( 402 JNIEnv *env, jobject thiz, jobject cryptoInfoObj) { 403 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 404 405 if (extractor == NULL) { 406 jniThrowException(env, "java/lang/IllegalStateException", NULL); 407 return -1ll; 408 } 409 410 sp<MetaData> meta; 411 status_t err = extractor->getSampleMeta(&meta); 412 413 if (err != OK) { 414 return false; 415 } 416 417 uint32_t type; 418 const void *data; 419 size_t size; 420 if (!meta->findData(kKeyEncryptedSizes, &type, &data, &size)) { 421 return false; 422 } 423 424 size_t numSubSamples = size / sizeof(size_t); 425 426 if (numSubSamples == 0) { 427 return false; 428 } 429 430 jintArray numBytesOfEncryptedDataObj = env->NewIntArray(numSubSamples); 431 jboolean isCopy; 432 jint *dst = env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy); 433 for (size_t i = 0; i < numSubSamples; ++i) { 434 dst[i] = ((const size_t *)data)[i]; 435 } 436 env->ReleaseIntArrayElements(numBytesOfEncryptedDataObj, dst, 0); 437 dst = NULL; 438 439 size_t encSize = size; 440 jintArray numBytesOfPlainDataObj = NULL; 441 if (meta->findData(kKeyPlainSizes, &type, &data, &size)) { 442 if (size != encSize) { 443 // The two must be of the same length. 444 return false; 445 } 446 447 numBytesOfPlainDataObj = env->NewIntArray(numSubSamples); 448 jboolean isCopy; 449 jint *dst = env->GetIntArrayElements(numBytesOfPlainDataObj, &isCopy); 450 for (size_t i = 0; i < numSubSamples; ++i) { 451 dst[i] = ((const size_t *)data)[i]; 452 } 453 env->ReleaseIntArrayElements(numBytesOfPlainDataObj, dst, 0); 454 dst = NULL; 455 } 456 457 jbyteArray keyObj = NULL; 458 if (meta->findData(kKeyCryptoKey, &type, &data, &size)) { 459 if (size != 16) { 460 // Keys must be 16 bytes in length. 461 return false; 462 } 463 464 keyObj = env->NewByteArray(size); 465 jboolean isCopy; 466 jbyte *dst = env->GetByteArrayElements(keyObj, &isCopy); 467 memcpy(dst, data, size); 468 env->ReleaseByteArrayElements(keyObj, dst, 0); 469 dst = NULL; 470 } 471 472 jbyteArray ivObj = NULL; 473 if (meta->findData(kKeyCryptoIV, &type, &data, &size)) { 474 if (size != 16) { 475 // IVs must be 16 bytes in length. 476 return false; 477 } 478 479 ivObj = env->NewByteArray(size); 480 jboolean isCopy; 481 jbyte *dst = env->GetByteArrayElements(ivObj, &isCopy); 482 memcpy(dst, data, size); 483 env->ReleaseByteArrayElements(ivObj, dst, 0); 484 dst = NULL; 485 } 486 487 int32_t mode; 488 if (!meta->findInt32(kKeyCryptoMode, &mode)) { 489 mode = CryptoPlugin::kMode_AES_CTR; 490 } 491 492 env->CallVoidMethod( 493 cryptoInfoObj, 494 gFields.cryptoInfoSetID, 495 numSubSamples, 496 numBytesOfPlainDataObj, 497 numBytesOfEncryptedDataObj, 498 keyObj, 499 ivObj, 500 mode); 501 502 return true; 503} 504 505static void android_media_MediaExtractor_native_init(JNIEnv *env) { 506 jclass clazz = env->FindClass("android/media/MediaExtractor"); 507 CHECK(clazz != NULL); 508 509 gFields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 510 CHECK(gFields.context != NULL); 511 512 clazz = env->FindClass("android/media/MediaCodec$CryptoInfo"); 513 CHECK(clazz != NULL); 514 515 gFields.cryptoInfoSetID = 516 env->GetMethodID(clazz, "set", "(I[I[I[B[BI)V"); 517 518 DataSource::RegisterDefaultSniffers(); 519} 520 521static void android_media_MediaExtractor_native_setup( 522 JNIEnv *env, jobject thiz) { 523 sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz); 524 setMediaExtractor(env,thiz, extractor); 525} 526 527static void android_media_MediaExtractor_setDataSource( 528 JNIEnv *env, jobject thiz, 529 jstring pathObj, jobjectArray keysArray, jobjectArray valuesArray) { 530 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 531 532 if (extractor == NULL) { 533 jniThrowException(env, "java/lang/IllegalStateException", NULL); 534 return; 535 } 536 537 if (pathObj == NULL) { 538 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 539 return; 540 } 541 542 KeyedVector<String8, String8> headers; 543 if (!ConvertKeyValueArraysToKeyedVector( 544 env, keysArray, valuesArray, &headers)) { 545 return; 546 } 547 548 const char *path = env->GetStringUTFChars(pathObj, NULL); 549 550 if (path == NULL) { 551 return; 552 } 553 554 status_t err = extractor->setDataSource(path, &headers); 555 556 env->ReleaseStringUTFChars(pathObj, path); 557 path = NULL; 558 559 if (err != OK) { 560 jniThrowException( 561 env, 562 "java/io/IOException", 563 "Failed to instantiate extractor."); 564 return; 565 } 566} 567 568static void android_media_MediaExtractor_setDataSourceFd( 569 JNIEnv *env, jobject thiz, 570 jobject fileDescObj, jlong offset, jlong length) { 571 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 572 573 if (extractor == NULL) { 574 jniThrowException(env, "java/lang/IllegalStateException", NULL); 575 return; 576 } 577 578 if (fileDescObj == NULL) { 579 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 580 return; 581 } 582 583 int fd = jniGetFDFromFileDescriptor(env, fileDescObj); 584 585 status_t err = extractor->setDataSource(fd, offset, length); 586 587 if (err != OK) { 588 jniThrowException( 589 env, 590 "java/io/IOException", 591 "Failed to instantiate extractor."); 592 return; 593 } 594} 595 596static void android_media_MediaExtractor_native_finalize( 597 JNIEnv *env, jobject thiz) { 598 android_media_MediaExtractor_release(env, thiz); 599} 600 601static JNINativeMethod gMethods[] = { 602 { "release", "()V", (void *)android_media_MediaExtractor_release }, 603 604 { "countTracks", "()I", (void *)android_media_MediaExtractor_countTracks }, 605 606 { "getTrackFormat", "(I)Ljava/util/Map;", 607 (void *)android_media_MediaExtractor_getTrackFormat }, 608 609 { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack }, 610 611 { "seekTo", "(J)V", (void *)android_media_MediaExtractor_seekTo }, 612 613 { "advance", "()Z", (void *)android_media_MediaExtractor_advance }, 614 615 { "readSampleData", "(Ljava/nio/ByteBuffer;I)I", 616 (void *)android_media_MediaExtractor_readSampleData }, 617 618 { "getSampleTrackIndex", "()I", 619 (void *)android_media_MediaExtractor_getSampleTrackIndex }, 620 621 { "getSampleTime", "()J", 622 (void *)android_media_MediaExtractor_getSampleTime }, 623 624 { "getSampleFlags", "()I", 625 (void *)android_media_MediaExtractor_getSampleFlags }, 626 627 { "getSampleCryptoInfo", "(Landroid/media/MediaCodec$CryptoInfo;)Z", 628 (void *)android_media_MediaExtractor_getSampleCryptoInfo }, 629 630 { "native_init", "()V", (void *)android_media_MediaExtractor_native_init }, 631 632 { "native_setup", "()V", 633 (void *)android_media_MediaExtractor_native_setup }, 634 635 { "native_finalize", "()V", 636 (void *)android_media_MediaExtractor_native_finalize }, 637 638 { "setDataSource", "(Ljava/lang/String;[Ljava/lang/String;" 639 "[Ljava/lang/String;)V", 640 (void *)android_media_MediaExtractor_setDataSource }, 641 642 { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V", 643 (void *)android_media_MediaExtractor_setDataSourceFd }, 644}; 645 646int register_android_media_MediaExtractor(JNIEnv *env) { 647 return AndroidRuntime::registerNativeMethods(env, 648 "android/media/MediaExtractor", gMethods, NELEM(gMethods)); 649} 650