android_media_MediaCodec.cpp revision fad6fa7a04e9227f621dfea9e90f6b867c40af86
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 "MediaCodec-JNI" 19#include <utils/Log.h> 20 21#include "android_media_MediaCodec.h" 22 23#include "android_media_MediaCrypto.h" 24#include "android_media_Utils.h" 25#include "android_runtime/AndroidRuntime.h" 26#include "android_runtime/android_view_Surface.h" 27#include "jni.h" 28#include "JNIHelp.h" 29 30#include <cutils/compiler.h> 31 32#include <gui/Surface.h> 33 34#include <media/ICrypto.h> 35#include <media/stagefright/MediaCodec.h> 36#include <media/stagefright/foundation/ABuffer.h> 37#include <media/stagefright/foundation/ADebug.h> 38#include <media/stagefright/foundation/ALooper.h> 39#include <media/stagefright/foundation/AMessage.h> 40#include <media/stagefright/foundation/AString.h> 41#include <media/stagefright/MediaErrors.h> 42 43#include <nativehelper/ScopedLocalRef.h> 44 45#include <system/window.h> 46 47namespace android { 48 49// Keep these in sync with their equivalents in MediaCodec.java !!! 50enum { 51 DEQUEUE_INFO_TRY_AGAIN_LATER = -1, 52 DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED = -2, 53 DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3, 54}; 55 56enum { 57 EVENT_NOTIFY = 1, 58}; 59 60struct CryptoErrorCodes { 61 jint cryptoErrorNoKey; 62 jint cryptoErrorKeyExpired; 63 jint cryptoErrorResourceBusy; 64} gCryptoErrorCodes; 65 66struct fields_t { 67 jfieldID context; 68 jmethodID postEventFromNativeID; 69 jfieldID cryptoInfoNumSubSamplesID; 70 jfieldID cryptoInfoNumBytesOfClearDataID; 71 jfieldID cryptoInfoNumBytesOfEncryptedDataID; 72 jfieldID cryptoInfoKeyID; 73 jfieldID cryptoInfoIVID; 74 jfieldID cryptoInfoModeID; 75}; 76 77static fields_t gFields; 78 79//////////////////////////////////////////////////////////////////////////////// 80 81JMediaCodec::JMediaCodec( 82 JNIEnv *env, jobject thiz, 83 const char *name, bool nameIsType, bool encoder) 84 : mClass(NULL), 85 mObject(NULL), 86 mGeneration(1), 87 mRequestedActivityNotification(false) { 88 jclass clazz = env->GetObjectClass(thiz); 89 CHECK(clazz != NULL); 90 91 mClass = (jclass)env->NewGlobalRef(clazz); 92 mObject = env->NewWeakGlobalRef(thiz); 93 94 mLooper = new ALooper; 95 mLooper->setName("MediaCodec_looper"); 96 97 mLooper->start( 98 false, // runOnCallingThread 99 true, // canCallJava 100 PRIORITY_FOREGROUND); 101 102 if (nameIsType) { 103 mCodec = MediaCodec::CreateByType(mLooper, name, encoder); 104 } else { 105 mCodec = MediaCodec::CreateByComponentName(mLooper, name); 106 } 107} 108 109status_t JMediaCodec::initCheck() const { 110 return mCodec != NULL ? OK : NO_INIT; 111} 112 113void JMediaCodec::registerSelf() { 114 mLooper->registerHandler(this); 115} 116 117void JMediaCodec::release() { 118 if (mCodec != NULL) { 119 mCodec->release(); 120 mCodec.clear(); 121 } 122 123 if (mLooper != NULL) { 124 mLooper->unregisterHandler(id()); 125 mLooper->stop(); 126 mLooper.clear(); 127 } 128} 129 130JMediaCodec::~JMediaCodec() { 131 if (mCodec != NULL || mLooper != NULL) { 132 /* MediaCodec and looper should have been released explicitly already 133 * in setMediaCodec() (see comments in setMediaCodec()). 134 * 135 * Otherwise JMediaCodec::~JMediaCodec() might be called from within the 136 * message handler, doing release() there risks deadlock as MediaCodec:: 137 * release() post synchronous message to the same looper. 138 * 139 * Print a warning and try to proceed with releasing. 140 */ 141 ALOGW("try to release MediaCodec from JMediaCodec::~JMediaCodec()..."); 142 release(); 143 ALOGW("done releasing MediaCodec from JMediaCodec::~JMediaCodec()."); 144 } 145 146 JNIEnv *env = AndroidRuntime::getJNIEnv(); 147 148 env->DeleteWeakGlobalRef(mObject); 149 mObject = NULL; 150 env->DeleteGlobalRef(mClass); 151 mClass = NULL; 152} 153 154status_t JMediaCodec::configure( 155 const sp<AMessage> &format, 156 const sp<IGraphicBufferProducer> &bufferProducer, 157 const sp<ICrypto> &crypto, 158 int flags) { 159 sp<Surface> client; 160 if (bufferProducer != NULL) { 161 mSurfaceTextureClient = 162 new Surface(bufferProducer, true /* controlledByApp */); 163 } else { 164 mSurfaceTextureClient.clear(); 165 } 166 167 return mCodec->configure(format, mSurfaceTextureClient, crypto, flags); 168} 169 170status_t JMediaCodec::createInputSurface( 171 sp<IGraphicBufferProducer>* bufferProducer) { 172 return mCodec->createInputSurface(bufferProducer); 173} 174 175status_t JMediaCodec::start() { 176 status_t err = mCodec->start(); 177 178 if (err != OK) { 179 return err; 180 } 181 182 mActivityNotification = new AMessage(kWhatActivityNotify, id()); 183 mActivityNotification->setInt32("generation", mGeneration); 184 185 requestActivityNotification(); 186 187 return err; 188} 189 190status_t JMediaCodec::stop() { 191 mSurfaceTextureClient.clear(); 192 193 status_t err = mCodec->stop(); 194 195 sp<AMessage> msg = new AMessage(kWhatStopActivityNotifications, id()); 196 sp<AMessage> response; 197 msg->postAndAwaitResponse(&response); 198 199 mActivityNotification.clear(); 200 201 return err; 202} 203 204status_t JMediaCodec::flush() { 205 return mCodec->flush(); 206} 207 208status_t JMediaCodec::queueInputBuffer( 209 size_t index, 210 size_t offset, size_t size, int64_t timeUs, uint32_t flags, 211 AString *errorDetailMsg) { 212 return mCodec->queueInputBuffer( 213 index, offset, size, timeUs, flags, errorDetailMsg); 214} 215 216status_t JMediaCodec::queueSecureInputBuffer( 217 size_t index, 218 size_t offset, 219 const CryptoPlugin::SubSample *subSamples, 220 size_t numSubSamples, 221 const uint8_t key[16], 222 const uint8_t iv[16], 223 CryptoPlugin::Mode mode, 224 int64_t presentationTimeUs, 225 uint32_t flags, 226 AString *errorDetailMsg) { 227 return mCodec->queueSecureInputBuffer( 228 index, offset, subSamples, numSubSamples, key, iv, mode, 229 presentationTimeUs, flags, errorDetailMsg); 230} 231 232status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { 233 status_t err = mCodec->dequeueInputBuffer(index, timeoutUs); 234 235 requestActivityNotification(); 236 237 return err; 238} 239 240status_t JMediaCodec::dequeueOutputBuffer( 241 JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) { 242 size_t size, offset; 243 int64_t timeUs; 244 uint32_t flags; 245 status_t err = mCodec->dequeueOutputBuffer( 246 index, &offset, &size, &timeUs, &flags, timeoutUs); 247 248 requestActivityNotification(); 249 250 if (err != OK) { 251 return err; 252 } 253 254 ScopedLocalRef<jclass> clazz( 255 env, env->FindClass("android/media/MediaCodec$BufferInfo")); 256 257 jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V"); 258 env->CallVoidMethod(bufferInfo, method, (jint)offset, (jint)size, timeUs, flags); 259 260 return OK; 261} 262 263status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) { 264 return render 265 ? mCodec->renderOutputBufferAndRelease(index) 266 : mCodec->releaseOutputBuffer(index); 267} 268 269status_t JMediaCodec::signalEndOfInputStream() { 270 return mCodec->signalEndOfInputStream(); 271} 272 273status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const { 274 sp<AMessage> msg; 275 status_t err; 276 if ((err = mCodec->getOutputFormat(&msg)) != OK) { 277 return err; 278 } 279 280 return ConvertMessageToMap(env, msg, format); 281} 282 283status_t JMediaCodec::getBuffers( 284 JNIEnv *env, bool input, jobjectArray *bufArray) const { 285 Vector<sp<ABuffer> > buffers; 286 287 status_t err = 288 input 289 ? mCodec->getInputBuffers(&buffers) 290 : mCodec->getOutputBuffers(&buffers); 291 292 if (err != OK) { 293 return err; 294 } 295 296 ScopedLocalRef<jclass> byteBufferClass( 297 env, env->FindClass("java/nio/ByteBuffer")); 298 299 CHECK(byteBufferClass.get() != NULL); 300 301 jmethodID orderID = env->GetMethodID( 302 byteBufferClass.get(), 303 "order", 304 "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"); 305 306 CHECK(orderID != NULL); 307 308 ScopedLocalRef<jclass> byteOrderClass( 309 env, env->FindClass("java/nio/ByteOrder")); 310 311 CHECK(byteOrderClass.get() != NULL); 312 313 jmethodID nativeOrderID = env->GetStaticMethodID( 314 byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;"); 315 CHECK(nativeOrderID != NULL); 316 317 jobject nativeByteOrderObj = 318 env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID); 319 CHECK(nativeByteOrderObj != NULL); 320 321 *bufArray = (jobjectArray)env->NewObjectArray( 322 buffers.size(), byteBufferClass.get(), NULL); 323 if (*bufArray == NULL) { 324 env->DeleteLocalRef(nativeByteOrderObj); 325 return NO_MEMORY; 326 } 327 328 for (size_t i = 0; i < buffers.size(); ++i) { 329 const sp<ABuffer> &buffer = buffers.itemAt(i); 330 331 // if this is an ABuffer that doesn't actually hold any accessible memory, 332 // use a null ByteBuffer 333 if (buffer->base() == NULL) { 334 continue; 335 } 336 jobject byteBuffer = 337 env->NewDirectByteBuffer( 338 buffer->base(), 339 buffer->capacity()); 340 if (byteBuffer == NULL) { 341 env->DeleteLocalRef(nativeByteOrderObj); 342 return NO_MEMORY; 343 } 344 jobject me = env->CallObjectMethod( 345 byteBuffer, orderID, nativeByteOrderObj); 346 env->DeleteLocalRef(me); 347 me = NULL; 348 349 env->SetObjectArrayElement( 350 *bufArray, i, byteBuffer); 351 352 env->DeleteLocalRef(byteBuffer); 353 byteBuffer = NULL; 354 } 355 356 env->DeleteLocalRef(nativeByteOrderObj); 357 nativeByteOrderObj = NULL; 358 359 return OK; 360} 361 362status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const { 363 AString name; 364 365 status_t err = mCodec->getName(&name); 366 367 if (err != OK) { 368 return err; 369 } 370 371 *nameStr = env->NewStringUTF(name.c_str()); 372 373 return OK; 374} 375 376status_t JMediaCodec::setParameters(const sp<AMessage> &msg) { 377 return mCodec->setParameters(msg); 378} 379 380void JMediaCodec::setVideoScalingMode(int mode) { 381 if (mSurfaceTextureClient != NULL) { 382 native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode); 383 } 384} 385 386void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) { 387 switch (msg->what()) { 388 case kWhatRequestActivityNotifications: 389 { 390 if (mRequestedActivityNotification) { 391 break; 392 } 393 394 mCodec->requestActivityNotification(mActivityNotification); 395 mRequestedActivityNotification = true; 396 break; 397 } 398 399 case kWhatActivityNotify: 400 { 401 { 402 int32_t generation; 403 CHECK(msg->findInt32("generation", &generation)); 404 405 if (generation != mGeneration) { 406 // stale 407 break; 408 } 409 410 mRequestedActivityNotification = false; 411 } 412 413 JNIEnv *env = AndroidRuntime::getJNIEnv(); 414 env->CallVoidMethod( 415 mObject, 416 gFields.postEventFromNativeID, 417 EVENT_NOTIFY, 418 0 /* arg1 */, 419 0 /* arg2 */, 420 NULL /* obj */); 421 422 break; 423 } 424 425 case kWhatStopActivityNotifications: 426 { 427 uint32_t replyID; 428 CHECK(msg->senderAwaitsResponse(&replyID)); 429 430 ++mGeneration; 431 mRequestedActivityNotification = false; 432 433 sp<AMessage> response = new AMessage; 434 response->postReply(replyID); 435 break; 436 } 437 438 default: 439 TRESPASS(); 440 } 441} 442 443void JMediaCodec::requestActivityNotification() { 444 (new AMessage(kWhatRequestActivityNotifications, id()))->post(); 445} 446 447} // namespace android 448 449//////////////////////////////////////////////////////////////////////////////// 450 451using namespace android; 452 453static sp<JMediaCodec> setMediaCodec( 454 JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) { 455 sp<JMediaCodec> old = (JMediaCodec *)env->GetLongField(thiz, gFields.context); 456 if (codec != NULL) { 457 codec->incStrong(thiz); 458 } 459 if (old != NULL) { 460 /* release MediaCodec and stop the looper now before decStrong. 461 * otherwise JMediaCodec::~JMediaCodec() could be called from within 462 * its message handler, doing release() from there will deadlock 463 * (as MediaCodec::release() post synchronous message to the same looper) 464 */ 465 old->release(); 466 old->decStrong(thiz); 467 } 468 env->SetLongField(thiz, gFields.context, (jlong)codec.get()); 469 470 return old; 471} 472 473static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) { 474 return (JMediaCodec *)env->GetLongField(thiz, gFields.context); 475} 476 477static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) { 478 setMediaCodec(env, thiz, NULL); 479} 480 481static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { 482 ScopedLocalRef<jclass> clazz( 483 env, env->FindClass("android/media/MediaCodec$CryptoException")); 484 CHECK(clazz.get() != NULL); 485 486 jmethodID constructID = 487 env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V"); 488 CHECK(constructID != NULL); 489 490 jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error"); 491 492 /* translate OS errors to Java API CryptoException errorCodes */ 493 switch (err) { 494 case ERROR_DRM_NO_LICENSE: 495 err = gCryptoErrorCodes.cryptoErrorNoKey; 496 break; 497 case ERROR_DRM_LICENSE_EXPIRED: 498 err = gCryptoErrorCodes.cryptoErrorKeyExpired; 499 break; 500 case ERROR_DRM_RESOURCE_BUSY: 501 err = gCryptoErrorCodes.cryptoErrorResourceBusy; 502 break; 503 default: 504 break; 505 } 506 507 jthrowable exception = 508 (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj); 509 510 env->Throw(exception); 511} 512 513static jint throwExceptionAsNecessary( 514 JNIEnv *env, status_t err, const char *msg = NULL) { 515 if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) { 516 // We'll throw our custom MediaCodec.CryptoException 517 throwCryptoException(env, err, msg); 518 return 0; 519 } 520 521 switch (err) { 522 case OK: 523 return 0; 524 525 case -EAGAIN: 526 return DEQUEUE_INFO_TRY_AGAIN_LATER; 527 528 case INFO_FORMAT_CHANGED: 529 return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED; 530 531 case INFO_OUTPUT_BUFFERS_CHANGED: 532 return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED; 533 534 case ERROR_DRM_NO_LICENSE: 535 case ERROR_DRM_LICENSE_EXPIRED: 536 case ERROR_DRM_RESOURCE_BUSY: 537 throwCryptoException(env, err, msg); 538 break; 539 540 default: 541 { 542 jniThrowException(env, "java/lang/IllegalStateException", msg); 543 break; 544 } 545 } 546 547 return 0; 548} 549 550static void android_media_MediaCodec_native_configure( 551 JNIEnv *env, 552 jobject thiz, 553 jobjectArray keys, jobjectArray values, 554 jobject jsurface, 555 jobject jcrypto, 556 jint flags) { 557 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 558 559 if (codec == NULL) { 560 jniThrowException(env, "java/lang/IllegalStateException", NULL); 561 return; 562 } 563 564 sp<AMessage> format; 565 status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format); 566 567 if (err != OK) { 568 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 569 return; 570 } 571 572 sp<IGraphicBufferProducer> bufferProducer; 573 if (jsurface != NULL) { 574 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface)); 575 if (surface != NULL) { 576 bufferProducer = surface->getIGraphicBufferProducer(); 577 } else { 578 jniThrowException( 579 env, 580 "java/lang/IllegalArgumentException", 581 "The surface has been released"); 582 return; 583 } 584 } 585 586 sp<ICrypto> crypto; 587 if (jcrypto != NULL) { 588 crypto = JCrypto::GetCrypto(env, jcrypto); 589 } 590 591 err = codec->configure(format, bufferProducer, crypto, flags); 592 593 throwExceptionAsNecessary(env, err); 594} 595 596static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env, 597 jobject thiz) { 598 ALOGV("android_media_MediaCodec_createInputSurface"); 599 600 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 601 if (codec == NULL) { 602 jniThrowException(env, "java/lang/IllegalStateException", NULL); 603 return NULL; 604 } 605 606 // Tell the MediaCodec that we want to use a Surface as input. 607 sp<IGraphicBufferProducer> bufferProducer; 608 status_t err = codec->createInputSurface(&bufferProducer); 609 if (err != NO_ERROR) { 610 throwExceptionAsNecessary(env, err); 611 return NULL; 612 } 613 614 // Wrap the IGBP in a Java-language Surface. 615 return android_view_Surface_createFromIGraphicBufferProducer(env, 616 bufferProducer); 617} 618 619static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) { 620 ALOGV("android_media_MediaCodec_start"); 621 622 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 623 624 if (codec == NULL) { 625 jniThrowException(env, "java/lang/IllegalStateException", "no codec found"); 626 return; 627 } 628 629 status_t err = codec->start(); 630 631 throwExceptionAsNecessary(env, err, "start failed"); 632} 633 634static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) { 635 ALOGV("android_media_MediaCodec_stop"); 636 637 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 638 639 if (codec == NULL) { 640 jniThrowException(env, "java/lang/IllegalStateException", NULL); 641 return; 642 } 643 644 status_t err = codec->stop(); 645 646 throwExceptionAsNecessary(env, err); 647} 648 649static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) { 650 ALOGV("android_media_MediaCodec_flush"); 651 652 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 653 654 if (codec == NULL) { 655 jniThrowException(env, "java/lang/IllegalStateException", NULL); 656 return; 657 } 658 659 status_t err = codec->flush(); 660 661 throwExceptionAsNecessary(env, err); 662} 663 664static void android_media_MediaCodec_queueInputBuffer( 665 JNIEnv *env, 666 jobject thiz, 667 jint index, 668 jint offset, 669 jint size, 670 jlong timestampUs, 671 jint flags) { 672 ALOGV("android_media_MediaCodec_queueInputBuffer"); 673 674 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 675 676 if (codec == NULL) { 677 jniThrowException(env, "java/lang/IllegalStateException", NULL); 678 return; 679 } 680 681 AString errorDetailMsg; 682 683 status_t err = codec->queueInputBuffer( 684 index, offset, size, timestampUs, flags, &errorDetailMsg); 685 686 throwExceptionAsNecessary( 687 env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); 688} 689 690static void android_media_MediaCodec_queueSecureInputBuffer( 691 JNIEnv *env, 692 jobject thiz, 693 jint index, 694 jint offset, 695 jobject cryptoInfoObj, 696 jlong timestampUs, 697 jint flags) { 698 ALOGV("android_media_MediaCodec_queueSecureInputBuffer"); 699 700 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 701 702 if (codec == NULL) { 703 jniThrowException(env, "java/lang/IllegalStateException", NULL); 704 return; 705 } 706 707 jint numSubSamples = 708 env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID); 709 710 jintArray numBytesOfClearDataObj = 711 (jintArray)env->GetObjectField( 712 cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID); 713 714 jintArray numBytesOfEncryptedDataObj = 715 (jintArray)env->GetObjectField( 716 cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID); 717 718 jbyteArray keyObj = 719 (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID); 720 721 jbyteArray ivObj = 722 (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID); 723 724 jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID); 725 726 status_t err = OK; 727 728 CryptoPlugin::SubSample *subSamples = NULL; 729 jbyte *key = NULL; 730 jbyte *iv = NULL; 731 732 if (numSubSamples <= 0) { 733 err = -EINVAL; 734 } else if (numBytesOfClearDataObj == NULL 735 && numBytesOfEncryptedDataObj == NULL) { 736 err = -EINVAL; 737 } else if (numBytesOfEncryptedDataObj != NULL 738 && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) { 739 err = -ERANGE; 740 } else if (numBytesOfClearDataObj != NULL 741 && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) { 742 err = -ERANGE; 743 // subSamples array may silently overflow if number of samples are too large. Use 744 // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms 745 } else if ( CC_UNLIKELY(numSubSamples >= INT32_MAX / sizeof(*subSamples)) ) { 746 err = -EINVAL; 747 } else { 748 jboolean isCopy; 749 750 jint *numBytesOfClearData = 751 (numBytesOfClearDataObj == NULL) 752 ? NULL 753 : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy); 754 755 jint *numBytesOfEncryptedData = 756 (numBytesOfEncryptedDataObj == NULL) 757 ? NULL 758 : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy); 759 760 subSamples = new CryptoPlugin::SubSample[numSubSamples]; 761 762 for (jint i = 0; i < numSubSamples; ++i) { 763 subSamples[i].mNumBytesOfClearData = 764 (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i]; 765 766 subSamples[i].mNumBytesOfEncryptedData = 767 (numBytesOfEncryptedData == NULL) 768 ? 0 : numBytesOfEncryptedData[i]; 769 } 770 771 if (numBytesOfEncryptedData != NULL) { 772 env->ReleaseIntArrayElements( 773 numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0); 774 numBytesOfEncryptedData = NULL; 775 } 776 777 if (numBytesOfClearData != NULL) { 778 env->ReleaseIntArrayElements( 779 numBytesOfClearDataObj, numBytesOfClearData, 0); 780 numBytesOfClearData = NULL; 781 } 782 } 783 784 if (err == OK && keyObj != NULL) { 785 if (env->GetArrayLength(keyObj) != 16) { 786 err = -EINVAL; 787 } else { 788 jboolean isCopy; 789 key = env->GetByteArrayElements(keyObj, &isCopy); 790 } 791 } 792 793 if (err == OK && ivObj != NULL) { 794 if (env->GetArrayLength(ivObj) != 16) { 795 err = -EINVAL; 796 } else { 797 jboolean isCopy; 798 iv = env->GetByteArrayElements(ivObj, &isCopy); 799 } 800 } 801 802 AString errorDetailMsg; 803 804 if (err == OK) { 805 err = codec->queueSecureInputBuffer( 806 index, offset, 807 subSamples, numSubSamples, 808 (const uint8_t *)key, (const uint8_t *)iv, 809 (CryptoPlugin::Mode)mode, 810 timestampUs, 811 flags, 812 &errorDetailMsg); 813 } 814 815 if (iv != NULL) { 816 env->ReleaseByteArrayElements(ivObj, iv, 0); 817 iv = NULL; 818 } 819 820 if (key != NULL) { 821 env->ReleaseByteArrayElements(keyObj, key, 0); 822 key = NULL; 823 } 824 825 delete[] subSamples; 826 subSamples = NULL; 827 828 throwExceptionAsNecessary( 829 env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); 830} 831 832static jint android_media_MediaCodec_dequeueInputBuffer( 833 JNIEnv *env, jobject thiz, jlong timeoutUs) { 834 ALOGV("android_media_MediaCodec_dequeueInputBuffer"); 835 836 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 837 838 if (codec == NULL) { 839 jniThrowException(env, "java/lang/IllegalStateException", NULL); 840 return -1; 841 } 842 843 size_t index; 844 status_t err = codec->dequeueInputBuffer(&index, timeoutUs); 845 846 if (err == OK) { 847 return (jint) index; 848 } 849 850 return throwExceptionAsNecessary(env, err); 851} 852 853static jint android_media_MediaCodec_dequeueOutputBuffer( 854 JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) { 855 ALOGV("android_media_MediaCodec_dequeueOutputBuffer"); 856 857 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 858 859 if (codec == NULL) { 860 jniThrowException(env, "java/lang/IllegalStateException", NULL); 861 return 0; 862 } 863 864 size_t index; 865 status_t err = codec->dequeueOutputBuffer( 866 env, bufferInfo, &index, timeoutUs); 867 868 if (err == OK) { 869 return (jint) index; 870 } 871 872 return throwExceptionAsNecessary(env, err); 873} 874 875static void android_media_MediaCodec_releaseOutputBuffer( 876 JNIEnv *env, jobject thiz, jint index, jboolean render) { 877 ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease"); 878 879 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 880 881 if (codec == NULL) { 882 jniThrowException(env, "java/lang/IllegalStateException", NULL); 883 return; 884 } 885 886 status_t err = codec->releaseOutputBuffer(index, render); 887 888 throwExceptionAsNecessary(env, err); 889} 890 891static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env, 892 jobject thiz) { 893 ALOGV("android_media_MediaCodec_signalEndOfInputStream"); 894 895 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 896 if (codec == NULL) { 897 jniThrowException(env, "java/lang/IllegalStateException", NULL); 898 return; 899 } 900 901 status_t err = codec->signalEndOfInputStream(); 902 903 throwExceptionAsNecessary(env, err); 904} 905 906static jobject android_media_MediaCodec_getOutputFormatNative( 907 JNIEnv *env, jobject thiz) { 908 ALOGV("android_media_MediaCodec_getOutputFormatNative"); 909 910 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 911 912 if (codec == NULL) { 913 jniThrowException(env, "java/lang/IllegalStateException", NULL); 914 return NULL; 915 } 916 917 jobject format; 918 status_t err = codec->getOutputFormat(env, &format); 919 920 if (err == OK) { 921 return format; 922 } 923 924 throwExceptionAsNecessary(env, err); 925 926 return NULL; 927} 928 929static jobjectArray android_media_MediaCodec_getBuffers( 930 JNIEnv *env, jobject thiz, jboolean input) { 931 ALOGV("android_media_MediaCodec_getBuffers"); 932 933 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 934 935 if (codec == NULL) { 936 jniThrowException(env, "java/lang/IllegalStateException", NULL); 937 return NULL; 938 } 939 940 jobjectArray buffers; 941 status_t err = codec->getBuffers(env, input, &buffers); 942 943 if (err == OK) { 944 return buffers; 945 } 946 947 // if we're out of memory, an exception was already thrown 948 if (err != NO_MEMORY) { 949 throwExceptionAsNecessary(env, err); 950 } 951 952 return NULL; 953} 954 955static jobject android_media_MediaCodec_getName( 956 JNIEnv *env, jobject thiz) { 957 ALOGV("android_media_MediaCodec_getName"); 958 959 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 960 961 if (codec == NULL) { 962 jniThrowException(env, "java/lang/IllegalStateException", NULL); 963 return NULL; 964 } 965 966 jstring name; 967 status_t err = codec->getName(env, &name); 968 969 if (err == OK) { 970 return name; 971 } 972 973 throwExceptionAsNecessary(env, err); 974 975 return NULL; 976} 977 978static void android_media_MediaCodec_setParameters( 979 JNIEnv *env, jobject thiz, jobjectArray keys, jobjectArray vals) { 980 ALOGV("android_media_MediaCodec_setParameters"); 981 982 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 983 984 if (codec == NULL) { 985 jniThrowException(env, "java/lang/IllegalStateException", NULL); 986 return; 987 } 988 989 sp<AMessage> params; 990 status_t err = ConvertKeyValueArraysToMessage(env, keys, vals, ¶ms); 991 992 if (err == OK) { 993 err = codec->setParameters(params); 994 } 995 996 throwExceptionAsNecessary(env, err); 997} 998 999static void android_media_MediaCodec_setVideoScalingMode( 1000 JNIEnv *env, jobject thiz, jint mode) { 1001 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1002 1003 if (codec == NULL) { 1004 jniThrowException(env, "java/lang/IllegalStateException", NULL); 1005 return; 1006 } 1007 1008 if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW 1009 && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) { 1010 jniThrowException(env, "java/lang/InvalidArgumentException", NULL); 1011 return; 1012 } 1013 1014 codec->setVideoScalingMode(mode); 1015} 1016 1017static void android_media_MediaCodec_native_init(JNIEnv *env) { 1018 ScopedLocalRef<jclass> clazz( 1019 env, env->FindClass("android/media/MediaCodec")); 1020 CHECK(clazz.get() != NULL); 1021 1022 gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J"); 1023 CHECK(gFields.context != NULL); 1024 1025 gFields.postEventFromNativeID = 1026 env->GetMethodID( 1027 clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V"); 1028 1029 CHECK(gFields.postEventFromNativeID != NULL); 1030 1031 clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo")); 1032 CHECK(clazz.get() != NULL); 1033 1034 gFields.cryptoInfoNumSubSamplesID = 1035 env->GetFieldID(clazz.get(), "numSubSamples", "I"); 1036 CHECK(gFields.cryptoInfoNumSubSamplesID != NULL); 1037 1038 gFields.cryptoInfoNumBytesOfClearDataID = 1039 env->GetFieldID(clazz.get(), "numBytesOfClearData", "[I"); 1040 CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL); 1041 1042 gFields.cryptoInfoNumBytesOfEncryptedDataID = 1043 env->GetFieldID(clazz.get(), "numBytesOfEncryptedData", "[I"); 1044 CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL); 1045 1046 gFields.cryptoInfoKeyID = env->GetFieldID(clazz.get(), "key", "[B"); 1047 CHECK(gFields.cryptoInfoKeyID != NULL); 1048 1049 gFields.cryptoInfoIVID = env->GetFieldID(clazz.get(), "iv", "[B"); 1050 CHECK(gFields.cryptoInfoIVID != NULL); 1051 1052 gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I"); 1053 CHECK(gFields.cryptoInfoModeID != NULL); 1054 1055 clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException")); 1056 CHECK(clazz.get() != NULL); 1057 1058 jfieldID field; 1059 field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I"); 1060 CHECK(field != NULL); 1061 gCryptoErrorCodes.cryptoErrorNoKey = 1062 env->GetStaticIntField(clazz.get(), field); 1063 1064 field = env->GetStaticFieldID(clazz.get(), "ERROR_KEY_EXPIRED", "I"); 1065 CHECK(field != NULL); 1066 gCryptoErrorCodes.cryptoErrorKeyExpired = 1067 env->GetStaticIntField(clazz.get(), field); 1068 1069 field = env->GetStaticFieldID(clazz.get(), "ERROR_RESOURCE_BUSY", "I"); 1070 CHECK(field != NULL); 1071 gCryptoErrorCodes.cryptoErrorResourceBusy = 1072 env->GetStaticIntField(clazz.get(), field); 1073} 1074 1075static void android_media_MediaCodec_native_setup( 1076 JNIEnv *env, jobject thiz, 1077 jstring name, jboolean nameIsType, jboolean encoder) { 1078 if (name == NULL) { 1079 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 1080 return; 1081 } 1082 1083 const char *tmp = env->GetStringUTFChars(name, NULL); 1084 1085 if (tmp == NULL) { 1086 return; 1087 } 1088 1089 sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder); 1090 1091 status_t err = codec->initCheck(); 1092 1093 env->ReleaseStringUTFChars(name, tmp); 1094 tmp = NULL; 1095 1096 if (err != OK) { 1097 jniThrowException( 1098 env, 1099 "java/io/IOException", 1100 "Failed to allocate component instance"); 1101 return; 1102 } 1103 1104 codec->registerSelf(); 1105 1106 setMediaCodec(env,thiz, codec); 1107} 1108 1109static void android_media_MediaCodec_native_finalize( 1110 JNIEnv *env, jobject thiz) { 1111 android_media_MediaCodec_release(env, thiz); 1112} 1113 1114static JNINativeMethod gMethods[] = { 1115 { "release", "()V", (void *)android_media_MediaCodec_release }, 1116 1117 { "native_configure", 1118 "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;" 1119 "Landroid/media/MediaCrypto;I)V", 1120 (void *)android_media_MediaCodec_native_configure }, 1121 1122 { "createInputSurface", "()Landroid/view/Surface;", 1123 (void *)android_media_MediaCodec_createInputSurface }, 1124 1125 { "start", "()V", (void *)android_media_MediaCodec_start }, 1126 { "native_stop", "()V", (void *)android_media_MediaCodec_stop }, 1127 { "flush", "()V", (void *)android_media_MediaCodec_flush }, 1128 1129 { "queueInputBuffer", "(IIIJI)V", 1130 (void *)android_media_MediaCodec_queueInputBuffer }, 1131 1132 { "queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V", 1133 (void *)android_media_MediaCodec_queueSecureInputBuffer }, 1134 1135 { "dequeueInputBuffer", "(J)I", 1136 (void *)android_media_MediaCodec_dequeueInputBuffer }, 1137 1138 { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I", 1139 (void *)android_media_MediaCodec_dequeueOutputBuffer }, 1140 1141 { "releaseOutputBuffer", "(IZ)V", 1142 (void *)android_media_MediaCodec_releaseOutputBuffer }, 1143 1144 { "signalEndOfInputStream", "()V", 1145 (void *)android_media_MediaCodec_signalEndOfInputStream }, 1146 1147 { "getOutputFormatNative", "()Ljava/util/Map;", 1148 (void *)android_media_MediaCodec_getOutputFormatNative }, 1149 1150 { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;", 1151 (void *)android_media_MediaCodec_getBuffers }, 1152 1153 { "getName", "()Ljava/lang/String;", 1154 (void *)android_media_MediaCodec_getName }, 1155 1156 { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V", 1157 (void *)android_media_MediaCodec_setParameters }, 1158 1159 { "setVideoScalingMode", "(I)V", 1160 (void *)android_media_MediaCodec_setVideoScalingMode }, 1161 1162 { "native_init", "()V", (void *)android_media_MediaCodec_native_init }, 1163 1164 { "native_setup", "(Ljava/lang/String;ZZ)V", 1165 (void *)android_media_MediaCodec_native_setup }, 1166 1167 { "native_finalize", "()V", 1168 (void *)android_media_MediaCodec_native_finalize }, 1169}; 1170 1171int register_android_media_MediaCodec(JNIEnv *env) { 1172 return AndroidRuntime::registerNativeMethods(env, 1173 "android/media/MediaCodec", gMethods, NELEM(gMethods)); 1174} 1175