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