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