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