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