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