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