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 <nativehelper/JNIHelp.h>
30#include "android_media_Utils.h"
31
32using namespace android;
33
34static 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
44static 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
54static 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
72static 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
97static 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
114static 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
146static 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
265static jobject android_media_MediaCodecList_getGlobalSettings(JNIEnv *env, jobject /* thiz */) {
266    sp<IMediaCodecList> mcl = getCodecList(env);
267    if (mcl == NULL) {
268        // Runtime exception already pending.
269        return NULL;
270    }
271
272    const sp<AMessage> settings = mcl->getGlobalSettings();
273    if (settings == NULL) {
274        jniThrowException(env, "java/lang/RuntimeException", "cannot get global settings");
275        return NULL;
276    }
277
278    jobject settingsObj = NULL;
279    if (ConvertMessageToMap(env, settings, &settingsObj)) {
280        return NULL;
281    }
282
283    return settingsObj;
284}
285
286static void android_media_MediaCodecList_native_init(JNIEnv* /* env */) {
287}
288
289static const JNINativeMethod gMethods[] = {
290    { "native_getCodecCount", "()I", (void *)android_media_MediaCodecList_getCodecCount },
291    { "getCodecName", "(I)Ljava/lang/String;",
292      (void *)android_media_MediaCodecList_getCodecName },
293    { "isEncoder", "(I)Z", (void *)android_media_MediaCodecList_isEncoder },
294    { "getSupportedTypes", "(I)[Ljava/lang/String;",
295      (void *)android_media_MediaCodecList_getSupportedTypes },
296
297    { "getCodecCapabilities",
298      "(ILjava/lang/String;)Landroid/media/MediaCodecInfo$CodecCapabilities;",
299      (void *)android_media_MediaCodecList_getCodecCapabilities },
300
301    { "native_getGlobalSettings",
302      "()Ljava/util/Map;",
303      (void *)android_media_MediaCodecList_getGlobalSettings },
304
305    { "findCodecByName", "(Ljava/lang/String;)I",
306      (void *)android_media_MediaCodecList_findCodecByName },
307
308    { "native_init", "()V", (void *)android_media_MediaCodecList_native_init },
309};
310
311int register_android_media_MediaCodecList(JNIEnv *env) {
312    return AndroidRuntime::registerNativeMethods(env,
313                "android/media/MediaCodecList", gMethods, NELEM(gMethods));
314}
315
316