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