android_media_MediaCodec.cpp revision 9e6bcce17d13bd4f7bba5f8fbcc2e6a0d695274b
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_Crypto.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 <gui/Surface.h> 31#include <gui/SurfaceTextureClient.h> 32 33#include <media/ICrypto.h> 34#include <media/stagefright/MediaCodec.h> 35#include <media/stagefright/foundation/ABuffer.h> 36#include <media/stagefright/foundation/ADebug.h> 37#include <media/stagefright/foundation/ALooper.h> 38#include <media/stagefright/foundation/AMessage.h> 39#include <media/stagefright/MediaErrors.h> 40 41namespace android { 42 43// Keep these in sync with their equivalents in MediaCodec.java !!! 44enum { 45 DEQUEUE_INFO_TRY_AGAIN_LATER = -1, 46 DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED = -2, 47 DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3, 48}; 49 50struct fields_t { 51 jfieldID context; 52}; 53 54static fields_t gFields; 55 56//////////////////////////////////////////////////////////////////////////////// 57 58JMediaCodec::JMediaCodec( 59 JNIEnv *env, jobject thiz, 60 const char *name, bool nameIsType, bool encoder) 61 : mClass(NULL), 62 mObject(NULL) { 63 jclass clazz = env->GetObjectClass(thiz); 64 CHECK(clazz != NULL); 65 66 mClass = (jclass)env->NewGlobalRef(clazz); 67 mObject = env->NewWeakGlobalRef(thiz); 68 69 mLooper = new ALooper; 70 mLooper->setName("MediaCodec_looper"); 71 72 mLooper->start( 73 false, // runOnCallingThread 74 false, // canCallJava 75 PRIORITY_DEFAULT); 76 77 if (nameIsType) { 78 mCodec = MediaCodec::CreateByType(mLooper, name, encoder); 79 } else { 80 mCodec = MediaCodec::CreateByComponentName(mLooper, name); 81 } 82} 83 84status_t JMediaCodec::initCheck() const { 85 return mCodec != NULL ? OK : NO_INIT; 86} 87 88JMediaCodec::~JMediaCodec() { 89 mCodec->release(); 90 91 JNIEnv *env = AndroidRuntime::getJNIEnv(); 92 93 env->DeleteWeakGlobalRef(mObject); 94 mObject = NULL; 95 env->DeleteGlobalRef(mClass); 96 mClass = NULL; 97} 98 99status_t JMediaCodec::configure( 100 const sp<AMessage> &format, 101 const sp<ISurfaceTexture> &surfaceTexture, 102 const sp<ICrypto> &crypto, 103 int flags) { 104 sp<SurfaceTextureClient> client; 105 if (surfaceTexture != NULL) { 106 client = new SurfaceTextureClient(surfaceTexture); 107 } 108 return mCodec->configure(format, client, crypto, flags); 109} 110 111status_t JMediaCodec::start() { 112 return mCodec->start(); 113} 114 115status_t JMediaCodec::stop() { 116 return mCodec->stop(); 117} 118 119status_t JMediaCodec::flush() { 120 return mCodec->flush(); 121} 122 123status_t JMediaCodec::queueInputBuffer( 124 size_t index, 125 size_t offset, size_t size, int64_t timeUs, uint32_t flags) { 126 return mCodec->queueInputBuffer(index, offset, size, timeUs, flags); 127} 128 129status_t JMediaCodec::queueSecureInputBuffer( 130 size_t index, 131 size_t offset, 132 const CryptoPlugin::SubSample *subSamples, 133 size_t numSubSamples, 134 const uint8_t key[16], 135 const uint8_t iv[16], 136 CryptoPlugin::Mode mode, 137 int64_t presentationTimeUs, 138 uint32_t flags) { 139 return mCodec->queueSecureInputBuffer( 140 index, offset, subSamples, numSubSamples, key, iv, mode, 141 presentationTimeUs, flags); 142} 143 144status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { 145 return mCodec->dequeueInputBuffer(index, timeoutUs); 146} 147 148status_t JMediaCodec::dequeueOutputBuffer( 149 JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) { 150 size_t size, offset; 151 int64_t timeUs; 152 uint32_t flags; 153 status_t err; 154 if ((err = mCodec->dequeueOutputBuffer( 155 index, &offset, &size, &timeUs, &flags, timeoutUs)) != OK) { 156 return err; 157 } 158 159 jclass clazz = env->FindClass("android/media/MediaCodec$BufferInfo"); 160 161 jmethodID method = env->GetMethodID(clazz, "set", "(IIJI)V"); 162 env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags); 163 164 return OK; 165} 166 167status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) { 168 return render 169 ? mCodec->renderOutputBufferAndRelease(index) 170 : mCodec->releaseOutputBuffer(index); 171} 172 173status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const { 174 sp<AMessage> msg; 175 status_t err; 176 if ((err = mCodec->getOutputFormat(&msg)) != OK) { 177 return err; 178 } 179 180 return ConvertMessageToMap(env, msg, format); 181} 182 183status_t JMediaCodec::getBuffers( 184 JNIEnv *env, bool input, jobjectArray *bufArray) const { 185 Vector<sp<ABuffer> > buffers; 186 187 status_t err = 188 input 189 ? mCodec->getInputBuffers(&buffers) 190 : mCodec->getOutputBuffers(&buffers); 191 192 if (err != OK) { 193 return err; 194 } 195 196 jclass byteBufferClass = env->FindClass("java/nio/ByteBuffer"); 197 198 *bufArray = (jobjectArray)env->NewObjectArray( 199 buffers.size(), byteBufferClass, NULL); 200 201 for (size_t i = 0; i < buffers.size(); ++i) { 202 const sp<ABuffer> &buffer = buffers.itemAt(i); 203 204 jobject byteBuffer = 205 env->NewDirectByteBuffer( 206 buffer->base(), 207 buffer->capacity()); 208 209 env->SetObjectArrayElement( 210 *bufArray, i, byteBuffer); 211 212 env->DeleteLocalRef(byteBuffer); 213 byteBuffer = NULL; 214 } 215 216 return OK; 217} 218 219} // namespace android 220 221//////////////////////////////////////////////////////////////////////////////// 222 223using namespace android; 224 225static sp<JMediaCodec> setMediaCodec( 226 JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) { 227 sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context); 228 if (codec != NULL) { 229 codec->incStrong(thiz); 230 } 231 if (old != NULL) { 232 old->decStrong(thiz); 233 } 234 env->SetIntField(thiz, gFields.context, (int)codec.get()); 235 236 return old; 237} 238 239static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) { 240 return (JMediaCodec *)env->GetIntField(thiz, gFields.context); 241} 242 243static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) { 244 setMediaCodec(env, thiz, NULL); 245} 246 247static jint throwExceptionAsNecessary(JNIEnv *env, status_t err) { 248 switch (err) { 249 case OK: 250 return 0; 251 252 case -EAGAIN: 253 return DEQUEUE_INFO_TRY_AGAIN_LATER; 254 255 case INFO_FORMAT_CHANGED: 256 return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED; 257 258 case INFO_OUTPUT_BUFFERS_CHANGED: 259 return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED; 260 261 default: 262 { 263 jniThrowException(env, "java/lang/IllegalStateException", NULL); 264 break; 265 } 266 } 267 268 return 0; 269} 270 271static void android_media_MediaCodec_native_configure( 272 JNIEnv *env, 273 jobject thiz, 274 jobjectArray keys, jobjectArray values, 275 jobject jsurface, 276 jobject jcrypto, 277 jint flags) { 278 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 279 280 if (codec == NULL) { 281 jniThrowException(env, "java/lang/IllegalStateException", NULL); 282 return; 283 } 284 285 sp<AMessage> format; 286 status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format); 287 288 if (err != OK) { 289 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 290 return; 291 } 292 293 sp<ISurfaceTexture> surfaceTexture; 294 if (jsurface != NULL) { 295 sp<Surface> surface(Surface_getSurface(env, jsurface)); 296 if (surface != NULL) { 297 surfaceTexture = surface->getSurfaceTexture(); 298 } else { 299 jniThrowException( 300 env, 301 "java/lang/IllegalArgumentException", 302 "The surface has been released"); 303 return; 304 } 305 } 306 307 sp<ICrypto> crypto; 308 if (jcrypto != NULL) { 309 crypto = JCrypto::GetCrypto(env, jcrypto); 310 } 311 312 err = codec->configure(format, surfaceTexture, crypto, flags); 313 314 throwExceptionAsNecessary(env, err); 315} 316 317static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) { 318 ALOGV("android_media_MediaCodec_start"); 319 320 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 321 322 if (codec == NULL) { 323 jniThrowException(env, "java/lang/IllegalStateException", NULL); 324 return; 325 } 326 327 status_t err = codec->start(); 328 329 throwExceptionAsNecessary(env, err); 330} 331 332static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) { 333 ALOGV("android_media_MediaCodec_stop"); 334 335 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 336 337 if (codec == NULL) { 338 jniThrowException(env, "java/lang/IllegalStateException", NULL); 339 return; 340 } 341 342 status_t err = codec->stop(); 343 344 throwExceptionAsNecessary(env, err); 345} 346 347static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) { 348 ALOGV("android_media_MediaCodec_flush"); 349 350 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 351 352 if (codec == NULL) { 353 jniThrowException(env, "java/lang/IllegalStateException", NULL); 354 return; 355 } 356 357 status_t err = codec->flush(); 358 359 throwExceptionAsNecessary(env, err); 360} 361 362static void android_media_MediaCodec_queueInputBuffer( 363 JNIEnv *env, 364 jobject thiz, 365 jint index, 366 jint offset, 367 jint size, 368 jlong timestampUs, 369 jint flags) { 370 ALOGV("android_media_MediaCodec_queueInputBuffer"); 371 372 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 373 374 if (codec == NULL) { 375 jniThrowException(env, "java/lang/IllegalStateException", NULL); 376 return; 377 } 378 379 status_t err = codec->queueInputBuffer( 380 index, offset, size, timestampUs, flags); 381 382 throwExceptionAsNecessary(env, err); 383} 384 385static void android_media_MediaCodec_queueSecureInputBuffer( 386 JNIEnv *env, 387 jobject thiz, 388 jint index, 389 jint offset, 390 jintArray numBytesOfClearDataObj, 391 jintArray numBytesOfEncryptedDataObj, 392 jint numSubSamples, 393 jbyteArray keyObj, 394 jbyteArray ivObj, 395 jint mode, 396 jlong timestampUs, 397 jint flags) { 398 ALOGV("android_media_MediaCodec_queueSecureInputBuffer"); 399 400 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 401 402 if (codec == NULL) { 403 jniThrowException(env, "java/lang/IllegalStateException", NULL); 404 return; 405 } 406 407 status_t err = OK; 408 409 CryptoPlugin::SubSample *subSamples = NULL; 410 jbyte *key = NULL; 411 jbyte *iv = NULL; 412 413 if (numSubSamples <= 0) { 414 err = -EINVAL; 415 } else if (numBytesOfClearDataObj == NULL 416 && numBytesOfEncryptedDataObj == NULL) { 417 err = -EINVAL; 418 } else if (numBytesOfEncryptedDataObj != NULL 419 && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) { 420 err = -ERANGE; 421 } else if (numBytesOfClearDataObj != NULL 422 && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) { 423 err = -ERANGE; 424 } else { 425 jboolean isCopy; 426 427 jint *numBytesOfClearData = 428 (numBytesOfClearDataObj == NULL) 429 ? NULL 430 : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy); 431 432 jint *numBytesOfEncryptedData = 433 (numBytesOfEncryptedDataObj == NULL) 434 ? NULL 435 : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy); 436 437 subSamples = new CryptoPlugin::SubSample[numSubSamples]; 438 439 for (jint i = 0; i < numSubSamples; ++i) { 440 subSamples[i].mNumBytesOfClearData = 441 (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i]; 442 443 subSamples[i].mNumBytesOfEncryptedData = 444 (numBytesOfEncryptedData == NULL) 445 ? 0 : numBytesOfEncryptedData[i]; 446 } 447 448 if (numBytesOfEncryptedData != NULL) { 449 env->ReleaseIntArrayElements( 450 numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0); 451 numBytesOfEncryptedData = NULL; 452 } 453 454 if (numBytesOfClearData != NULL) { 455 env->ReleaseIntArrayElements( 456 numBytesOfClearDataObj, numBytesOfClearData, 0); 457 numBytesOfClearData = NULL; 458 } 459 } 460 461 if (err == OK && keyObj != NULL) { 462 if (env->GetArrayLength(keyObj) != 16) { 463 err = -EINVAL; 464 } else { 465 jboolean isCopy; 466 key = env->GetByteArrayElements(keyObj, &isCopy); 467 } 468 } 469 470 if (err == OK && ivObj != NULL) { 471 if (env->GetArrayLength(ivObj) != 16) { 472 err = -EINVAL; 473 } else { 474 jboolean isCopy; 475 iv = env->GetByteArrayElements(ivObj, &isCopy); 476 } 477 } 478 479 if (err == OK) { 480 err = codec->queueSecureInputBuffer( 481 index, offset, 482 subSamples, numSubSamples, 483 (const uint8_t *)key, (const uint8_t *)iv, 484 (CryptoPlugin::Mode)mode, 485 timestampUs, flags); 486 } 487 488 if (iv != NULL) { 489 env->ReleaseByteArrayElements(ivObj, iv, 0); 490 iv = NULL; 491 } 492 493 if (key != NULL) { 494 env->ReleaseByteArrayElements(keyObj, key, 0); 495 key = NULL; 496 } 497 498 delete[] subSamples; 499 subSamples = NULL; 500 501 throwExceptionAsNecessary(env, err); 502} 503 504static jint android_media_MediaCodec_dequeueInputBuffer( 505 JNIEnv *env, jobject thiz, jlong timeoutUs) { 506 ALOGV("android_media_MediaCodec_dequeueInputBuffer"); 507 508 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 509 510 if (codec == NULL) { 511 jniThrowException(env, "java/lang/IllegalStateException", NULL); 512 return -1; 513 } 514 515 size_t index; 516 status_t err = codec->dequeueInputBuffer(&index, timeoutUs); 517 518 if (err == OK) { 519 return index; 520 } 521 522 return throwExceptionAsNecessary(env, err); 523} 524 525static jint android_media_MediaCodec_dequeueOutputBuffer( 526 JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) { 527 ALOGV("android_media_MediaCodec_dequeueOutputBuffer"); 528 529 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 530 531 if (codec == NULL) { 532 jniThrowException(env, "java/lang/IllegalStateException", NULL); 533 return 0; 534 } 535 536 size_t index; 537 status_t err = codec->dequeueOutputBuffer( 538 env, bufferInfo, &index, timeoutUs); 539 540 if (err == OK) { 541 return index; 542 } 543 544 return throwExceptionAsNecessary(env, err); 545} 546 547static void android_media_MediaCodec_releaseOutputBuffer( 548 JNIEnv *env, jobject thiz, jint index, jboolean render) { 549 ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease"); 550 551 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 552 553 if (codec == NULL) { 554 jniThrowException(env, "java/lang/IllegalStateException", NULL); 555 return; 556 } 557 558 status_t err = codec->releaseOutputBuffer(index, render); 559 560 throwExceptionAsNecessary(env, err); 561} 562 563static jobject android_media_MediaCodec_getOutputFormat( 564 JNIEnv *env, jobject thiz) { 565 ALOGV("android_media_MediaCodec_getOutputFormat"); 566 567 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 568 569 if (codec == NULL) { 570 jniThrowException(env, "java/lang/IllegalStateException", NULL); 571 return NULL; 572 } 573 574 jobject format; 575 status_t err = codec->getOutputFormat(env, &format); 576 577 if (err == OK) { 578 return format; 579 } 580 581 throwExceptionAsNecessary(env, err); 582 583 return NULL; 584} 585 586static jobjectArray android_media_MediaCodec_getBuffers( 587 JNIEnv *env, jobject thiz, jboolean input) { 588 ALOGV("android_media_MediaCodec_getBuffers"); 589 590 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 591 592 if (codec == NULL) { 593 jniThrowException(env, "java/lang/IllegalStateException", NULL); 594 return NULL; 595 } 596 597 jobjectArray buffers; 598 status_t err = codec->getBuffers(env, input, &buffers); 599 600 if (err == OK) { 601 return buffers; 602 } 603 604 throwExceptionAsNecessary(env, err); 605 606 return NULL; 607} 608 609static void android_media_MediaCodec_native_init(JNIEnv *env) { 610 jclass clazz = env->FindClass("android/media/MediaCodec"); 611 CHECK(clazz != NULL); 612 613 gFields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 614 CHECK(gFields.context != NULL); 615} 616 617static void android_media_MediaCodec_native_setup( 618 JNIEnv *env, jobject thiz, 619 jstring name, jboolean nameIsType, jboolean encoder) { 620 if (name == NULL) { 621 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 622 return; 623 } 624 625 const char *tmp = env->GetStringUTFChars(name, NULL); 626 627 if (tmp == NULL) { 628 return; 629 } 630 631 sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder); 632 633 status_t err = codec->initCheck(); 634 635 env->ReleaseStringUTFChars(name, tmp); 636 tmp = NULL; 637 638 if (err != OK) { 639 jniThrowException( 640 env, 641 "java/io/IOException", 642 "Failed to allocate component instance"); 643 return; 644 } 645 646 setMediaCodec(env,thiz, codec); 647} 648 649static void android_media_MediaCodec_native_finalize( 650 JNIEnv *env, jobject thiz) { 651 android_media_MediaCodec_release(env, thiz); 652} 653 654static JNINativeMethod gMethods[] = { 655 { "release", "()V", (void *)android_media_MediaCodec_release }, 656 657 { "native_configure", 658 "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;" 659 "Landroid/media/Crypto;I)V", 660 (void *)android_media_MediaCodec_native_configure }, 661 662 { "start", "()V", (void *)android_media_MediaCodec_start }, 663 { "stop", "()V", (void *)android_media_MediaCodec_stop }, 664 { "flush", "()V", (void *)android_media_MediaCodec_flush }, 665 666 { "queueInputBuffer", "(IIIJI)V", 667 (void *)android_media_MediaCodec_queueInputBuffer }, 668 669 { "queueSecureInputBuffer", "(II[I[II[B[BIJI)V", 670 (void *)android_media_MediaCodec_queueSecureInputBuffer }, 671 672 { "dequeueInputBuffer", "(J)I", 673 (void *)android_media_MediaCodec_dequeueInputBuffer }, 674 675 { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I", 676 (void *)android_media_MediaCodec_dequeueOutputBuffer }, 677 678 { "releaseOutputBuffer", "(IZ)V", 679 (void *)android_media_MediaCodec_releaseOutputBuffer }, 680 681 { "getOutputFormat", "()Ljava/util/Map;", 682 (void *)android_media_MediaCodec_getOutputFormat }, 683 684 { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;", 685 (void *)android_media_MediaCodec_getBuffers }, 686 687 { "native_init", "()V", (void *)android_media_MediaCodec_native_init }, 688 689 { "native_setup", "(Ljava/lang/String;ZZ)V", 690 (void *)android_media_MediaCodec_native_setup }, 691 692 { "native_finalize", "()V", 693 (void *)android_media_MediaCodec_native_finalize }, 694}; 695 696int register_android_media_MediaCodec(JNIEnv *env) { 697 return AndroidRuntime::registerNativeMethods(env, 698 "android/media/MediaCodec", gMethods, NELEM(gMethods)); 699} 700