android_media_MediaCodec.cpp revision 8034d60f6e044deafe48501dfbfe17c31284bc17
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#include <media/stagefright/PersistentSurface.h> 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 61static struct CryptoErrorCodes { 62 jint cryptoErrorNoKey; 63 jint cryptoErrorKeyExpired; 64 jint cryptoErrorResourceBusy; 65 jint cryptoErrorInsufficientOutputProtection; 66} gCryptoErrorCodes; 67 68static struct CodecActionCodes { 69 jint codecActionTransient; 70 jint codecActionRecoverable; 71} gCodecActionCodes; 72 73static struct ExceptionReason { 74 jint reasonHardware; 75 jint reasonReclaimed; 76} gExceptionReason; 77 78static struct { 79 jclass clazz; 80 jfieldID mLock; 81 jfieldID mPersistentObject; 82 jmethodID ctor; 83 jmethodID setNativeObjectLocked; 84} gPersistentSurfaceClassInfo; 85 86struct fields_t { 87 jfieldID context; 88 jmethodID postEventFromNativeID; 89 jfieldID cryptoInfoNumSubSamplesID; 90 jfieldID cryptoInfoNumBytesOfClearDataID; 91 jfieldID cryptoInfoNumBytesOfEncryptedDataID; 92 jfieldID cryptoInfoKeyID; 93 jfieldID cryptoInfoIVID; 94 jfieldID cryptoInfoModeID; 95}; 96 97static fields_t gFields; 98static const void *sRefBaseOwner; 99 100//////////////////////////////////////////////////////////////////////////////// 101 102JMediaCodec::JMediaCodec( 103 JNIEnv *env, jobject thiz, 104 const char *name, bool nameIsType, bool encoder) 105 : mClass(NULL), 106 mObject(NULL) { 107 jclass clazz = env->GetObjectClass(thiz); 108 CHECK(clazz != NULL); 109 110 mClass = (jclass)env->NewGlobalRef(clazz); 111 mObject = env->NewWeakGlobalRef(thiz); 112 113 cacheJavaObjects(env); 114 115 mLooper = new ALooper; 116 mLooper->setName("MediaCodec_looper"); 117 118 mLooper->start( 119 false, // runOnCallingThread 120 true, // canCallJava 121 PRIORITY_FOREGROUND); 122 123 if (nameIsType) { 124 mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus); 125 } else { 126 mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus); 127 } 128 CHECK((mCodec != NULL) != (mInitStatus != OK)); 129} 130 131void JMediaCodec::cacheJavaObjects(JNIEnv *env) { 132 jclass clazz = (jclass)env->FindClass("java/nio/ByteBuffer"); 133 mByteBufferClass = (jclass)env->NewGlobalRef(clazz); 134 CHECK(mByteBufferClass != NULL); 135 136 ScopedLocalRef<jclass> byteOrderClass( 137 env, env->FindClass("java/nio/ByteOrder")); 138 CHECK(byteOrderClass.get() != NULL); 139 140 jmethodID nativeOrderID = env->GetStaticMethodID( 141 byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;"); 142 CHECK(nativeOrderID != NULL); 143 144 jobject nativeByteOrderObj = 145 env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID); 146 mNativeByteOrderObj = env->NewGlobalRef(nativeByteOrderObj); 147 CHECK(mNativeByteOrderObj != NULL); 148 env->DeleteLocalRef(nativeByteOrderObj); 149 nativeByteOrderObj = NULL; 150 151 mByteBufferOrderMethodID = env->GetMethodID( 152 mByteBufferClass, 153 "order", 154 "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"); 155 CHECK(mByteBufferOrderMethodID != NULL); 156 157 mByteBufferAsReadOnlyBufferMethodID = env->GetMethodID( 158 mByteBufferClass, "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;"); 159 CHECK(mByteBufferAsReadOnlyBufferMethodID != NULL); 160 161 mByteBufferPositionMethodID = env->GetMethodID( 162 mByteBufferClass, "position", "(I)Ljava/nio/Buffer;"); 163 CHECK(mByteBufferPositionMethodID != NULL); 164 165 mByteBufferLimitMethodID = env->GetMethodID( 166 mByteBufferClass, "limit", "(I)Ljava/nio/Buffer;"); 167 CHECK(mByteBufferLimitMethodID != NULL); 168} 169 170status_t JMediaCodec::initCheck() const { 171 return mInitStatus; 172} 173 174void JMediaCodec::registerSelf() { 175 mLooper->registerHandler(this); 176} 177 178void JMediaCodec::release() { 179 if (mCodec != NULL) { 180 mCodec->release(); 181 mCodec.clear(); 182 mInitStatus = NO_INIT; 183 } 184 185 if (mLooper != NULL) { 186 mLooper->unregisterHandler(id()); 187 mLooper->stop(); 188 mLooper.clear(); 189 } 190} 191 192JMediaCodec::~JMediaCodec() { 193 if (mCodec != NULL || mLooper != NULL) { 194 /* MediaCodec and looper should have been released explicitly already 195 * in setMediaCodec() (see comments in setMediaCodec()). 196 * 197 * Otherwise JMediaCodec::~JMediaCodec() might be called from within the 198 * message handler, doing release() there risks deadlock as MediaCodec:: 199 * release() post synchronous message to the same looper. 200 * 201 * Print a warning and try to proceed with releasing. 202 */ 203 ALOGW("try to release MediaCodec from JMediaCodec::~JMediaCodec()..."); 204 release(); 205 ALOGW("done releasing MediaCodec from JMediaCodec::~JMediaCodec()."); 206 } 207 208 JNIEnv *env = AndroidRuntime::getJNIEnv(); 209 210 env->DeleteWeakGlobalRef(mObject); 211 mObject = NULL; 212 env->DeleteGlobalRef(mClass); 213 mClass = NULL; 214 deleteJavaObjects(env); 215} 216 217void JMediaCodec::deleteJavaObjects(JNIEnv *env) { 218 env->DeleteGlobalRef(mByteBufferClass); 219 mByteBufferClass = NULL; 220 env->DeleteGlobalRef(mNativeByteOrderObj); 221 mNativeByteOrderObj = NULL; 222 223 mByteBufferOrderMethodID = NULL; 224 mByteBufferAsReadOnlyBufferMethodID = NULL; 225 mByteBufferPositionMethodID = NULL; 226 mByteBufferLimitMethodID = NULL; 227} 228 229status_t JMediaCodec::setCallback(jobject cb) { 230 if (cb != NULL) { 231 if (mCallbackNotification == NULL) { 232 mCallbackNotification = new AMessage(kWhatCallbackNotify, this); 233 } 234 } else { 235 mCallbackNotification.clear(); 236 } 237 238 return mCodec->setCallback(mCallbackNotification); 239} 240 241status_t JMediaCodec::configure( 242 const sp<AMessage> &format, 243 const sp<IGraphicBufferProducer> &bufferProducer, 244 const sp<ICrypto> &crypto, 245 int flags) { 246 sp<Surface> client; 247 if (bufferProducer != NULL) { 248 mSurfaceTextureClient = 249 new Surface(bufferProducer, true /* controlledByApp */); 250 } else { 251 mSurfaceTextureClient.clear(); 252 } 253 254 return mCodec->configure(format, mSurfaceTextureClient, crypto, flags); 255} 256 257status_t JMediaCodec::createInputSurface( 258 sp<IGraphicBufferProducer>* bufferProducer) { 259 return mCodec->createInputSurface(bufferProducer); 260} 261 262status_t JMediaCodec::usePersistentInputSurface( 263 const sp<PersistentSurface> &surface) { 264 return mCodec->usePersistentInputSurface(surface); 265} 266 267status_t JMediaCodec::start() { 268 return mCodec->start(); 269} 270 271status_t JMediaCodec::stop() { 272 mSurfaceTextureClient.clear(); 273 274 return mCodec->stop(); 275} 276 277status_t JMediaCodec::flush() { 278 return mCodec->flush(); 279} 280 281status_t JMediaCodec::reset() { 282 return mCodec->reset(); 283} 284 285status_t JMediaCodec::queueInputBuffer( 286 size_t index, 287 size_t offset, size_t size, int64_t timeUs, uint32_t flags, 288 AString *errorDetailMsg) { 289 return mCodec->queueInputBuffer( 290 index, offset, size, timeUs, flags, errorDetailMsg); 291} 292 293status_t JMediaCodec::queueSecureInputBuffer( 294 size_t index, 295 size_t offset, 296 const CryptoPlugin::SubSample *subSamples, 297 size_t numSubSamples, 298 const uint8_t key[16], 299 const uint8_t iv[16], 300 CryptoPlugin::Mode mode, 301 int64_t presentationTimeUs, 302 uint32_t flags, 303 AString *errorDetailMsg) { 304 return mCodec->queueSecureInputBuffer( 305 index, offset, subSamples, numSubSamples, key, iv, mode, 306 presentationTimeUs, flags, errorDetailMsg); 307} 308 309status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { 310 return mCodec->dequeueInputBuffer(index, timeoutUs); 311} 312 313status_t JMediaCodec::dequeueOutputBuffer( 314 JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) { 315 size_t size, offset; 316 int64_t timeUs; 317 uint32_t flags; 318 status_t err = mCodec->dequeueOutputBuffer( 319 index, &offset, &size, &timeUs, &flags, timeoutUs); 320 321 if (err != OK) { 322 return err; 323 } 324 325 ScopedLocalRef<jclass> clazz( 326 env, env->FindClass("android/media/MediaCodec$BufferInfo")); 327 328 jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V"); 329 env->CallVoidMethod(bufferInfo, method, (jint)offset, (jint)size, timeUs, flags); 330 331 return OK; 332} 333 334status_t JMediaCodec::releaseOutputBuffer( 335 size_t index, bool render, bool updatePTS, int64_t timestampNs) { 336 if (updatePTS) { 337 return mCodec->renderOutputBufferAndRelease(index, timestampNs); 338 } 339 return render 340 ? mCodec->renderOutputBufferAndRelease(index) 341 : mCodec->releaseOutputBuffer(index); 342} 343 344status_t JMediaCodec::signalEndOfInputStream() { 345 return mCodec->signalEndOfInputStream(); 346} 347 348status_t JMediaCodec::getFormat(JNIEnv *env, bool input, jobject *format) const { 349 sp<AMessage> msg; 350 status_t err; 351 err = input ? mCodec->getInputFormat(&msg) : mCodec->getOutputFormat(&msg); 352 if (err != OK) { 353 return err; 354 } 355 356 return ConvertMessageToMap(env, msg, format); 357} 358 359status_t JMediaCodec::getOutputFormat(JNIEnv *env, size_t index, jobject *format) const { 360 sp<AMessage> msg; 361 status_t err; 362 if ((err = mCodec->getOutputFormat(index, &msg)) != OK) { 363 return err; 364 } 365 366 return ConvertMessageToMap(env, msg, format); 367} 368 369status_t JMediaCodec::getBuffers( 370 JNIEnv *env, bool input, jobjectArray *bufArray) const { 371 Vector<sp<ABuffer> > buffers; 372 373 status_t err = 374 input 375 ? mCodec->getInputBuffers(&buffers) 376 : mCodec->getOutputBuffers(&buffers); 377 378 if (err != OK) { 379 return err; 380 } 381 382 *bufArray = (jobjectArray)env->NewObjectArray( 383 buffers.size(), mByteBufferClass, NULL); 384 if (*bufArray == NULL) { 385 return NO_MEMORY; 386 } 387 388 for (size_t i = 0; i < buffers.size(); ++i) { 389 const sp<ABuffer> &buffer = buffers.itemAt(i); 390 391 jobject byteBuffer = NULL; 392 err = createByteBufferFromABuffer( 393 env, !input /* readOnly */, true /* clearBuffer */, buffer, &byteBuffer); 394 if (err != OK) { 395 return err; 396 } 397 if (byteBuffer != NULL) { 398 env->SetObjectArrayElement( 399 *bufArray, i, byteBuffer); 400 401 env->DeleteLocalRef(byteBuffer); 402 byteBuffer = NULL; 403 } 404 } 405 406 return OK; 407} 408 409// static 410status_t JMediaCodec::createByteBufferFromABuffer( 411 JNIEnv *env, bool readOnly, bool clearBuffer, const sp<ABuffer> &buffer, 412 jobject *buf) const { 413 // if this is an ABuffer that doesn't actually hold any accessible memory, 414 // use a null ByteBuffer 415 *buf = NULL; 416 if (buffer->base() == NULL) { 417 return OK; 418 } 419 420 jobject byteBuffer = 421 env->NewDirectByteBuffer(buffer->base(), buffer->capacity()); 422 if (readOnly && byteBuffer != NULL) { 423 jobject readOnlyBuffer = env->CallObjectMethod( 424 byteBuffer, mByteBufferAsReadOnlyBufferMethodID); 425 env->DeleteLocalRef(byteBuffer); 426 byteBuffer = readOnlyBuffer; 427 } 428 if (byteBuffer == NULL) { 429 return NO_MEMORY; 430 } 431 jobject me = env->CallObjectMethod( 432 byteBuffer, mByteBufferOrderMethodID, mNativeByteOrderObj); 433 env->DeleteLocalRef(me); 434 me = env->CallObjectMethod( 435 byteBuffer, mByteBufferLimitMethodID, 436 clearBuffer ? buffer->capacity() : (buffer->offset() + buffer->size())); 437 env->DeleteLocalRef(me); 438 me = env->CallObjectMethod( 439 byteBuffer, mByteBufferPositionMethodID, 440 clearBuffer ? 0 : buffer->offset()); 441 env->DeleteLocalRef(me); 442 me = NULL; 443 444 *buf = byteBuffer; 445 return OK; 446} 447 448status_t JMediaCodec::getBuffer( 449 JNIEnv *env, bool input, size_t index, jobject *buf) const { 450 sp<ABuffer> buffer; 451 452 status_t err = 453 input 454 ? mCodec->getInputBuffer(index, &buffer) 455 : mCodec->getOutputBuffer(index, &buffer); 456 457 if (err != OK) { 458 return err; 459 } 460 461 return createByteBufferFromABuffer( 462 env, !input /* readOnly */, input /* clearBuffer */, buffer, buf); 463} 464 465status_t JMediaCodec::getImage( 466 JNIEnv *env, bool input, size_t index, jobject *buf) const { 467 sp<ABuffer> buffer; 468 469 status_t err = 470 input 471 ? mCodec->getInputBuffer(index, &buffer) 472 : mCodec->getOutputBuffer(index, &buffer); 473 474 if (err != OK) { 475 return err; 476 } 477 478 // if this is an ABuffer that doesn't actually hold any accessible memory, 479 // use a null ByteBuffer 480 *buf = NULL; 481 if (buffer->base() == NULL) { 482 return OK; 483 } 484 485 // check if buffer is an image 486 sp<ABuffer> imageData; 487 if (!buffer->meta()->findBuffer("image-data", &imageData)) { 488 return OK; 489 } 490 491 int64_t timestamp = 0; 492 if (!input && buffer->meta()->findInt64("timeUs", ×tamp)) { 493 timestamp *= 1000; // adjust to ns 494 } 495 496 jobject byteBuffer = NULL; 497 err = createByteBufferFromABuffer( 498 env, !input /* readOnly */, input /* clearBuffer */, buffer, &byteBuffer); 499 if (err != OK) { 500 return OK; 501 } 502 503 jobject infoBuffer = NULL; 504 err = createByteBufferFromABuffer( 505 env, true /* readOnly */, true /* clearBuffer */, imageData, &infoBuffer); 506 if (err != OK) { 507 env->DeleteLocalRef(byteBuffer); 508 byteBuffer = NULL; 509 return OK; 510 } 511 512 jobject cropRect = NULL; 513 int32_t left, top, right, bottom; 514 if (buffer->meta()->findRect("crop-rect", &left, &top, &right, &bottom)) { 515 ScopedLocalRef<jclass> rectClazz( 516 env, env->FindClass("android/graphics/Rect")); 517 CHECK(rectClazz.get() != NULL); 518 519 jmethodID rectConstructID = env->GetMethodID( 520 rectClazz.get(), "<init>", "(IIII)V"); 521 522 cropRect = env->NewObject( 523 rectClazz.get(), rectConstructID, left, top, right + 1, bottom + 1); 524 } 525 526 ScopedLocalRef<jclass> imageClazz( 527 env, env->FindClass("android/media/MediaCodec$MediaImage")); 528 CHECK(imageClazz.get() != NULL); 529 530 jmethodID imageConstructID = env->GetMethodID(imageClazz.get(), "<init>", 531 "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;ZJIILandroid/graphics/Rect;)V"); 532 533 *buf = env->NewObject(imageClazz.get(), imageConstructID, 534 byteBuffer, infoBuffer, 535 (jboolean)!input /* readOnly */, 536 (jlong)timestamp, 537 (jint)0 /* xOffset */, (jint)0 /* yOffset */, cropRect); 538 539 // if MediaImage creation fails, return null 540 if (env->ExceptionCheck()) { 541 env->ExceptionDescribe(); 542 env->ExceptionClear(); 543 *buf = NULL; 544 } 545 546 if (cropRect != NULL) { 547 env->DeleteLocalRef(cropRect); 548 cropRect = NULL; 549 } 550 551 env->DeleteLocalRef(byteBuffer); 552 byteBuffer = NULL; 553 554 env->DeleteLocalRef(infoBuffer); 555 infoBuffer = NULL; 556 557 return OK; 558} 559 560status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const { 561 AString name; 562 563 status_t err = mCodec->getName(&name); 564 565 if (err != OK) { 566 return err; 567 } 568 569 *nameStr = env->NewStringUTF(name.c_str()); 570 571 return OK; 572} 573 574status_t JMediaCodec::setParameters(const sp<AMessage> &msg) { 575 return mCodec->setParameters(msg); 576} 577 578void JMediaCodec::setVideoScalingMode(int mode) { 579 if (mSurfaceTextureClient != NULL) { 580 native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode); 581 } 582} 583 584static jthrowable createCodecException( 585 JNIEnv *env, status_t err, int32_t actionCode, const char *msg = NULL) { 586 ScopedLocalRef<jclass> clazz( 587 env, env->FindClass("android/media/MediaCodec$CodecException")); 588 CHECK(clazz.get() != NULL); 589 590 const jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(IILjava/lang/String;I)V"); 591 CHECK(ctor != NULL); 592 593 ScopedLocalRef<jstring> msgObj( 594 env, env->NewStringUTF(msg != NULL ? msg : String8::format("Error %#x", err))); 595 596 // translate action code to Java equivalent 597 switch (actionCode) { 598 case ACTION_CODE_TRANSIENT: 599 actionCode = gCodecActionCodes.codecActionTransient; 600 break; 601 case ACTION_CODE_RECOVERABLE: 602 actionCode = gCodecActionCodes.codecActionRecoverable; 603 break; 604 default: 605 actionCode = 0; // everything else is fatal 606 break; 607 } 608 609 int reason = 610 (err == DEAD_OBJECT) ? gExceptionReason.reasonReclaimed : gExceptionReason.reasonHardware; 611 return (jthrowable)env->NewObject(clazz.get(), ctor, err, actionCode, msgObj.get(), reason); 612} 613 614void JMediaCodec::handleCallback(const sp<AMessage> &msg) { 615 int32_t arg1, arg2 = 0; 616 jobject obj = NULL; 617 CHECK(msg->findInt32("callbackID", &arg1)); 618 JNIEnv *env = AndroidRuntime::getJNIEnv(); 619 620 switch (arg1) { 621 case MediaCodec::CB_INPUT_AVAILABLE: 622 { 623 CHECK(msg->findInt32("index", &arg2)); 624 break; 625 } 626 627 case MediaCodec::CB_OUTPUT_AVAILABLE: 628 { 629 CHECK(msg->findInt32("index", &arg2)); 630 631 size_t size, offset; 632 int64_t timeUs; 633 uint32_t flags; 634 CHECK(msg->findSize("size", &size)); 635 CHECK(msg->findSize("offset", &offset)); 636 CHECK(msg->findInt64("timeUs", &timeUs)); 637 CHECK(msg->findInt32("flags", (int32_t *)&flags)); 638 639 ScopedLocalRef<jclass> clazz( 640 env, env->FindClass("android/media/MediaCodec$BufferInfo")); 641 jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "()V"); 642 jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V"); 643 644 obj = env->NewObject(clazz.get(), ctor); 645 646 if (obj == NULL) { 647 if (env->ExceptionCheck()) { 648 ALOGE("Could not create MediaCodec.BufferInfo."); 649 env->ExceptionClear(); 650 } 651 jniThrowException(env, "java/lang/IllegalStateException", NULL); 652 return; 653 } 654 655 env->CallVoidMethod(obj, method, (jint)offset, (jint)size, timeUs, flags); 656 break; 657 } 658 659 case MediaCodec::CB_ERROR: 660 { 661 int32_t err, actionCode; 662 CHECK(msg->findInt32("err", &err)); 663 CHECK(msg->findInt32("actionCode", &actionCode)); 664 665 // note that DRM errors could conceivably alias into a CodecException 666 obj = (jobject)createCodecException(env, err, actionCode); 667 668 if (obj == NULL) { 669 if (env->ExceptionCheck()) { 670 ALOGE("Could not create CodecException object."); 671 env->ExceptionClear(); 672 } 673 jniThrowException(env, "java/lang/IllegalStateException", NULL); 674 return; 675 } 676 677 break; 678 } 679 680 case MediaCodec::CB_OUTPUT_FORMAT_CHANGED: 681 { 682 sp<AMessage> format; 683 CHECK(msg->findMessage("format", &format)); 684 685 if (OK != ConvertMessageToMap(env, format, &obj)) { 686 jniThrowException(env, "java/lang/IllegalStateException", NULL); 687 return; 688 } 689 690 break; 691 } 692 693 default: 694 TRESPASS(); 695 } 696 697 env->CallVoidMethod( 698 mObject, 699 gFields.postEventFromNativeID, 700 EVENT_CALLBACK, 701 arg1, 702 arg2, 703 obj); 704 705 env->DeleteLocalRef(obj); 706} 707 708void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) { 709 switch (msg->what()) { 710 case kWhatCallbackNotify: 711 { 712 handleCallback(msg); 713 break; 714 } 715 default: 716 TRESPASS(); 717 } 718} 719 720} // namespace android 721 722//////////////////////////////////////////////////////////////////////////////// 723 724using namespace android; 725 726static sp<JMediaCodec> setMediaCodec( 727 JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) { 728 sp<JMediaCodec> old = (JMediaCodec *)env->GetLongField(thiz, gFields.context); 729 if (codec != NULL) { 730 codec->incStrong(thiz); 731 } 732 if (old != NULL) { 733 /* release MediaCodec and stop the looper now before decStrong. 734 * otherwise JMediaCodec::~JMediaCodec() could be called from within 735 * its message handler, doing release() from there will deadlock 736 * (as MediaCodec::release() post synchronous message to the same looper) 737 */ 738 old->release(); 739 old->decStrong(thiz); 740 } 741 env->SetLongField(thiz, gFields.context, (jlong)codec.get()); 742 743 return old; 744} 745 746static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) { 747 return (JMediaCodec *)env->GetLongField(thiz, gFields.context); 748} 749 750static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) { 751 setMediaCodec(env, thiz, NULL); 752} 753 754static void throwCodecException(JNIEnv *env, status_t err, int32_t actionCode, const char *msg) { 755 jthrowable exception = createCodecException(env, err, actionCode, msg); 756 env->Throw(exception); 757} 758 759static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { 760 ScopedLocalRef<jclass> clazz( 761 env, env->FindClass("android/media/MediaCodec$CryptoException")); 762 CHECK(clazz.get() != NULL); 763 764 jmethodID constructID = 765 env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V"); 766 CHECK(constructID != NULL); 767 768 jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error"); 769 770 /* translate OS errors to Java API CryptoException errorCodes (which are positive) */ 771 switch (err) { 772 case ERROR_DRM_NO_LICENSE: 773 err = gCryptoErrorCodes.cryptoErrorNoKey; 774 break; 775 case ERROR_DRM_LICENSE_EXPIRED: 776 err = gCryptoErrorCodes.cryptoErrorKeyExpired; 777 break; 778 case ERROR_DRM_RESOURCE_BUSY: 779 err = gCryptoErrorCodes.cryptoErrorResourceBusy; 780 break; 781 case ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION: 782 err = gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection; 783 break; 784 default: /* Other negative DRM error codes go out as is. */ 785 break; 786 } 787 788 jthrowable exception = 789 (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj); 790 791 env->Throw(exception); 792} 793 794static jint throwExceptionAsNecessary( 795 JNIEnv *env, status_t err, int32_t actionCode = ACTION_CODE_FATAL, 796 const char *msg = NULL) { 797 switch (err) { 798 case OK: 799 return 0; 800 801 case -EAGAIN: 802 return DEQUEUE_INFO_TRY_AGAIN_LATER; 803 804 case INFO_FORMAT_CHANGED: 805 return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED; 806 807 case INFO_OUTPUT_BUFFERS_CHANGED: 808 return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED; 809 810 case INVALID_OPERATION: 811 jniThrowException(env, "java/lang/IllegalStateException", msg); 812 return 0; 813 814 default: 815 if (isCryptoError(err)) { 816 throwCryptoException(env, err, msg); 817 return 0; 818 } 819 throwCodecException(env, err, actionCode, msg); 820 return 0; 821 } 822} 823 824static void android_media_MediaCodec_native_setCallback( 825 JNIEnv *env, 826 jobject thiz, 827 jobject cb) { 828 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 829 830 if (codec == NULL) { 831 throwExceptionAsNecessary(env, INVALID_OPERATION); 832 return; 833 } 834 835 status_t err = codec->setCallback(cb); 836 837 throwExceptionAsNecessary(env, err); 838} 839 840static void android_media_MediaCodec_native_configure( 841 JNIEnv *env, 842 jobject thiz, 843 jobjectArray keys, jobjectArray values, 844 jobject jsurface, 845 jobject jcrypto, 846 jint flags) { 847 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 848 849 if (codec == NULL) { 850 throwExceptionAsNecessary(env, INVALID_OPERATION); 851 return; 852 } 853 854 sp<AMessage> format; 855 status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format); 856 857 if (err != OK) { 858 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 859 return; 860 } 861 862 sp<IGraphicBufferProducer> bufferProducer; 863 if (jsurface != NULL) { 864 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface)); 865 if (surface != NULL) { 866 bufferProducer = surface->getIGraphicBufferProducer(); 867 } else { 868 jniThrowException( 869 env, 870 "java/lang/IllegalArgumentException", 871 "The surface has been released"); 872 return; 873 } 874 } 875 876 sp<ICrypto> crypto; 877 if (jcrypto != NULL) { 878 crypto = JCrypto::GetCrypto(env, jcrypto); 879 } 880 881 err = codec->configure(format, bufferProducer, crypto, flags); 882 883 throwExceptionAsNecessary(env, err); 884} 885 886sp<PersistentSurface> android_media_MediaCodec_getPersistentInputSurface( 887 JNIEnv* env, jobject object) { 888 sp<PersistentSurface> persistentSurface; 889 890 jobject lock = env->GetObjectField( 891 object, gPersistentSurfaceClassInfo.mLock); 892 if (env->MonitorEnter(lock) == JNI_OK) { 893 persistentSurface = reinterpret_cast<PersistentSurface *>( 894 env->GetLongField(object, 895 gPersistentSurfaceClassInfo.mPersistentObject)); 896 env->MonitorExit(lock); 897 } 898 env->DeleteLocalRef(lock); 899 900 return persistentSurface; 901} 902 903static jobject android_media_MediaCodec_createPersistentInputSurface( 904 JNIEnv* env, jclass /* clazz */) { 905 ALOGV("android_media_MediaCodec_createPersistentInputSurface"); 906 sp<PersistentSurface> persistentSurface = 907 MediaCodec::CreatePersistentInputSurface(); 908 909 if (persistentSurface == NULL) { 910 return NULL; 911 } 912 913 sp<Surface> surface = new Surface( 914 persistentSurface->getBufferProducer(), true); 915 if (surface == NULL) { 916 return NULL; 917 } 918 919 jobject object = env->NewObject( 920 gPersistentSurfaceClassInfo.clazz, 921 gPersistentSurfaceClassInfo.ctor); 922 923 if (object == NULL) { 924 if (env->ExceptionCheck()) { 925 ALOGE("Could not create PersistentSurface."); 926 env->ExceptionClear(); 927 } 928 return NULL; 929 } 930 931 jobject lock = env->GetObjectField( 932 object, gPersistentSurfaceClassInfo.mLock); 933 if (env->MonitorEnter(lock) == JNI_OK) { 934 env->CallVoidMethod( 935 object, 936 gPersistentSurfaceClassInfo.setNativeObjectLocked, 937 (jlong)surface.get()); 938 env->SetLongField( 939 object, 940 gPersistentSurfaceClassInfo.mPersistentObject, 941 (jlong)persistentSurface.get()); 942 env->MonitorExit(lock); 943 } else { 944 env->DeleteLocalRef(object); 945 object = NULL; 946 } 947 env->DeleteLocalRef(lock); 948 949 if (object != NULL) { 950 surface->incStrong(&sRefBaseOwner); 951 persistentSurface->incStrong(&sRefBaseOwner); 952 } 953 954 return object; 955} 956 957static void android_media_MediaCodec_releasePersistentInputSurface( 958 JNIEnv* env, jclass /* clazz */, jobject object) { 959 sp<PersistentSurface> persistentSurface; 960 961 jobject lock = env->GetObjectField( 962 object, gPersistentSurfaceClassInfo.mLock); 963 if (env->MonitorEnter(lock) == JNI_OK) { 964 persistentSurface = reinterpret_cast<PersistentSurface *>( 965 env->GetLongField( 966 object, gPersistentSurfaceClassInfo.mPersistentObject)); 967 env->SetLongField( 968 object, 969 gPersistentSurfaceClassInfo.mPersistentObject, 970 (jlong)0); 971 env->MonitorExit(lock); 972 } 973 env->DeleteLocalRef(lock); 974 975 if (persistentSurface != NULL) { 976 persistentSurface->decStrong(&sRefBaseOwner); 977 } 978 // no need to release surface as it will be released by Surface's jni 979} 980 981static void android_media_MediaCodec_usePersistentInputSurface( 982 JNIEnv* env, jobject thiz, jobject object) { 983 ALOGV("android_media_MediaCodec_usePersistentInputSurface"); 984 985 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 986 if (codec == NULL) { 987 throwExceptionAsNecessary(env, INVALID_OPERATION); 988 return; 989 } 990 991 sp<PersistentSurface> persistentSurface = 992 android_media_MediaCodec_getPersistentInputSurface(env, object); 993 994 status_t err = codec->usePersistentInputSurface(persistentSurface); 995 if (err != NO_ERROR) { 996 throwExceptionAsNecessary(env, err); 997 } 998} 999 1000static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env, 1001 jobject thiz) { 1002 ALOGV("android_media_MediaCodec_createInputSurface"); 1003 1004 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1005 if (codec == NULL) { 1006 throwExceptionAsNecessary(env, INVALID_OPERATION); 1007 return NULL; 1008 } 1009 1010 // Tell the MediaCodec that we want to use a Surface as input. 1011 sp<IGraphicBufferProducer> bufferProducer; 1012 status_t err = codec->createInputSurface(&bufferProducer); 1013 if (err != NO_ERROR) { 1014 throwExceptionAsNecessary(env, err); 1015 return NULL; 1016 } 1017 1018 // Wrap the IGBP in a Java-language Surface. 1019 return android_view_Surface_createFromIGraphicBufferProducer(env, 1020 bufferProducer); 1021} 1022 1023static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) { 1024 ALOGV("android_media_MediaCodec_start"); 1025 1026 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1027 1028 if (codec == NULL) { 1029 throwExceptionAsNecessary(env, INVALID_OPERATION); 1030 return; 1031 } 1032 1033 status_t err = codec->start(); 1034 1035 throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, "start failed"); 1036} 1037 1038static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) { 1039 ALOGV("android_media_MediaCodec_stop"); 1040 1041 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1042 1043 if (codec == NULL) { 1044 throwExceptionAsNecessary(env, INVALID_OPERATION); 1045 return; 1046 } 1047 1048 status_t err = codec->stop(); 1049 1050 throwExceptionAsNecessary(env, err); 1051} 1052 1053static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) { 1054 ALOGV("android_media_MediaCodec_reset"); 1055 1056 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1057 1058 if (codec == NULL) { 1059 throwExceptionAsNecessary(env, INVALID_OPERATION); 1060 return; 1061 } 1062 1063 status_t err = codec->reset(); 1064 if (err != OK) { 1065 // treat all errors as fatal for now, though resource not available 1066 // errors could be treated as transient. 1067 // we also should avoid sending INVALID_OPERATION here due to 1068 // the transitory nature of reset(), it should not inadvertently 1069 // trigger an IllegalStateException. 1070 err = UNKNOWN_ERROR; 1071 } 1072 throwExceptionAsNecessary(env, err); 1073} 1074 1075static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) { 1076 ALOGV("android_media_MediaCodec_flush"); 1077 1078 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1079 1080 if (codec == NULL) { 1081 throwExceptionAsNecessary(env, INVALID_OPERATION); 1082 return; 1083 } 1084 1085 status_t err = codec->flush(); 1086 1087 throwExceptionAsNecessary(env, err); 1088} 1089 1090static void android_media_MediaCodec_queueInputBuffer( 1091 JNIEnv *env, 1092 jobject thiz, 1093 jint index, 1094 jint offset, 1095 jint size, 1096 jlong timestampUs, 1097 jint flags) { 1098 ALOGV("android_media_MediaCodec_queueInputBuffer"); 1099 1100 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1101 1102 if (codec == NULL) { 1103 throwExceptionAsNecessary(env, INVALID_OPERATION); 1104 return; 1105 } 1106 1107 AString errorDetailMsg; 1108 1109 status_t err = codec->queueInputBuffer( 1110 index, offset, size, timestampUs, flags, &errorDetailMsg); 1111 1112 throwExceptionAsNecessary( 1113 env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); 1114} 1115 1116static void android_media_MediaCodec_queueSecureInputBuffer( 1117 JNIEnv *env, 1118 jobject thiz, 1119 jint index, 1120 jint offset, 1121 jobject cryptoInfoObj, 1122 jlong timestampUs, 1123 jint flags) { 1124 ALOGV("android_media_MediaCodec_queueSecureInputBuffer"); 1125 1126 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1127 1128 if (codec == NULL) { 1129 throwExceptionAsNecessary(env, INVALID_OPERATION); 1130 return; 1131 } 1132 1133 jint numSubSamples = 1134 env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID); 1135 1136 jintArray numBytesOfClearDataObj = 1137 (jintArray)env->GetObjectField( 1138 cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID); 1139 1140 jintArray numBytesOfEncryptedDataObj = 1141 (jintArray)env->GetObjectField( 1142 cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID); 1143 1144 jbyteArray keyObj = 1145 (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID); 1146 1147 jbyteArray ivObj = 1148 (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID); 1149 1150 jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID); 1151 1152 status_t err = OK; 1153 1154 CryptoPlugin::SubSample *subSamples = NULL; 1155 jbyte *key = NULL; 1156 jbyte *iv = NULL; 1157 1158 if (numSubSamples <= 0) { 1159 err = -EINVAL; 1160 } else if (numBytesOfClearDataObj == NULL 1161 && numBytesOfEncryptedDataObj == NULL) { 1162 err = -EINVAL; 1163 } else if (numBytesOfEncryptedDataObj != NULL 1164 && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) { 1165 err = -ERANGE; 1166 } else if (numBytesOfClearDataObj != NULL 1167 && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) { 1168 err = -ERANGE; 1169 // subSamples array may silently overflow if number of samples are too large. Use 1170 // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms 1171 } else if ( CC_UNLIKELY(numSubSamples >= (signed)(INT32_MAX / sizeof(*subSamples))) ) { 1172 err = -EINVAL; 1173 } else { 1174 jboolean isCopy; 1175 1176 jint *numBytesOfClearData = 1177 (numBytesOfClearDataObj == NULL) 1178 ? NULL 1179 : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy); 1180 1181 jint *numBytesOfEncryptedData = 1182 (numBytesOfEncryptedDataObj == NULL) 1183 ? NULL 1184 : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy); 1185 1186 subSamples = new CryptoPlugin::SubSample[numSubSamples]; 1187 1188 for (jint i = 0; i < numSubSamples; ++i) { 1189 subSamples[i].mNumBytesOfClearData = 1190 (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i]; 1191 1192 subSamples[i].mNumBytesOfEncryptedData = 1193 (numBytesOfEncryptedData == NULL) 1194 ? 0 : numBytesOfEncryptedData[i]; 1195 } 1196 1197 if (numBytesOfEncryptedData != NULL) { 1198 env->ReleaseIntArrayElements( 1199 numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0); 1200 numBytesOfEncryptedData = NULL; 1201 } 1202 1203 if (numBytesOfClearData != NULL) { 1204 env->ReleaseIntArrayElements( 1205 numBytesOfClearDataObj, numBytesOfClearData, 0); 1206 numBytesOfClearData = NULL; 1207 } 1208 } 1209 1210 if (err == OK && keyObj != NULL) { 1211 if (env->GetArrayLength(keyObj) != 16) { 1212 err = -EINVAL; 1213 } else { 1214 jboolean isCopy; 1215 key = env->GetByteArrayElements(keyObj, &isCopy); 1216 } 1217 } 1218 1219 if (err == OK && ivObj != NULL) { 1220 if (env->GetArrayLength(ivObj) != 16) { 1221 err = -EINVAL; 1222 } else { 1223 jboolean isCopy; 1224 iv = env->GetByteArrayElements(ivObj, &isCopy); 1225 } 1226 } 1227 1228 AString errorDetailMsg; 1229 1230 if (err == OK) { 1231 err = codec->queueSecureInputBuffer( 1232 index, offset, 1233 subSamples, numSubSamples, 1234 (const uint8_t *)key, (const uint8_t *)iv, 1235 (CryptoPlugin::Mode)mode, 1236 timestampUs, 1237 flags, 1238 &errorDetailMsg); 1239 } 1240 1241 if (iv != NULL) { 1242 env->ReleaseByteArrayElements(ivObj, iv, 0); 1243 iv = NULL; 1244 } 1245 1246 if (key != NULL) { 1247 env->ReleaseByteArrayElements(keyObj, key, 0); 1248 key = NULL; 1249 } 1250 1251 delete[] subSamples; 1252 subSamples = NULL; 1253 1254 throwExceptionAsNecessary( 1255 env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); 1256} 1257 1258static jint android_media_MediaCodec_dequeueInputBuffer( 1259 JNIEnv *env, jobject thiz, jlong timeoutUs) { 1260 ALOGV("android_media_MediaCodec_dequeueInputBuffer"); 1261 1262 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1263 1264 if (codec == NULL) { 1265 throwExceptionAsNecessary(env, INVALID_OPERATION); 1266 return -1; 1267 } 1268 1269 size_t index; 1270 status_t err = codec->dequeueInputBuffer(&index, timeoutUs); 1271 1272 if (err == OK) { 1273 return (jint) index; 1274 } 1275 1276 return throwExceptionAsNecessary(env, err); 1277} 1278 1279static jint android_media_MediaCodec_dequeueOutputBuffer( 1280 JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) { 1281 ALOGV("android_media_MediaCodec_dequeueOutputBuffer"); 1282 1283 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1284 1285 if (codec == NULL) { 1286 throwExceptionAsNecessary(env, INVALID_OPERATION); 1287 return 0; 1288 } 1289 1290 size_t index; 1291 status_t err = codec->dequeueOutputBuffer( 1292 env, bufferInfo, &index, timeoutUs); 1293 1294 if (err == OK) { 1295 return (jint) index; 1296 } 1297 1298 return throwExceptionAsNecessary(env, err); 1299} 1300 1301static void android_media_MediaCodec_releaseOutputBuffer( 1302 JNIEnv *env, jobject thiz, 1303 jint index, jboolean render, jboolean updatePTS, jlong timestampNs) { 1304 ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease"); 1305 1306 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1307 1308 if (codec == NULL) { 1309 throwExceptionAsNecessary(env, INVALID_OPERATION); 1310 return; 1311 } 1312 1313 status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs); 1314 1315 throwExceptionAsNecessary(env, err); 1316} 1317 1318static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env, 1319 jobject thiz) { 1320 ALOGV("android_media_MediaCodec_signalEndOfInputStream"); 1321 1322 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1323 if (codec == NULL) { 1324 throwExceptionAsNecessary(env, INVALID_OPERATION); 1325 return; 1326 } 1327 1328 status_t err = codec->signalEndOfInputStream(); 1329 1330 throwExceptionAsNecessary(env, err); 1331} 1332 1333static jobject android_media_MediaCodec_getFormatNative( 1334 JNIEnv *env, jobject thiz, jboolean input) { 1335 ALOGV("android_media_MediaCodec_getFormatNative"); 1336 1337 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1338 1339 if (codec == NULL) { 1340 throwExceptionAsNecessary(env, INVALID_OPERATION); 1341 return NULL; 1342 } 1343 1344 jobject format; 1345 status_t err = codec->getFormat(env, input, &format); 1346 1347 if (err == OK) { 1348 return format; 1349 } 1350 1351 throwExceptionAsNecessary(env, err); 1352 1353 return NULL; 1354} 1355 1356static jobject android_media_MediaCodec_getOutputFormatForIndexNative( 1357 JNIEnv *env, jobject thiz, jint index) { 1358 ALOGV("android_media_MediaCodec_getOutputFormatForIndexNative"); 1359 1360 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1361 1362 if (codec == NULL) { 1363 throwExceptionAsNecessary(env, INVALID_OPERATION); 1364 return NULL; 1365 } 1366 1367 jobject format; 1368 status_t err = codec->getOutputFormat(env, index, &format); 1369 1370 if (err == OK) { 1371 return format; 1372 } 1373 1374 throwExceptionAsNecessary(env, err); 1375 1376 return NULL; 1377} 1378 1379static jobjectArray android_media_MediaCodec_getBuffers( 1380 JNIEnv *env, jobject thiz, jboolean input) { 1381 ALOGV("android_media_MediaCodec_getBuffers"); 1382 1383 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1384 1385 if (codec == NULL) { 1386 throwExceptionAsNecessary(env, INVALID_OPERATION); 1387 return NULL; 1388 } 1389 1390 jobjectArray buffers; 1391 status_t err = codec->getBuffers(env, input, &buffers); 1392 1393 if (err == OK) { 1394 return buffers; 1395 } 1396 1397 // if we're out of memory, an exception was already thrown 1398 if (err != NO_MEMORY) { 1399 throwExceptionAsNecessary(env, err); 1400 } 1401 1402 return NULL; 1403} 1404 1405static jobject android_media_MediaCodec_getBuffer( 1406 JNIEnv *env, jobject thiz, jboolean input, jint index) { 1407 ALOGV("android_media_MediaCodec_getBuffer"); 1408 1409 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1410 1411 if (codec == NULL) { 1412 throwExceptionAsNecessary(env, INVALID_OPERATION); 1413 return NULL; 1414 } 1415 1416 jobject buffer; 1417 status_t err = codec->getBuffer(env, input, index, &buffer); 1418 1419 if (err == OK) { 1420 return buffer; 1421 } 1422 1423 // if we're out of memory, an exception was already thrown 1424 if (err != NO_MEMORY) { 1425 throwExceptionAsNecessary(env, err); 1426 } 1427 1428 return NULL; 1429} 1430 1431static jobject android_media_MediaCodec_getImage( 1432 JNIEnv *env, jobject thiz, jboolean input, jint index) { 1433 ALOGV("android_media_MediaCodec_getImage"); 1434 1435 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1436 1437 if (codec == NULL) { 1438 throwExceptionAsNecessary(env, INVALID_OPERATION); 1439 return NULL; 1440 } 1441 1442 jobject image; 1443 status_t err = codec->getImage(env, input, index, &image); 1444 1445 if (err == OK) { 1446 return image; 1447 } 1448 1449 // if we're out of memory, an exception was already thrown 1450 if (err != NO_MEMORY) { 1451 throwExceptionAsNecessary(env, err); 1452 } 1453 1454 return NULL; 1455} 1456 1457static jobject android_media_MediaCodec_getName( 1458 JNIEnv *env, jobject thiz) { 1459 ALOGV("android_media_MediaCodec_getName"); 1460 1461 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1462 1463 if (codec == NULL) { 1464 throwExceptionAsNecessary(env, INVALID_OPERATION); 1465 return NULL; 1466 } 1467 1468 jstring name; 1469 status_t err = codec->getName(env, &name); 1470 1471 if (err == OK) { 1472 return name; 1473 } 1474 1475 throwExceptionAsNecessary(env, err); 1476 1477 return NULL; 1478} 1479 1480static void android_media_MediaCodec_setParameters( 1481 JNIEnv *env, jobject thiz, jobjectArray keys, jobjectArray vals) { 1482 ALOGV("android_media_MediaCodec_setParameters"); 1483 1484 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1485 1486 if (codec == NULL) { 1487 throwExceptionAsNecessary(env, INVALID_OPERATION); 1488 return; 1489 } 1490 1491 sp<AMessage> params; 1492 status_t err = ConvertKeyValueArraysToMessage(env, keys, vals, ¶ms); 1493 1494 if (err == OK) { 1495 err = codec->setParameters(params); 1496 } 1497 1498 throwExceptionAsNecessary(env, err); 1499} 1500 1501static void android_media_MediaCodec_setVideoScalingMode( 1502 JNIEnv *env, jobject thiz, jint mode) { 1503 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 1504 1505 if (codec == NULL) { 1506 throwExceptionAsNecessary(env, INVALID_OPERATION); 1507 return; 1508 } 1509 1510 if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW 1511 && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) { 1512 jniThrowException(env, "java/lang/InvalidArgumentException", NULL); 1513 return; 1514 } 1515 1516 codec->setVideoScalingMode(mode); 1517} 1518 1519static void android_media_MediaCodec_native_init(JNIEnv *env) { 1520 ScopedLocalRef<jclass> clazz( 1521 env, env->FindClass("android/media/MediaCodec")); 1522 CHECK(clazz.get() != NULL); 1523 1524 gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J"); 1525 CHECK(gFields.context != NULL); 1526 1527 gFields.postEventFromNativeID = 1528 env->GetMethodID( 1529 clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V"); 1530 1531 CHECK(gFields.postEventFromNativeID != NULL); 1532 1533 clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo")); 1534 CHECK(clazz.get() != NULL); 1535 1536 gFields.cryptoInfoNumSubSamplesID = 1537 env->GetFieldID(clazz.get(), "numSubSamples", "I"); 1538 CHECK(gFields.cryptoInfoNumSubSamplesID != NULL); 1539 1540 gFields.cryptoInfoNumBytesOfClearDataID = 1541 env->GetFieldID(clazz.get(), "numBytesOfClearData", "[I"); 1542 CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL); 1543 1544 gFields.cryptoInfoNumBytesOfEncryptedDataID = 1545 env->GetFieldID(clazz.get(), "numBytesOfEncryptedData", "[I"); 1546 CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL); 1547 1548 gFields.cryptoInfoKeyID = env->GetFieldID(clazz.get(), "key", "[B"); 1549 CHECK(gFields.cryptoInfoKeyID != NULL); 1550 1551 gFields.cryptoInfoIVID = env->GetFieldID(clazz.get(), "iv", "[B"); 1552 CHECK(gFields.cryptoInfoIVID != NULL); 1553 1554 gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I"); 1555 CHECK(gFields.cryptoInfoModeID != NULL); 1556 1557 clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException")); 1558 CHECK(clazz.get() != NULL); 1559 1560 jfieldID field; 1561 field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I"); 1562 CHECK(field != NULL); 1563 gCryptoErrorCodes.cryptoErrorNoKey = 1564 env->GetStaticIntField(clazz.get(), field); 1565 1566 field = env->GetStaticFieldID(clazz.get(), "ERROR_KEY_EXPIRED", "I"); 1567 CHECK(field != NULL); 1568 gCryptoErrorCodes.cryptoErrorKeyExpired = 1569 env->GetStaticIntField(clazz.get(), field); 1570 1571 field = env->GetStaticFieldID(clazz.get(), "ERROR_RESOURCE_BUSY", "I"); 1572 CHECK(field != NULL); 1573 gCryptoErrorCodes.cryptoErrorResourceBusy = 1574 env->GetStaticIntField(clazz.get(), field); 1575 1576 field = env->GetStaticFieldID(clazz.get(), "ERROR_INSUFFICIENT_OUTPUT_PROTECTION", "I"); 1577 CHECK(field != NULL); 1578 gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection = 1579 env->GetStaticIntField(clazz.get(), field); 1580 1581 clazz.reset(env->FindClass("android/media/MediaCodec$CodecException")); 1582 CHECK(clazz.get() != NULL); 1583 field = env->GetStaticFieldID(clazz.get(), "ACTION_TRANSIENT", "I"); 1584 CHECK(field != NULL); 1585 gCodecActionCodes.codecActionTransient = 1586 env->GetStaticIntField(clazz.get(), field); 1587 1588 field = env->GetStaticFieldID(clazz.get(), "ACTION_RECOVERABLE", "I"); 1589 CHECK(field != NULL); 1590 gCodecActionCodes.codecActionRecoverable = 1591 env->GetStaticIntField(clazz.get(), field); 1592 1593 field = env->GetStaticFieldID(clazz.get(), "REASON_HARDWARE", "I"); 1594 CHECK(field != NULL); 1595 gExceptionReason.reasonHardware = 1596 env->GetStaticIntField(clazz.get(), field); 1597 1598 field = env->GetStaticFieldID(clazz.get(), "REASON_RECLAIMED", "I"); 1599 CHECK(field != NULL); 1600 gExceptionReason.reasonReclaimed = 1601 env->GetStaticIntField(clazz.get(), field); 1602 1603 clazz.reset(env->FindClass("android/view/Surface")); 1604 CHECK(clazz.get() != NULL); 1605 1606 field = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;"); 1607 CHECK(field != NULL); 1608 gPersistentSurfaceClassInfo.mLock = field; 1609 1610 jmethodID method = env->GetMethodID(clazz.get(), "setNativeObjectLocked", "(J)V"); 1611 CHECK(method != NULL); 1612 gPersistentSurfaceClassInfo.setNativeObjectLocked = method; 1613 1614 clazz.reset(env->FindClass("android/media/MediaCodec$PersistentSurface")); 1615 CHECK(clazz.get() != NULL); 1616 gPersistentSurfaceClassInfo.clazz = (jclass)env->NewGlobalRef(clazz.get()); 1617 1618 method = env->GetMethodID(clazz.get(), "<init>", "()V"); 1619 CHECK(method != NULL); 1620 gPersistentSurfaceClassInfo.ctor = method; 1621 1622 field = env->GetFieldID(clazz.get(), "mPersistentObject", "J"); 1623 CHECK(field != NULL); 1624 gPersistentSurfaceClassInfo.mPersistentObject = field; 1625} 1626 1627static void android_media_MediaCodec_native_setup( 1628 JNIEnv *env, jobject thiz, 1629 jstring name, jboolean nameIsType, jboolean encoder) { 1630 if (name == NULL) { 1631 jniThrowException(env, "java/lang/NullPointerException", NULL); 1632 return; 1633 } 1634 1635 const char *tmp = env->GetStringUTFChars(name, NULL); 1636 1637 if (tmp == NULL) { 1638 return; 1639 } 1640 1641 sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder); 1642 1643 const status_t err = codec->initCheck(); 1644 if (err == NAME_NOT_FOUND) { 1645 // fail and do not try again. 1646 jniThrowException(env, "java/lang/IllegalArgumentException", 1647 String8::format("Failed to initialize %s, error %#x", tmp, err)); 1648 env->ReleaseStringUTFChars(name, tmp); 1649 return; 1650 } else if (err != OK) { 1651 // believed possible to try again 1652 jniThrowException(env, "java/io/IOException", 1653 String8::format("Failed to find matching codec %s, error %#x", tmp, err)); 1654 env->ReleaseStringUTFChars(name, tmp); 1655 return; 1656 } 1657 1658 env->ReleaseStringUTFChars(name, tmp); 1659 1660 codec->registerSelf(); 1661 1662 setMediaCodec(env,thiz, codec); 1663} 1664 1665static void android_media_MediaCodec_native_finalize( 1666 JNIEnv *env, jobject thiz) { 1667 android_media_MediaCodec_release(env, thiz); 1668} 1669 1670static JNINativeMethod gMethods[] = { 1671 { "native_release", "()V", (void *)android_media_MediaCodec_release }, 1672 1673 { "native_reset", "()V", (void *)android_media_MediaCodec_reset }, 1674 1675 { "native_releasePersistentInputSurface", 1676 "(Landroid/view/Surface;)V", 1677 (void *)android_media_MediaCodec_releasePersistentInputSurface}, 1678 1679 { "native_createPersistentInputSurface", 1680 "()Landroid/media/MediaCodec$PersistentSurface;", 1681 (void *)android_media_MediaCodec_createPersistentInputSurface }, 1682 1683 { "native_usePersistentInputSurface", "(Landroid/view/Surface;)V", 1684 (void *)android_media_MediaCodec_usePersistentInputSurface }, 1685 1686 { "native_setCallback", 1687 "(Landroid/media/MediaCodec$Callback;)V", 1688 (void *)android_media_MediaCodec_native_setCallback }, 1689 1690 { "native_configure", 1691 "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;" 1692 "Landroid/media/MediaCrypto;I)V", 1693 (void *)android_media_MediaCodec_native_configure }, 1694 1695 { "createInputSurface", "()Landroid/view/Surface;", 1696 (void *)android_media_MediaCodec_createInputSurface }, 1697 1698 { "native_start", "()V", (void *)android_media_MediaCodec_start }, 1699 { "native_stop", "()V", (void *)android_media_MediaCodec_stop }, 1700 { "native_flush", "()V", (void *)android_media_MediaCodec_flush }, 1701 1702 { "native_queueInputBuffer", "(IIIJI)V", 1703 (void *)android_media_MediaCodec_queueInputBuffer }, 1704 1705 { "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V", 1706 (void *)android_media_MediaCodec_queueSecureInputBuffer }, 1707 1708 { "native_dequeueInputBuffer", "(J)I", 1709 (void *)android_media_MediaCodec_dequeueInputBuffer }, 1710 1711 { "native_dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I", 1712 (void *)android_media_MediaCodec_dequeueOutputBuffer }, 1713 1714 { "releaseOutputBuffer", "(IZZJ)V", 1715 (void *)android_media_MediaCodec_releaseOutputBuffer }, 1716 1717 { "signalEndOfInputStream", "()V", 1718 (void *)android_media_MediaCodec_signalEndOfInputStream }, 1719 1720 { "getFormatNative", "(Z)Ljava/util/Map;", 1721 (void *)android_media_MediaCodec_getFormatNative }, 1722 1723 { "getOutputFormatNative", "(I)Ljava/util/Map;", 1724 (void *)android_media_MediaCodec_getOutputFormatForIndexNative }, 1725 1726 { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;", 1727 (void *)android_media_MediaCodec_getBuffers }, 1728 1729 { "getBuffer", "(ZI)Ljava/nio/ByteBuffer;", 1730 (void *)android_media_MediaCodec_getBuffer }, 1731 1732 { "getImage", "(ZI)Landroid/media/Image;", 1733 (void *)android_media_MediaCodec_getImage }, 1734 1735 { "getName", "()Ljava/lang/String;", 1736 (void *)android_media_MediaCodec_getName }, 1737 1738 { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V", 1739 (void *)android_media_MediaCodec_setParameters }, 1740 1741 { "setVideoScalingMode", "(I)V", 1742 (void *)android_media_MediaCodec_setVideoScalingMode }, 1743 1744 { "native_init", "()V", (void *)android_media_MediaCodec_native_init }, 1745 1746 { "native_setup", "(Ljava/lang/String;ZZ)V", 1747 (void *)android_media_MediaCodec_native_setup }, 1748 1749 { "native_finalize", "()V", 1750 (void *)android_media_MediaCodec_native_finalize }, 1751}; 1752 1753int register_android_media_MediaCodec(JNIEnv *env) { 1754 return AndroidRuntime::registerNativeMethods(env, 1755 "android/media/MediaCodec", gMethods, NELEM(gMethods)); 1756} 1757