• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <utils/Vector.h>
28 
29 #include <vector>
30 
31 #include "android_runtime/AndroidRuntime.h"
32 #include "jni.h"
33 #include <nativehelper/JNIHelp.h>
34 #include "android_media_Streams.h"
35 
36 using namespace android;
37 
38 /**
39  * This object unwraps codec aliases into individual codec infos as the Java interface handles
40  * aliases in this way.
41  */
42 class JavaMediaCodecListWrapper {
43 public:
44     struct Info {
45         sp<MediaCodecInfo> info;
46         AString alias;
47     };
48 
getCodecInfo(size_t index) const49     const Info getCodecInfo(size_t index) const {
50         if (index < mInfoList.size()) {
51             return mInfoList[index];
52         }
53         // return
54         return Info { nullptr /* info */, "(none)" /* alias */ };
55     }
56 
countCodecs() const57     size_t countCodecs() const {
58         return mInfoList.size();
59     }
60 
getCodecList() const61     sp<IMediaCodecList> getCodecList() const {
62         return mCodecList;
63     }
64 
findCodecByName(AString name) const65     size_t findCodecByName(AString name) const {
66         auto it = mInfoIndex.find(name);
67         return it == mInfoIndex.end() ? -ENOENT : it->second;
68     }
69 
JavaMediaCodecListWrapper(sp<IMediaCodecList> mcl)70     JavaMediaCodecListWrapper(sp<IMediaCodecList> mcl)
71             : mCodecList(mcl) {
72         size_t numCodecs = mcl->countCodecs();
73         for (size_t ix = 0; ix < numCodecs; ++ix) {
74             sp<MediaCodecInfo> info = mcl->getCodecInfo(ix);
75             Vector<AString> namesAndAliases;
76             info->getAliases(&namesAndAliases);
77             namesAndAliases.insertAt(0);
78             namesAndAliases.editItemAt(0) = info->getCodecName();
79             for (const AString &nameOrAlias : namesAndAliases) {
80                 if (mInfoIndex.count(nameOrAlias) > 0) {
81                     // skip duplicate names or aliases
82                     continue;
83                 }
84                 mInfoIndex.emplace(nameOrAlias, mInfoList.size());
85                 mInfoList.emplace_back(Info { info, nameOrAlias });
86             }
87         }
88     }
89 
90 private:
91     sp<IMediaCodecList> mCodecList;
92     std::vector<Info> mInfoList;
93     std::map<AString, size_t> mInfoIndex;
94 };
95 
96 static std::mutex sMutex;
97 static std::unique_ptr<JavaMediaCodecListWrapper> sListWrapper;
98 
getCodecList(JNIEnv * env)99 static const JavaMediaCodecListWrapper *getCodecList(JNIEnv *env) {
100     std::lock_guard<std::mutex> lock(sMutex);
101     if (sListWrapper == nullptr) {
102         sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
103         if (mcl == NULL) {
104             // This should never happen unless something is really wrong
105             jniThrowException(
106                         env, "java/lang/RuntimeException", "cannot get MediaCodecList");
107         }
108 
109         sListWrapper.reset(new JavaMediaCodecListWrapper(mcl));
110     }
111     return sListWrapper.get();
112 }
113 
getCodecInfo(JNIEnv * env,jint index)114 static JavaMediaCodecListWrapper::Info getCodecInfo(JNIEnv *env, jint index) {
115     const JavaMediaCodecListWrapper *mcl = getCodecList(env);
116     if (mcl == nullptr) {
117         // Runtime exception already pending.
118         return JavaMediaCodecListWrapper::Info { nullptr /* info */, "(none)" /* alias */ };
119     }
120 
121     JavaMediaCodecListWrapper::Info info = mcl->getCodecInfo(index);
122     if (info.info == NULL) {
123         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
124     }
125 
126     return info;
127 }
128 
android_media_MediaCodecList_getCodecCount(JNIEnv * env,jobject)129 static jint android_media_MediaCodecList_getCodecCount(
130         JNIEnv *env, jobject /* thiz */) {
131     const JavaMediaCodecListWrapper *mcl = getCodecList(env);
132     if (mcl == NULL) {
133         // Runtime exception already pending.
134         return 0;
135     }
136 
137     return mcl->countCodecs();
138 }
139 
android_media_MediaCodecList_getCodecName(JNIEnv * env,jobject,jint index)140 static jstring android_media_MediaCodecList_getCodecName(
141         JNIEnv *env, jobject /* thiz */, jint index) {
142     JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
143     if (info.info == NULL) {
144         // Runtime exception already pending.
145         return NULL;
146     }
147 
148     const char *name = info.alias.c_str();
149     return env->NewStringUTF(name);
150 }
151 
android_media_MediaCodecList_getCanonicalName(JNIEnv * env,jobject,jint index)152 static jstring android_media_MediaCodecList_getCanonicalName(
153         JNIEnv *env, jobject /* thiz */, jint index) {
154     JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
155     if (info.info == NULL) {
156         // Runtime exception already pending.
157         return NULL;
158     }
159 
160     const char *name = info.info->getCodecName();
161     return env->NewStringUTF(name);
162 }
163 
android_media_MediaCodecList_findCodecByName(JNIEnv * env,jobject,jstring name)164 static jint android_media_MediaCodecList_findCodecByName(
165         JNIEnv *env, jobject /* thiz */, jstring name) {
166     if (name == NULL) {
167         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
168         return -ENOENT;
169     }
170 
171     const char *nameStr = env->GetStringUTFChars(name, NULL);
172     if (nameStr == NULL) {
173         // Out of memory exception already pending.
174         return -ENOENT;
175     }
176 
177     const JavaMediaCodecListWrapper *mcl = getCodecList(env);
178     if (mcl == NULL) {
179         // Runtime exception already pending.
180         env->ReleaseStringUTFChars(name, nameStr);
181         return -ENOENT;
182     }
183 
184     jint ret = mcl->findCodecByName(nameStr);
185     env->ReleaseStringUTFChars(name, nameStr);
186     return ret;
187 }
188 
android_media_MediaCodecList_getAttributes(JNIEnv * env,jobject,jint index)189 static jboolean android_media_MediaCodecList_getAttributes(
190         JNIEnv *env, jobject /* thiz */, jint index) {
191     JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
192     if (info.info == NULL) {
193         // Runtime exception already pending.
194         return 0;
195     }
196 
197     return info.info->getAttributes();
198 }
199 
android_media_MediaCodecList_getSupportedTypes(JNIEnv * env,jobject,jint index)200 static jarray android_media_MediaCodecList_getSupportedTypes(
201         JNIEnv *env, jobject /* thiz */, jint index) {
202     JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
203     if (info.info == NULL) {
204         // Runtime exception already pending.
205         return NULL;
206     }
207 
208     Vector<AString> types;
209     info.info->getSupportedMediaTypes(&types);
210 
211     jclass clazz = env->FindClass("java/lang/String");
212     CHECK(clazz != NULL);
213 
214     jobjectArray array = env->NewObjectArray(types.size(), clazz, NULL);
215 
216     for (size_t i = 0; i < types.size(); ++i) {
217         jstring obj = env->NewStringUTF(types.itemAt(i).c_str());
218         env->SetObjectArrayElement(array, i, obj);
219         env->DeleteLocalRef(obj);
220         obj = NULL;
221     }
222 
223     return array;
224 }
225 
android_media_MediaCodecList_getCodecCapabilities(JNIEnv * env,jobject,jint index,jstring type)226 static jobject android_media_MediaCodecList_getCodecCapabilities(
227         JNIEnv *env, jobject /* thiz */, jint index, jstring type) {
228     if (type == NULL) {
229         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
230         return NULL;
231     }
232 
233     JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
234     if (info.info == NULL) {
235         // Runtime exception already pending.
236         return NULL;
237     }
238 
239 
240     const char *typeStr = env->GetStringUTFChars(type, NULL);
241     if (typeStr == NULL) {
242         // Out of memory exception already pending.
243         return NULL;
244     }
245 
246     Vector<MediaCodecInfo::ProfileLevel> profileLevels;
247     Vector<uint32_t> colorFormats;
248 
249     sp<AMessage> defaultFormat = new AMessage();
250     defaultFormat->setString("mime", typeStr);
251 
252     // TODO query default-format also from codec/codec list
253     const sp<MediaCodecInfo::Capabilities> &capabilities =
254         info.info->getCapabilitiesFor(typeStr);
255     env->ReleaseStringUTFChars(type, typeStr);
256     typeStr = NULL;
257     if (capabilities == NULL) {
258         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
259         return NULL;
260     }
261 
262     capabilities->getSupportedColorFormats(&colorFormats);
263     capabilities->getSupportedProfileLevels(&profileLevels);
264     sp<AMessage> details = capabilities->getDetails();
265     bool isEncoder = info.info->isEncoder();
266 
267     jobject defaultFormatObj = NULL;
268     if (ConvertMessageToMap(env, defaultFormat, &defaultFormatObj)) {
269         return NULL;
270     }
271 
272     jobject infoObj = NULL;
273     if (ConvertMessageToMap(env, details, &infoObj)) {
274         env->DeleteLocalRef(defaultFormatObj);
275         return NULL;
276     }
277 
278     jclass capsClazz =
279         env->FindClass("android/media/MediaCodecInfo$CodecCapabilities");
280     CHECK(capsClazz != NULL);
281 
282     jclass profileLevelClazz =
283         env->FindClass("android/media/MediaCodecInfo$CodecProfileLevel");
284     CHECK(profileLevelClazz != NULL);
285 
286     jobjectArray profileLevelArray =
287         env->NewObjectArray(profileLevels.size(), profileLevelClazz, NULL);
288 
289     jfieldID profileField =
290         env->GetFieldID(profileLevelClazz, "profile", "I");
291 
292     jfieldID levelField =
293         env->GetFieldID(profileLevelClazz, "level", "I");
294 
295     for (size_t i = 0; i < profileLevels.size(); ++i) {
296         const MediaCodecInfo::ProfileLevel &src = profileLevels.itemAt(i);
297 
298         jobject profileLevelObj = env->AllocObject(profileLevelClazz);
299 
300         env->SetIntField(profileLevelObj, profileField, src.mProfile);
301         env->SetIntField(profileLevelObj, levelField, src.mLevel);
302 
303         env->SetObjectArrayElement(profileLevelArray, i, profileLevelObj);
304 
305         env->DeleteLocalRef(profileLevelObj);
306         profileLevelObj = NULL;
307     }
308 
309     jintArray colorFormatsArray = env->NewIntArray(colorFormats.size());
310 
311     for (size_t i = 0; i < colorFormats.size(); ++i) {
312         jint val = colorFormats.itemAt(i);
313         env->SetIntArrayRegion(colorFormatsArray, i, 1, &val);
314     }
315 
316     jmethodID capsConstructID = env->GetMethodID(capsClazz, "<init>",
317             "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZ"
318             "Ljava/util/Map;Ljava/util/Map;)V");
319 
320     jobject caps = env->NewObject(capsClazz, capsConstructID,
321             profileLevelArray, colorFormatsArray, isEncoder,
322             defaultFormatObj, infoObj);
323 
324     env->DeleteLocalRef(profileLevelArray);
325     profileLevelArray = NULL;
326 
327     env->DeleteLocalRef(colorFormatsArray);
328     colorFormatsArray = NULL;
329 
330     env->DeleteLocalRef(defaultFormatObj);
331     defaultFormatObj = NULL;
332 
333     env->DeleteLocalRef(infoObj);
334     infoObj = NULL;
335 
336     return caps;
337 }
338 
android_media_MediaCodecList_getGlobalSettings(JNIEnv * env,jobject)339 static jobject android_media_MediaCodecList_getGlobalSettings(JNIEnv *env, jobject /* thiz */) {
340     const JavaMediaCodecListWrapper *mcl = getCodecList(env);
341     if (mcl == NULL) {
342         // Runtime exception already pending.
343         return NULL;
344     }
345 
346     const sp<AMessage> settings = mcl->getCodecList()->getGlobalSettings();
347     if (settings == NULL) {
348         jniThrowException(env, "java/lang/RuntimeException", "cannot get global settings");
349         return NULL;
350     }
351 
352     jobject settingsObj = NULL;
353     if (ConvertMessageToMap(env, settings, &settingsObj)) {
354         return NULL;
355     }
356 
357     return settingsObj;
358 }
359 
android_media_MediaCodecList_native_init(JNIEnv *)360 static void android_media_MediaCodecList_native_init(JNIEnv* /* env */) {
361 }
362 
363 static const JNINativeMethod gMethods[] = {
364     { "native_getCodecCount", "()I", (void *)android_media_MediaCodecList_getCodecCount },
365 
366     { "getCanonicalName", "(I)Ljava/lang/String;",
367       (void *)android_media_MediaCodecList_getCanonicalName },
368 
369     { "getCodecName", "(I)Ljava/lang/String;",
370       (void *)android_media_MediaCodecList_getCodecName },
371 
372     { "getAttributes", "(I)I", (void *)android_media_MediaCodecList_getAttributes },
373 
374     { "getSupportedTypes", "(I)[Ljava/lang/String;",
375       (void *)android_media_MediaCodecList_getSupportedTypes },
376 
377     { "getCodecCapabilities",
378       "(ILjava/lang/String;)Landroid/media/MediaCodecInfo$CodecCapabilities;",
379       (void *)android_media_MediaCodecList_getCodecCapabilities },
380 
381     { "native_getGlobalSettings",
382       "()Ljava/util/Map;",
383       (void *)android_media_MediaCodecList_getGlobalSettings },
384 
385     { "findCodecByName", "(Ljava/lang/String;)I",
386       (void *)android_media_MediaCodecList_findCodecByName },
387 
388     { "native_init", "()V", (void *)android_media_MediaCodecList_native_init },
389 };
390 
register_android_media_MediaCodecList(JNIEnv * env)391 int register_android_media_MediaCodecList(JNIEnv *env) {
392     return AndroidRuntime::registerNativeMethods(env,
393                 "android/media/MediaCodecList", gMethods, NELEM(gMethods));
394 }
395 
396