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