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