Home | History | Annotate | Download | only in jni
      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 <media/stagefright/foundation/ADebug.h>
     22 #include <media/stagefright/foundation/AMessage.h>
     23 #include <media/stagefright/MediaCodecList.h>
     24 #include <media/IMediaCodecList.h>
     25 #include <media/MediaCodecInfo.h>
     26 
     27 #include "android_runtime/AndroidRuntime.h"
     28 #include "jni.h"
     29 #include "JNIHelp.h"
     30 #include "android_media_Utils.h"
     31 
     32 using namespace android;
     33 
     34 static sp<IMediaCodecList> getCodecList(JNIEnv *env) {
     35     sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
     36     if (mcl == NULL) {
     37         // This should never happen unless something is really wrong
     38         jniThrowException(
     39                     env, "java/lang/RuntimeException", "cannot get MediaCodecList");
     40     }
     41     return mcl;
     42 }
     43 
     44 static jint android_media_MediaCodecList_getCodecCount(
     45         JNIEnv *env, jobject thiz) {
     46     sp<IMediaCodecList> mcl = getCodecList(env);
     47     if (mcl == NULL) {
     48         // Runtime exception already pending.
     49         return 0;
     50     }
     51     return mcl->countCodecs();
     52 }
     53 
     54 static jstring android_media_MediaCodecList_getCodecName(
     55         JNIEnv *env, jobject thiz, jint index) {
     56     sp<IMediaCodecList> mcl = getCodecList(env);
     57     if (mcl == NULL) {
     58         // Runtime exception already pending.
     59         return NULL;
     60     }
     61 
     62     const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index);
     63     if (info == NULL) {
     64         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
     65         return NULL;
     66     }
     67 
     68     const char *name = info->getCodecName();
     69     return env->NewStringUTF(name);
     70 }
     71 
     72 static jint android_media_MediaCodecList_findCodecByName(
     73         JNIEnv *env, jobject thiz, jstring name) {
     74     if (name == NULL) {
     75         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
     76         return -ENOENT;
     77     }
     78 
     79     const char *nameStr = env->GetStringUTFChars(name, NULL);
     80     if (nameStr == NULL) {
     81         // Out of memory exception already pending.
     82         return -ENOENT;
     83     }
     84 
     85     sp<IMediaCodecList> mcl = getCodecList(env);
     86     if (mcl == NULL) {
     87         // Runtime exception already pending.
     88         env->ReleaseStringUTFChars(name, nameStr);
     89         return -ENOENT;
     90     }
     91 
     92     jint ret = mcl->findCodecByName(nameStr);
     93     env->ReleaseStringUTFChars(name, nameStr);
     94     return ret;
     95 }
     96 
     97 static jboolean android_media_MediaCodecList_isEncoder(
     98         JNIEnv *env, jobject thiz, jint index) {
     99     sp<IMediaCodecList> mcl = getCodecList(env);
    100     if (mcl == NULL) {
    101         // Runtime exception already pending.
    102         return false;
    103     }
    104 
    105     const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index);
    106     if (info == NULL) {
    107         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    108         return false;
    109     }
    110 
    111     return info->isEncoder();
    112 }
    113 
    114 static jarray android_media_MediaCodecList_getSupportedTypes(
    115         JNIEnv *env, jobject thiz, jint index) {
    116     sp<IMediaCodecList> mcl = getCodecList(env);
    117     if (mcl == NULL) {
    118         // Runtime exception already pending.
    119         return NULL;
    120     }
    121 
    122     const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index);
    123     if (info == NULL) {
    124         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    125         return NULL;
    126     }
    127 
    128     Vector<AString> types;
    129     info->getSupportedMimes(&types);
    130 
    131     jclass clazz = env->FindClass("java/lang/String");
    132     CHECK(clazz != NULL);
    133 
    134     jobjectArray array = env->NewObjectArray(types.size(), clazz, NULL);
    135 
    136     for (size_t i = 0; i < types.size(); ++i) {
    137         jstring obj = env->NewStringUTF(types.itemAt(i).c_str());
    138         env->SetObjectArrayElement(array, i, obj);
    139         env->DeleteLocalRef(obj);
    140         obj = NULL;
    141     }
    142 
    143     return array;
    144 }
    145 
    146 static jobject android_media_MediaCodecList_getCodecCapabilities(
    147         JNIEnv *env, jobject thiz, jint index, jstring type) {
    148     if (type == NULL) {
    149         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    150         return NULL;
    151     }
    152 
    153     sp<IMediaCodecList> mcl = getCodecList(env);
    154     if (mcl == NULL) {
    155         // Runtime exception already pending.
    156         return NULL;
    157     }
    158 
    159     const sp<MediaCodecInfo> &info = mcl->getCodecInfo(index);
    160     if (info == NULL) {
    161         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    162         return NULL;
    163     }
    164 
    165     const char *typeStr = env->GetStringUTFChars(type, NULL);
    166     if (typeStr == NULL) {
    167         // Out of memory exception already pending.
    168         return NULL;
    169     }
    170 
    171     Vector<MediaCodecInfo::ProfileLevel> profileLevels;
    172     Vector<uint32_t> colorFormats;
    173 
    174     sp<AMessage> defaultFormat = new AMessage();
    175     defaultFormat->setString("mime", typeStr);
    176 
    177     // TODO query default-format also from codec/codec list
    178     const sp<MediaCodecInfo::Capabilities> &capabilities =
    179         info->getCapabilitiesFor(typeStr);
    180     env->ReleaseStringUTFChars(type, typeStr);
    181     typeStr = NULL;
    182     if (capabilities == NULL) {
    183         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    184         return NULL;
    185     }
    186 
    187     capabilities->getSupportedColorFormats(&colorFormats);
    188     capabilities->getSupportedProfileLevels(&profileLevels);
    189     uint32_t flags = capabilities->getFlags();
    190     sp<AMessage> details = capabilities->getDetails();
    191     bool isEncoder = info->isEncoder();
    192 
    193     jobject defaultFormatObj = NULL;
    194     if (ConvertMessageToMap(env, defaultFormat, &defaultFormatObj)) {
    195         return NULL;
    196     }
    197 
    198     jobject infoObj = NULL;
    199     if (ConvertMessageToMap(env, details, &infoObj)) {
    200         env->DeleteLocalRef(defaultFormatObj);
    201         return NULL;
    202     }
    203 
    204     jclass capsClazz =
    205         env->FindClass("android/media/MediaCodecInfo$CodecCapabilities");
    206     CHECK(capsClazz != NULL);
    207 
    208     jclass profileLevelClazz =
    209         env->FindClass("android/media/MediaCodecInfo$CodecProfileLevel");
    210     CHECK(profileLevelClazz != NULL);
    211 
    212     jobjectArray profileLevelArray =
    213         env->NewObjectArray(profileLevels.size(), profileLevelClazz, NULL);
    214 
    215     jfieldID profileField =
    216         env->GetFieldID(profileLevelClazz, "profile", "I");
    217 
    218     jfieldID levelField =
    219         env->GetFieldID(profileLevelClazz, "level", "I");
    220 
    221     for (size_t i = 0; i < profileLevels.size(); ++i) {
    222         const MediaCodecInfo::ProfileLevel &src = profileLevels.itemAt(i);
    223 
    224         jobject profileLevelObj = env->AllocObject(profileLevelClazz);
    225 
    226         env->SetIntField(profileLevelObj, profileField, src.mProfile);
    227         env->SetIntField(profileLevelObj, levelField, src.mLevel);
    228 
    229         env->SetObjectArrayElement(profileLevelArray, i, profileLevelObj);
    230 
    231         env->DeleteLocalRef(profileLevelObj);
    232         profileLevelObj = NULL;
    233     }
    234 
    235     jintArray colorFormatsArray = env->NewIntArray(colorFormats.size());
    236 
    237     for (size_t i = 0; i < colorFormats.size(); ++i) {
    238         jint val = colorFormats.itemAt(i);
    239         env->SetIntArrayRegion(colorFormatsArray, i, 1, &val);
    240     }
    241 
    242     jmethodID capsConstructID = env->GetMethodID(capsClazz, "<init>",
    243             "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZI"
    244             "Ljava/util/Map;Ljava/util/Map;)V");
    245 
    246     jobject caps = env->NewObject(capsClazz, capsConstructID,
    247             profileLevelArray, colorFormatsArray, isEncoder, flags,
    248             defaultFormatObj, infoObj);
    249 
    250     env->DeleteLocalRef(profileLevelArray);
    251     profileLevelArray = NULL;
    252 
    253     env->DeleteLocalRef(colorFormatsArray);
    254     colorFormatsArray = NULL;
    255 
    256     env->DeleteLocalRef(defaultFormatObj);
    257     defaultFormatObj = NULL;
    258 
    259     env->DeleteLocalRef(infoObj);
    260     infoObj = NULL;
    261 
    262     return caps;
    263 }
    264 
    265 static void android_media_MediaCodecList_native_init(JNIEnv *env) {
    266 }
    267 
    268 static JNINativeMethod gMethods[] = {
    269     { "native_getCodecCount", "()I", (void *)android_media_MediaCodecList_getCodecCount },
    270     { "getCodecName", "(I)Ljava/lang/String;",
    271       (void *)android_media_MediaCodecList_getCodecName },
    272     { "isEncoder", "(I)Z", (void *)android_media_MediaCodecList_isEncoder },
    273     { "getSupportedTypes", "(I)[Ljava/lang/String;",
    274       (void *)android_media_MediaCodecList_getSupportedTypes },
    275 
    276     { "getCodecCapabilities",
    277       "(ILjava/lang/String;)Landroid/media/MediaCodecInfo$CodecCapabilities;",
    278       (void *)android_media_MediaCodecList_getCodecCapabilities },
    279 
    280     { "findCodecByName", "(Ljava/lang/String;)I",
    281       (void *)android_media_MediaCodecList_findCodecByName },
    282 
    283     { "native_init", "()V", (void *)android_media_MediaCodecList_native_init },
    284 };
    285 
    286 int register_android_media_MediaCodecList(JNIEnv *env) {
    287     return AndroidRuntime::registerNativeMethods(env,
    288                 "android/media/MediaCodecList", gMethods, NELEM(gMethods));
    289 }
    290 
    291