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