android_media_MediaCodec.cpp revision 8335f1ccccedb6655d96d9d5b697a7f0938235dd
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_Utils.h" 24#include "android_runtime/AndroidRuntime.h" 25#include "android_runtime/android_view_Surface.h" 26#include "jni.h" 27#include "JNIHelp.h" 28 29#include <gui/Surface.h> 30#include <gui/SurfaceTextureClient.h> 31 32#include <media/stagefright/MediaCodec.h> 33#include <media/stagefright/foundation/ABuffer.h> 34#include <media/stagefright/foundation/ADebug.h> 35#include <media/stagefright/foundation/ALooper.h> 36#include <media/stagefright/foundation/AMessage.h> 37#include <media/stagefright/MediaErrors.h> 38 39namespace android { 40 41// Keep these in sync with their equivalents in MediaCodec.java !!! 42enum { 43 DEQUEUE_INFO_TRY_AGAIN_LATER = -1, 44 DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED = -2, 45 DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3, 46}; 47 48struct fields_t { 49 jfieldID context; 50}; 51 52static fields_t gFields; 53 54//////////////////////////////////////////////////////////////////////////////// 55 56JMediaCodec::JMediaCodec( 57 JNIEnv *env, jobject thiz, 58 const char *name, bool nameIsType, bool encoder) 59 : mClass(NULL), 60 mObject(NULL) { 61 jclass clazz = env->GetObjectClass(thiz); 62 CHECK(clazz != NULL); 63 64 mClass = (jclass)env->NewGlobalRef(clazz); 65 mObject = env->NewWeakGlobalRef(thiz); 66 67 mLooper = new ALooper; 68 mLooper->setName("MediaCodec_looper"); 69 70 mLooper->start( 71 false, // runOnCallingThread 72 false, // canCallJava 73 PRIORITY_DEFAULT); 74 75 if (nameIsType) { 76 mCodec = MediaCodec::CreateByType(mLooper, name, encoder); 77 } else { 78 mCodec = MediaCodec::CreateByComponentName(mLooper, name); 79 } 80} 81 82status_t JMediaCodec::initCheck() const { 83 return mCodec != NULL ? OK : NO_INIT; 84} 85 86JMediaCodec::~JMediaCodec() { 87 mCodec->stop(); 88 89 JNIEnv *env = AndroidRuntime::getJNIEnv(); 90 91 env->DeleteWeakGlobalRef(mObject); 92 mObject = NULL; 93 env->DeleteGlobalRef(mClass); 94 mClass = NULL; 95} 96 97status_t JMediaCodec::configure( 98 const sp<AMessage> &format, 99 const sp<ISurfaceTexture> &surfaceTexture, 100 int flags) { 101 sp<SurfaceTextureClient> client; 102 if (surfaceTexture != NULL) { 103 client = new SurfaceTextureClient(surfaceTexture); 104 } 105 return mCodec->configure(format, client, flags); 106} 107 108status_t JMediaCodec::start() { 109 return mCodec->start(); 110} 111 112status_t JMediaCodec::stop() { 113 return mCodec->stop(); 114} 115 116status_t JMediaCodec::flush() { 117 return mCodec->flush(); 118} 119 120status_t JMediaCodec::queueInputBuffer( 121 size_t index, 122 size_t offset, size_t size, int64_t timeUs, uint32_t flags) { 123 return mCodec->queueInputBuffer(index, offset, size, timeUs, flags); 124} 125 126status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { 127 return mCodec->dequeueInputBuffer(index, timeoutUs); 128} 129 130status_t JMediaCodec::dequeueOutputBuffer( 131 JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) { 132 size_t size, offset; 133 int64_t timeUs; 134 uint32_t flags; 135 status_t err; 136 if ((err = mCodec->dequeueOutputBuffer( 137 index, &size, &offset, &timeUs, &flags, timeoutUs)) != OK) { 138 return err; 139 } 140 141 jclass clazz = env->FindClass("android/media/MediaCodec$BufferInfo"); 142 143 jmethodID method = env->GetMethodID(clazz, "set", "(IIJI)V"); 144 env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags); 145 146 return OK; 147} 148 149status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) { 150 return render 151 ? mCodec->renderOutputBufferAndRelease(index) 152 : mCodec->releaseOutputBuffer(index); 153} 154 155status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const { 156 sp<AMessage> msg; 157 status_t err; 158 if ((err = mCodec->getOutputFormat(&msg)) != OK) { 159 return err; 160 } 161 162 return ConvertMessageToMap(env, msg, format); 163} 164 165status_t JMediaCodec::getBuffers( 166 JNIEnv *env, bool input, jobjectArray *bufArray) const { 167 Vector<sp<ABuffer> > buffers; 168 169 status_t err = 170 input 171 ? mCodec->getInputBuffers(&buffers) 172 : mCodec->getOutputBuffers(&buffers); 173 174 if (err != OK) { 175 return err; 176 } 177 178 jclass byteBufferClass = env->FindClass("java/nio/ByteBuffer"); 179 180 *bufArray = (jobjectArray)env->NewObjectArray( 181 buffers.size(), byteBufferClass, NULL); 182 183 for (size_t i = 0; i < buffers.size(); ++i) { 184 const sp<ABuffer> &buffer = buffers.itemAt(i); 185 186 jobject byteBuffer = 187 env->NewDirectByteBuffer( 188 buffer->base(), 189 buffer->capacity()); 190 191 env->SetObjectArrayElement( 192 *bufArray, i, byteBuffer); 193 194 env->DeleteLocalRef(byteBuffer); 195 byteBuffer = NULL; 196 } 197 198 return OK; 199} 200 201} // namespace android 202 203//////////////////////////////////////////////////////////////////////////////// 204 205using namespace android; 206 207static sp<JMediaCodec> setMediaCodec( 208 JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) { 209 sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context); 210 if (codec != NULL) { 211 codec->incStrong(thiz); 212 } 213 if (old != NULL) { 214 old->decStrong(thiz); 215 } 216 env->SetIntField(thiz, gFields.context, (int)codec.get()); 217 218 return old; 219} 220 221static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) { 222 return (JMediaCodec *)env->GetIntField(thiz, gFields.context); 223} 224 225static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) { 226 setMediaCodec(env, thiz, NULL); 227} 228 229static jint throwExceptionAsNecessary(JNIEnv *env, status_t err) { 230 switch (err) { 231 case OK: 232 return 0; 233 234 case -EAGAIN: 235 return DEQUEUE_INFO_TRY_AGAIN_LATER; 236 237 case INFO_FORMAT_CHANGED: 238 return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED; 239 240 case INFO_OUTPUT_BUFFERS_CHANGED: 241 return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED; 242 243 default: 244 { 245 jniThrowException(env, "java/lang/IllegalStateException", NULL); 246 break; 247 } 248 } 249 250 return 0; 251} 252 253static void android_media_MediaCodec_native_configure( 254 JNIEnv *env, 255 jobject thiz, 256 jobjectArray keys, jobjectArray values, 257 jobject jsurface, 258 jint flags) { 259 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 260 261 if (codec == NULL) { 262 jniThrowException(env, "java/lang/IllegalStateException", NULL); 263 return; 264 } 265 266 sp<AMessage> format; 267 status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format); 268 269 if (err != OK) { 270 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 271 return; 272 } 273 274 sp<ISurfaceTexture> surfaceTexture; 275 if (jsurface != NULL) { 276 sp<Surface> surface(Surface_getSurface(env, jsurface)); 277 if (surface != NULL) { 278 surfaceTexture = surface->getSurfaceTexture(); 279 } else { 280 jniThrowException( 281 env, 282 "java/lang/IllegalArgumentException", 283 "The surface has been released"); 284 return; 285 } 286 } 287 288 err = codec->configure(format, surfaceTexture, flags); 289 290 throwExceptionAsNecessary(env, err); 291} 292 293static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) { 294 ALOGV("android_media_MediaCodec_start"); 295 296 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 297 298 if (codec == NULL) { 299 jniThrowException(env, "java/lang/IllegalStateException", NULL); 300 return; 301 } 302 303 status_t err = codec->start(); 304 305 throwExceptionAsNecessary(env, err); 306} 307 308static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) { 309 ALOGV("android_media_MediaCodec_stop"); 310 311 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 312 313 if (codec == NULL) { 314 jniThrowException(env, "java/lang/IllegalStateException", NULL); 315 return; 316 } 317 318 status_t err = codec->stop(); 319 320 throwExceptionAsNecessary(env, err); 321} 322 323static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) { 324 ALOGV("android_media_MediaCodec_flush"); 325 326 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 327 328 if (codec == NULL) { 329 jniThrowException(env, "java/lang/IllegalStateException", NULL); 330 return; 331 } 332 333 status_t err = codec->flush(); 334 335 throwExceptionAsNecessary(env, err); 336} 337 338static void android_media_MediaCodec_queueInputBuffer( 339 JNIEnv *env, 340 jobject thiz, 341 jint index, 342 jint offset, 343 jint size, 344 jlong timestampUs, 345 jint flags) { 346 ALOGV("android_media_MediaCodec_queueInputBuffer"); 347 348 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 349 350 if (codec == NULL) { 351 jniThrowException(env, "java/lang/IllegalStateException", NULL); 352 return; 353 } 354 355 status_t err = codec->queueInputBuffer( 356 index, offset, size, timestampUs, flags); 357 358 throwExceptionAsNecessary(env, err); 359} 360 361static jint android_media_MediaCodec_dequeueInputBuffer( 362 JNIEnv *env, jobject thiz, jlong timeoutUs) { 363 ALOGV("android_media_MediaCodec_dequeueInputBuffer"); 364 365 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 366 367 if (codec == NULL) { 368 jniThrowException(env, "java/lang/IllegalStateException", NULL); 369 return -1; 370 } 371 372 size_t index; 373 status_t err = codec->dequeueInputBuffer(&index, timeoutUs); 374 375 if (err == OK) { 376 return index; 377 } 378 379 return throwExceptionAsNecessary(env, err); 380} 381 382static jint android_media_MediaCodec_dequeueOutputBuffer( 383 JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) { 384 ALOGV("android_media_MediaCodec_dequeueOutputBuffer"); 385 386 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 387 388 if (codec == NULL) { 389 jniThrowException(env, "java/lang/IllegalStateException", NULL); 390 return NULL; 391 } 392 393 size_t index; 394 status_t err = codec->dequeueOutputBuffer( 395 env, bufferInfo, &index, timeoutUs); 396 397 if (err == OK) { 398 return index; 399 } 400 401 return throwExceptionAsNecessary(env, err); 402} 403 404static void android_media_MediaCodec_releaseOutputBuffer( 405 JNIEnv *env, jobject thiz, jint index, jboolean render) { 406 ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease"); 407 408 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 409 410 if (codec == NULL) { 411 jniThrowException(env, "java/lang/IllegalStateException", NULL); 412 return; 413 } 414 415 status_t err = codec->releaseOutputBuffer(index, render); 416 417 throwExceptionAsNecessary(env, err); 418} 419 420static jobject android_media_MediaCodec_getOutputFormat( 421 JNIEnv *env, jobject thiz) { 422 ALOGV("android_media_MediaCodec_getOutputFormat"); 423 424 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 425 426 if (codec == NULL) { 427 jniThrowException(env, "java/lang/IllegalStateException", NULL); 428 return NULL; 429 } 430 431 jobject format; 432 status_t err = codec->getOutputFormat(env, &format); 433 434 if (err == OK) { 435 return format; 436 } 437 438 throwExceptionAsNecessary(env, err); 439 440 return NULL; 441} 442 443static jobjectArray android_media_MediaCodec_getBuffers( 444 JNIEnv *env, jobject thiz, jboolean input) { 445 ALOGV("android_media_MediaCodec_getBuffers"); 446 447 sp<JMediaCodec> codec = getMediaCodec(env, thiz); 448 449 if (codec == NULL) { 450 jniThrowException(env, "java/lang/IllegalStateException", NULL); 451 return NULL; 452 } 453 454 jobjectArray buffers; 455 status_t err = codec->getBuffers(env, input, &buffers); 456 457 if (err == OK) { 458 return buffers; 459 } 460 461 throwExceptionAsNecessary(env, err); 462 463 return NULL; 464} 465 466static void android_media_MediaCodec_native_init(JNIEnv *env) { 467 jclass clazz = env->FindClass("android/media/MediaCodec"); 468 CHECK(clazz != NULL); 469 470 gFields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 471 CHECK(gFields.context != NULL); 472} 473 474static void android_media_MediaCodec_native_setup( 475 JNIEnv *env, jobject thiz, 476 jstring name, jboolean nameIsType, jboolean encoder) { 477 if (name == NULL) { 478 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 479 return; 480 } 481 482 const char *tmp = env->GetStringUTFChars(name, NULL); 483 484 if (tmp == NULL) { 485 return; 486 } 487 488 sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder); 489 490 status_t err = codec->initCheck(); 491 492 env->ReleaseStringUTFChars(name, tmp); 493 tmp = NULL; 494 495 if (err != OK) { 496 jniThrowException( 497 env, 498 "java/io/IOException", 499 "Failed to allocate component instance"); 500 return; 501 } 502 503 setMediaCodec(env,thiz, codec); 504} 505 506static void android_media_MediaCodec_native_finalize( 507 JNIEnv *env, jobject thiz) { 508 android_media_MediaCodec_release(env, thiz); 509} 510 511static JNINativeMethod gMethods[] = { 512 { "release", "()V", (void *)android_media_MediaCodec_release }, 513 514 { "native_configure", 515 "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;I)V", 516 (void *)android_media_MediaCodec_native_configure }, 517 518 { "start", "()V", (void *)android_media_MediaCodec_start }, 519 { "stop", "()V", (void *)android_media_MediaCodec_stop }, 520 { "flush", "()V", (void *)android_media_MediaCodec_flush }, 521 522 { "queueInputBuffer", "(IIIJI)V", 523 (void *)android_media_MediaCodec_queueInputBuffer }, 524 525 { "dequeueInputBuffer", "(J)I", 526 (void *)android_media_MediaCodec_dequeueInputBuffer }, 527 528 { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I", 529 (void *)android_media_MediaCodec_dequeueOutputBuffer }, 530 531 { "releaseOutputBuffer", "(IZ)V", 532 (void *)android_media_MediaCodec_releaseOutputBuffer }, 533 534 { "getOutputFormat", "()Ljava/util/Map;", 535 (void *)android_media_MediaCodec_getOutputFormat }, 536 537 { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;", 538 (void *)android_media_MediaCodec_getBuffers }, 539 540 { "native_init", "()V", (void *)android_media_MediaCodec_native_init }, 541 542 { "native_setup", "(Ljava/lang/String;ZZ)V", 543 (void *)android_media_MediaCodec_native_setup }, 544 545 { "native_finalize", "()V", 546 (void *)android_media_MediaCodec_native_finalize }, 547}; 548 549int register_android_media_MediaCodec(JNIEnv *env) { 550 return AndroidRuntime::registerNativeMethods(env, 551 "android/media/MediaCodec", gMethods, NELEM(gMethods)); 552} 553