android_media_MediaCodec.cpp revision 8240d9239d9aabed75c49f9d4d69fd8a5fe4c899
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::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { 130 return mCodec->dequeueInputBuffer(index, timeoutUs); 131} 132 133status_t JMediaCodec::dequeueOutputBuffer( 134 JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) { 135 size_t size, offset; 136 int64_t timeUs; 137 uint32_t flags; 138 status_t err; 139 if ((err = mCodec->dequeueOutputBuffer( 140 index, &offset, &size, &timeUs, &flags, timeoutUs)) != OK) { 141 return err; 142 } 143 144 jclass clazz = env->FindClass("android/media/MediaCodec$BufferInfo"); 145 146 jmethodID method = env->GetMethodID(clazz, "set", "(IIJI)V"); 147 env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags); 148 149 return OK; 150} 151 152status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) { 153 return render 154 ? mCodec->renderOutputBufferAndRelease(index) 155 : mCodec->releaseOutputBuffer(index); 156} 157 158status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const { 159 sp<AMessage> msg; 160 status_t err; 161 if ((err = mCodec->getOutputFormat(&msg)) != OK) { 162 return err; 163 } 164 165 return ConvertMessageToMap(env, msg, format); 166} 167 168status_t JMediaCodec::getBuffers( 169 JNIEnv *env, bool input, jobjectArray *bufArray) const { 170 Vector<sp<ABuffer> > buffers; 171 172 status_t err = 173 input 174 ? mCodec->getInputBuffers(&buffers) 175 : mCodec->getOutputBuffers(&buffers); 176 177 if (err != OK) { 178 return err; 179 } 180 181 jclass byteBufferClass = env->FindClass("java/nio/ByteBuffer"); 182 183 *bufArray = (jobjectArray)env->NewObjectArray( 184 buffers.size(), byteBufferClass, NULL); 185 186 for (size_t i = 0; i < buffers.size(); ++i) { 187 const sp<ABuffer> &buffer = buffers.itemAt(i); 188 189 jobject byteBuffer = 190 env->NewDirectByteBuffer( 191 buffer->base(), 192 buffer->capacity()); 193 194 env->SetObjectArrayElement( 195 *bufArray, i, byteBuffer); 196 197 env->DeleteLocalRef(byteBuffer); 198 byteBuffer = NULL; 199 } 200 201 return OK; 202} 203 204} // namespace android 205 206//////////////////////////////////////////////////////////////////////////////// 207 208using namespace android; 209 210static sp<JMediaCodec> setMediaCodec( 211 JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) { 212 sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context); 213 if (codec != NULL) { 214 codec->incStrong(thiz); 215 } 216 if (old != NULL) { 217 old->decStrong(thiz); 218 } 219 env->SetIntField(thiz, gFields.context, (int)codec.get()); 220 221 return old; 222} 223 224static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) { 225 return (JMediaCodec *)env->GetIntField(thiz, gFields.context); 226} 227 228static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) { 229 setMediaCodec(env, thiz, NULL); 230} 231 232static jint throwExceptionAsNecessary(JNIEnv *env, status_t err) { 233 switch (err) { 234 case OK: 235 return 0; 236 237 case -EAGAIN: 238 return DEQUEUE_INFO_TRY_AGAIN_LATER; 239 240 case INFO_FORMAT_CHANGED: 241 return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED; 242 243 case INFO_OUTPUT_BUFFERS_CHANGED: 244 return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED; 245 246 default: 247 { 248 jniThrowException(env, "java/lang/IllegalStateException", NULL); 249 break; 250 } 251 } 252 253 return 0; 254} 255 256static void android_media_MediaCodec_native_configure( 257 JNIEnv *env, 258 jobject thiz, 259 jobjectArray keys, jobjectArray values, 260 jobject jsurface, 261 jobject jcrypto, 262 jint flags) { 263 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 264 265 if (codec == NULL) { 266 jniThrowException(env, "java/lang/IllegalStateException", NULL); 267 return; 268 } 269 270 sp<AMessage> format; 271 status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format); 272 273 if (err != OK) { 274 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 275 return; 276 } 277 278 sp<ISurfaceTexture> surfaceTexture; 279 if (jsurface != NULL) { 280 sp<Surface> surface(Surface_getSurface(env, jsurface)); 281 if (surface != NULL) { 282 surfaceTexture = surface->getSurfaceTexture(); 283 } else { 284 jniThrowException( 285 env, 286 "java/lang/IllegalArgumentException", 287 "The surface has been released"); 288 return; 289 } 290 } 291 292 sp<ICrypto> crypto; 293 if (jcrypto != NULL) { 294 crypto = JCrypto::GetCrypto(env, jcrypto); 295 } 296 297 err = codec->configure(format, surfaceTexture, crypto, flags); 298 299 throwExceptionAsNecessary(env, err); 300} 301 302static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) { 303 ALOGV("android_media_MediaCodec_start"); 304 305 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 306 307 if (codec == NULL) { 308 jniThrowException(env, "java/lang/IllegalStateException", NULL); 309 return; 310 } 311 312 status_t err = codec->start(); 313 314 throwExceptionAsNecessary(env, err); 315} 316 317static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) { 318 ALOGV("android_media_MediaCodec_stop"); 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->stop(); 328 329 throwExceptionAsNecessary(env, err); 330} 331 332static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) { 333 ALOGV("android_media_MediaCodec_flush"); 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->flush(); 343 344 throwExceptionAsNecessary(env, err); 345} 346 347static void android_media_MediaCodec_queueInputBuffer( 348 JNIEnv *env, 349 jobject thiz, 350 jint index, 351 jint offset, 352 jint size, 353 jlong timestampUs, 354 jint flags) { 355 ALOGV("android_media_MediaCodec_queueInputBuffer"); 356 357 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 358 359 if (codec == NULL) { 360 jniThrowException(env, "java/lang/IllegalStateException", NULL); 361 return; 362 } 363 364 status_t err = codec->queueInputBuffer( 365 index, offset, size, timestampUs, flags); 366 367 throwExceptionAsNecessary(env, err); 368} 369 370static jint android_media_MediaCodec_dequeueInputBuffer( 371 JNIEnv *env, jobject thiz, jlong timeoutUs) { 372 ALOGV("android_media_MediaCodec_dequeueInputBuffer"); 373 374 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 375 376 if (codec == NULL) { 377 jniThrowException(env, "java/lang/IllegalStateException", NULL); 378 return -1; 379 } 380 381 size_t index; 382 status_t err = codec->dequeueInputBuffer(&index, timeoutUs); 383 384 if (err == OK) { 385 return index; 386 } 387 388 return throwExceptionAsNecessary(env, err); 389} 390 391static jint android_media_MediaCodec_dequeueOutputBuffer( 392 JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) { 393 ALOGV("android_media_MediaCodec_dequeueOutputBuffer"); 394 395 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 396 397 if (codec == NULL) { 398 jniThrowException(env, "java/lang/IllegalStateException", NULL); 399 return 0; 400 } 401 402 size_t index; 403 status_t err = codec->dequeueOutputBuffer( 404 env, bufferInfo, &index, timeoutUs); 405 406 if (err == OK) { 407 return index; 408 } 409 410 return throwExceptionAsNecessary(env, err); 411} 412 413static void android_media_MediaCodec_releaseOutputBuffer( 414 JNIEnv *env, jobject thiz, jint index, jboolean render) { 415 ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease"); 416 417 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 418 419 if (codec == NULL) { 420 jniThrowException(env, "java/lang/IllegalStateException", NULL); 421 return; 422 } 423 424 status_t err = codec->releaseOutputBuffer(index, render); 425 426 throwExceptionAsNecessary(env, err); 427} 428 429static jobject android_media_MediaCodec_getOutputFormat( 430 JNIEnv *env, jobject thiz) { 431 ALOGV("android_media_MediaCodec_getOutputFormat"); 432 433 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 434 435 if (codec == NULL) { 436 jniThrowException(env, "java/lang/IllegalStateException", NULL); 437 return NULL; 438 } 439 440 jobject format; 441 status_t err = codec->getOutputFormat(env, &format); 442 443 if (err == OK) { 444 return format; 445 } 446 447 throwExceptionAsNecessary(env, err); 448 449 return NULL; 450} 451 452static jobjectArray android_media_MediaCodec_getBuffers( 453 JNIEnv *env, jobject thiz, jboolean input) { 454 ALOGV("android_media_MediaCodec_getBuffers"); 455 456 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 457 458 if (codec == NULL) { 459 jniThrowException(env, "java/lang/IllegalStateException", NULL); 460 return NULL; 461 } 462 463 jobjectArray buffers; 464 status_t err = codec->getBuffers(env, input, &buffers); 465 466 if (err == OK) { 467 return buffers; 468 } 469 470 throwExceptionAsNecessary(env, err); 471 472 return NULL; 473} 474 475static void android_media_MediaCodec_native_init(JNIEnv *env) { 476 jclass clazz = env->FindClass("android/media/MediaCodec"); 477 CHECK(clazz != NULL); 478 479 gFields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 480 CHECK(gFields.context != NULL); 481} 482 483static void android_media_MediaCodec_native_setup( 484 JNIEnv *env, jobject thiz, 485 jstring name, jboolean nameIsType, jboolean encoder) { 486 if (name == NULL) { 487 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 488 return; 489 } 490 491 const char *tmp = env->GetStringUTFChars(name, NULL); 492 493 if (tmp == NULL) { 494 return; 495 } 496 497 sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder); 498 499 status_t err = codec->initCheck(); 500 501 env->ReleaseStringUTFChars(name, tmp); 502 tmp = NULL; 503 504 if (err != OK) { 505 jniThrowException( 506 env, 507 "java/io/IOException", 508 "Failed to allocate component instance"); 509 return; 510 } 511 512 setMediaCodec(env,thiz, codec); 513} 514 515static void android_media_MediaCodec_native_finalize( 516 JNIEnv *env, jobject thiz) { 517 android_media_MediaCodec_release(env, thiz); 518} 519 520static JNINativeMethod gMethods[] = { 521 { "release", "()V", (void *)android_media_MediaCodec_release }, 522 523 { "native_configure", 524 "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;" 525 "Landroid/media/Crypto;I)V", 526 (void *)android_media_MediaCodec_native_configure }, 527 528 { "start", "()V", (void *)android_media_MediaCodec_start }, 529 { "stop", "()V", (void *)android_media_MediaCodec_stop }, 530 { "flush", "()V", (void *)android_media_MediaCodec_flush }, 531 532 { "queueInputBuffer", "(IIIJI)V", 533 (void *)android_media_MediaCodec_queueInputBuffer }, 534 535 { "dequeueInputBuffer", "(J)I", 536 (void *)android_media_MediaCodec_dequeueInputBuffer }, 537 538 { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I", 539 (void *)android_media_MediaCodec_dequeueOutputBuffer }, 540 541 { "releaseOutputBuffer", "(IZ)V", 542 (void *)android_media_MediaCodec_releaseOutputBuffer }, 543 544 { "getOutputFormat", "()Ljava/util/Map;", 545 (void *)android_media_MediaCodec_getOutputFormat }, 546 547 { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;", 548 (void *)android_media_MediaCodec_getBuffers }, 549 550 { "native_init", "()V", (void *)android_media_MediaCodec_native_init }, 551 552 { "native_setup", "(Ljava/lang/String;ZZ)V", 553 (void *)android_media_MediaCodec_native_setup }, 554 555 { "native_finalize", "()V", 556 (void *)android_media_MediaCodec_native_finalize }, 557}; 558 559int register_android_media_MediaCodec(JNIEnv *env) { 560 return AndroidRuntime::registerNativeMethods(env, 561 "android/media/MediaCodec", gMethods, NELEM(gMethods)); 562} 563