android_media_MediaCodec.cpp revision 07ea426e3ae8915ca6bf67135f523f42cd920af0
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_MediaCrypto.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::queueSecureInputBuffer(
130        size_t index,
131        size_t offset,
132        const CryptoPlugin::SubSample *subSamples,
133        size_t numSubSamples,
134        const uint8_t key[16],
135        const uint8_t iv[16],
136        CryptoPlugin::Mode mode,
137        int64_t presentationTimeUs,
138        uint32_t flags) {
139    return mCodec->queueSecureInputBuffer(
140            index, offset, subSamples, numSubSamples, key, iv, mode,
141            presentationTimeUs, flags);
142}
143
144status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
145    return mCodec->dequeueInputBuffer(index, timeoutUs);
146}
147
148status_t JMediaCodec::dequeueOutputBuffer(
149        JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) {
150    size_t size, offset;
151    int64_t timeUs;
152    uint32_t flags;
153    status_t err;
154    if ((err = mCodec->dequeueOutputBuffer(
155                    index, &offset, &size, &timeUs, &flags, timeoutUs)) != OK) {
156        return err;
157    }
158
159    jclass clazz = env->FindClass("android/media/MediaCodec$BufferInfo");
160
161    jmethodID method = env->GetMethodID(clazz, "set", "(IIJI)V");
162    env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags);
163
164    return OK;
165}
166
167status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) {
168    return render
169        ? mCodec->renderOutputBufferAndRelease(index)
170        : mCodec->releaseOutputBuffer(index);
171}
172
173status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const {
174    sp<AMessage> msg;
175    status_t err;
176    if ((err = mCodec->getOutputFormat(&msg)) != OK) {
177        return err;
178    }
179
180    return ConvertMessageToMap(env, msg, format);
181}
182
183status_t JMediaCodec::getBuffers(
184        JNIEnv *env, bool input, jobjectArray *bufArray) const {
185    Vector<sp<ABuffer> > buffers;
186
187    status_t err =
188        input
189            ? mCodec->getInputBuffers(&buffers)
190            : mCodec->getOutputBuffers(&buffers);
191
192    if (err != OK) {
193        return err;
194    }
195
196    jclass byteBufferClass = env->FindClass("java/nio/ByteBuffer");
197
198    *bufArray = (jobjectArray)env->NewObjectArray(
199            buffers.size(), byteBufferClass, NULL);
200
201    for (size_t i = 0; i < buffers.size(); ++i) {
202        const sp<ABuffer> &buffer = buffers.itemAt(i);
203
204        jobject byteBuffer =
205            env->NewDirectByteBuffer(
206                buffer->base(),
207                buffer->capacity());
208
209        env->SetObjectArrayElement(
210                *bufArray, i, byteBuffer);
211
212        env->DeleteLocalRef(byteBuffer);
213        byteBuffer = NULL;
214    }
215
216    return OK;
217}
218
219}  // namespace android
220
221////////////////////////////////////////////////////////////////////////////////
222
223using namespace android;
224
225static sp<JMediaCodec> setMediaCodec(
226        JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
227    sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context);
228    if (codec != NULL) {
229        codec->incStrong(thiz);
230    }
231    if (old != NULL) {
232        old->decStrong(thiz);
233    }
234    env->SetIntField(thiz, gFields.context, (int)codec.get());
235
236    return old;
237}
238
239static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
240    return (JMediaCodec *)env->GetIntField(thiz, gFields.context);
241}
242
243static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
244    setMediaCodec(env, thiz, NULL);
245}
246
247static jint throwExceptionAsNecessary(JNIEnv *env, status_t err) {
248    switch (err) {
249        case OK:
250            return 0;
251
252        case -EAGAIN:
253            return DEQUEUE_INFO_TRY_AGAIN_LATER;
254
255        case INFO_FORMAT_CHANGED:
256            return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
257
258        case INFO_OUTPUT_BUFFERS_CHANGED:
259            return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
260
261        default:
262        {
263            jniThrowException(env, "java/lang/IllegalStateException", NULL);
264            break;
265        }
266    }
267
268    return 0;
269}
270
271static void android_media_MediaCodec_native_configure(
272        JNIEnv *env,
273        jobject thiz,
274        jobjectArray keys, jobjectArray values,
275        jobject jsurface,
276        jobject jcrypto,
277        jint flags) {
278    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
279
280    if (codec == NULL) {
281        jniThrowException(env, "java/lang/IllegalStateException", NULL);
282        return;
283    }
284
285    sp<AMessage> format;
286    status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
287
288    if (err != OK) {
289        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
290        return;
291    }
292
293    sp<ISurfaceTexture> surfaceTexture;
294    if (jsurface != NULL) {
295        sp<Surface> surface(Surface_getSurface(env, jsurface));
296        if (surface != NULL) {
297            surfaceTexture = surface->getSurfaceTexture();
298        } else {
299            jniThrowException(
300                    env,
301                    "java/lang/IllegalArgumentException",
302                    "The surface has been released");
303            return;
304        }
305    }
306
307    sp<ICrypto> crypto;
308    if (jcrypto != NULL) {
309        crypto = JCrypto::GetCrypto(env, jcrypto);
310    }
311
312    err = codec->configure(format, surfaceTexture, crypto, flags);
313
314    throwExceptionAsNecessary(env, err);
315}
316
317static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
318    ALOGV("android_media_MediaCodec_start");
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->start();
328
329    throwExceptionAsNecessary(env, err);
330}
331
332static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
333    ALOGV("android_media_MediaCodec_stop");
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->stop();
343
344    throwExceptionAsNecessary(env, err);
345}
346
347static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
348    ALOGV("android_media_MediaCodec_flush");
349
350    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
351
352    if (codec == NULL) {
353        jniThrowException(env, "java/lang/IllegalStateException", NULL);
354        return;
355    }
356
357    status_t err = codec->flush();
358
359    throwExceptionAsNecessary(env, err);
360}
361
362static void android_media_MediaCodec_queueInputBuffer(
363        JNIEnv *env,
364        jobject thiz,
365        jint index,
366        jint offset,
367        jint size,
368        jlong timestampUs,
369        jint flags) {
370    ALOGV("android_media_MediaCodec_queueInputBuffer");
371
372    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
373
374    if (codec == NULL) {
375        jniThrowException(env, "java/lang/IllegalStateException", NULL);
376        return;
377    }
378
379    status_t err = codec->queueInputBuffer(
380            index, offset, size, timestampUs, flags);
381
382    throwExceptionAsNecessary(env, err);
383}
384
385static void android_media_MediaCodec_queueSecureInputBuffer(
386        JNIEnv *env,
387        jobject thiz,
388        jint index,
389        jint offset,
390        jintArray numBytesOfClearDataObj,
391        jintArray numBytesOfEncryptedDataObj,
392        jint numSubSamples,
393        jbyteArray keyObj,
394        jbyteArray ivObj,
395        jint mode,
396        jlong timestampUs,
397        jint flags) {
398    ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
399
400    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
401
402    if (codec == NULL) {
403        jniThrowException(env, "java/lang/IllegalStateException", NULL);
404        return;
405    }
406
407    status_t err = OK;
408
409    CryptoPlugin::SubSample *subSamples = NULL;
410    jbyte *key = NULL;
411    jbyte *iv = NULL;
412
413    if (numSubSamples <= 0) {
414        err = -EINVAL;
415    } else if (numBytesOfClearDataObj == NULL
416            && numBytesOfEncryptedDataObj == NULL) {
417        err = -EINVAL;
418    } else if (numBytesOfEncryptedDataObj != NULL
419            && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
420        err = -ERANGE;
421    } else if (numBytesOfClearDataObj != NULL
422            && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
423        err = -ERANGE;
424    } else {
425        jboolean isCopy;
426
427        jint *numBytesOfClearData =
428            (numBytesOfClearDataObj == NULL)
429                ? NULL
430                : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
431
432        jint *numBytesOfEncryptedData =
433            (numBytesOfEncryptedDataObj == NULL)
434                ? NULL
435                : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
436
437        subSamples = new CryptoPlugin::SubSample[numSubSamples];
438
439        for (jint i = 0; i < numSubSamples; ++i) {
440            subSamples[i].mNumBytesOfClearData =
441                (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
442
443            subSamples[i].mNumBytesOfEncryptedData =
444                (numBytesOfEncryptedData == NULL)
445                    ? 0 : numBytesOfEncryptedData[i];
446        }
447
448        if (numBytesOfEncryptedData != NULL) {
449            env->ReleaseIntArrayElements(
450                    numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
451            numBytesOfEncryptedData = NULL;
452        }
453
454        if (numBytesOfClearData != NULL) {
455            env->ReleaseIntArrayElements(
456                    numBytesOfClearDataObj, numBytesOfClearData, 0);
457            numBytesOfClearData = NULL;
458        }
459    }
460
461    if (err == OK && keyObj != NULL) {
462        if (env->GetArrayLength(keyObj) != 16) {
463            err = -EINVAL;
464        } else {
465            jboolean isCopy;
466            key = env->GetByteArrayElements(keyObj, &isCopy);
467        }
468    }
469
470    if (err == OK && ivObj != NULL) {
471        if (env->GetArrayLength(ivObj) != 16) {
472            err = -EINVAL;
473        } else {
474            jboolean isCopy;
475            iv = env->GetByteArrayElements(ivObj, &isCopy);
476        }
477    }
478
479    if (err == OK) {
480        err = codec->queueSecureInputBuffer(
481                index, offset,
482                subSamples, numSubSamples,
483                (const uint8_t *)key, (const uint8_t *)iv,
484                (CryptoPlugin::Mode)mode,
485                timestampUs, flags);
486    }
487
488    if (iv != NULL) {
489        env->ReleaseByteArrayElements(ivObj, iv, 0);
490        iv = NULL;
491    }
492
493    if (key != NULL) {
494        env->ReleaseByteArrayElements(keyObj, key, 0);
495        key = NULL;
496    }
497
498    delete[] subSamples;
499    subSamples = NULL;
500
501    throwExceptionAsNecessary(env, err);
502}
503
504static jint android_media_MediaCodec_dequeueInputBuffer(
505        JNIEnv *env, jobject thiz, jlong timeoutUs) {
506    ALOGV("android_media_MediaCodec_dequeueInputBuffer");
507
508    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
509
510    if (codec == NULL) {
511        jniThrowException(env, "java/lang/IllegalStateException", NULL);
512        return -1;
513    }
514
515    size_t index;
516    status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
517
518    if (err == OK) {
519        return index;
520    }
521
522    return throwExceptionAsNecessary(env, err);
523}
524
525static jint android_media_MediaCodec_dequeueOutputBuffer(
526        JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
527    ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
528
529    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
530
531    if (codec == NULL) {
532        jniThrowException(env, "java/lang/IllegalStateException", NULL);
533        return 0;
534    }
535
536    size_t index;
537    status_t err = codec->dequeueOutputBuffer(
538            env, bufferInfo, &index, timeoutUs);
539
540    if (err == OK) {
541        return index;
542    }
543
544    return throwExceptionAsNecessary(env, err);
545}
546
547static void android_media_MediaCodec_releaseOutputBuffer(
548        JNIEnv *env, jobject thiz, jint index, jboolean render) {
549    ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
550
551    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
552
553    if (codec == NULL) {
554        jniThrowException(env, "java/lang/IllegalStateException", NULL);
555        return;
556    }
557
558    status_t err = codec->releaseOutputBuffer(index, render);
559
560    throwExceptionAsNecessary(env, err);
561}
562
563static jobject android_media_MediaCodec_getOutputFormat(
564        JNIEnv *env, jobject thiz) {
565    ALOGV("android_media_MediaCodec_getOutputFormat");
566
567    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
568
569    if (codec == NULL) {
570        jniThrowException(env, "java/lang/IllegalStateException", NULL);
571        return NULL;
572    }
573
574    jobject format;
575    status_t err = codec->getOutputFormat(env, &format);
576
577    if (err == OK) {
578        return format;
579    }
580
581    throwExceptionAsNecessary(env, err);
582
583    return NULL;
584}
585
586static jobjectArray android_media_MediaCodec_getBuffers(
587        JNIEnv *env, jobject thiz, jboolean input) {
588    ALOGV("android_media_MediaCodec_getBuffers");
589
590    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
591
592    if (codec == NULL) {
593        jniThrowException(env, "java/lang/IllegalStateException", NULL);
594        return NULL;
595    }
596
597    jobjectArray buffers;
598    status_t err = codec->getBuffers(env, input, &buffers);
599
600    if (err == OK) {
601        return buffers;
602    }
603
604    throwExceptionAsNecessary(env, err);
605
606    return NULL;
607}
608
609static void android_media_MediaCodec_native_init(JNIEnv *env) {
610    jclass clazz = env->FindClass("android/media/MediaCodec");
611    CHECK(clazz != NULL);
612
613    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
614    CHECK(gFields.context != NULL);
615}
616
617static void android_media_MediaCodec_native_setup(
618        JNIEnv *env, jobject thiz,
619        jstring name, jboolean nameIsType, jboolean encoder) {
620    if (name == NULL) {
621        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
622        return;
623    }
624
625    const char *tmp = env->GetStringUTFChars(name, NULL);
626
627    if (tmp == NULL) {
628        return;
629    }
630
631    sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
632
633    status_t err = codec->initCheck();
634
635    env->ReleaseStringUTFChars(name, tmp);
636    tmp = NULL;
637
638    if (err != OK) {
639        jniThrowException(
640                env,
641                "java/io/IOException",
642                "Failed to allocate component instance");
643        return;
644    }
645
646    setMediaCodec(env,thiz, codec);
647}
648
649static void android_media_MediaCodec_native_finalize(
650        JNIEnv *env, jobject thiz) {
651    android_media_MediaCodec_release(env, thiz);
652}
653
654static JNINativeMethod gMethods[] = {
655    { "release", "()V", (void *)android_media_MediaCodec_release },
656
657    { "native_configure",
658      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
659      "Landroid/media/MediaCrypto;I)V",
660      (void *)android_media_MediaCodec_native_configure },
661
662    { "start", "()V", (void *)android_media_MediaCodec_start },
663    { "stop", "()V", (void *)android_media_MediaCodec_stop },
664    { "flush", "()V", (void *)android_media_MediaCodec_flush },
665
666    { "queueInputBuffer", "(IIIJI)V",
667      (void *)android_media_MediaCodec_queueInputBuffer },
668
669    { "queueSecureInputBuffer", "(II[I[II[B[BIJI)V",
670      (void *)android_media_MediaCodec_queueSecureInputBuffer },
671
672    { "dequeueInputBuffer", "(J)I",
673      (void *)android_media_MediaCodec_dequeueInputBuffer },
674
675    { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
676      (void *)android_media_MediaCodec_dequeueOutputBuffer },
677
678    { "releaseOutputBuffer", "(IZ)V",
679      (void *)android_media_MediaCodec_releaseOutputBuffer },
680
681    { "getOutputFormat", "()Ljava/util/Map;",
682      (void *)android_media_MediaCodec_getOutputFormat },
683
684    { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
685      (void *)android_media_MediaCodec_getBuffers },
686
687    { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
688
689    { "native_setup", "(Ljava/lang/String;ZZ)V",
690      (void *)android_media_MediaCodec_native_setup },
691
692    { "native_finalize", "()V",
693      (void *)android_media_MediaCodec_native_finalize },
694};
695
696int register_android_media_MediaCodec(JNIEnv *env) {
697    return AndroidRuntime::registerNativeMethods(env,
698                "android/media/MediaCodec", gMethods, NELEM(gMethods));
699}
700