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