android_media_MediaCodec.cpp revision c52b980277f08aee7981b1fdbca7a89464cf66d9
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->release();
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, &offset, &size, &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