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