blob: 923d1d253c6e765a45688d2d8b959080f7d72c1c [file] [log] [blame]
Andreas Huber5a04bf32012-03-29 16:41:38 -07001/*
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>
Lajos Molnarb58dc312014-07-18 12:10:33 -070022#include <media/stagefright/foundation/AMessage.h>
Andreas Huber5a04bf32012-03-29 16:41:38 -070023#include <media/stagefright/MediaCodecList.h>
Lajos Molnarcd5e3f92014-08-06 12:18:34 -070024#include <media/IMediaCodecList.h>
25#include <media/MediaCodecInfo.h>
Andreas Huber5a04bf32012-03-29 16:41:38 -070026
Lajos Molnardd6125c2019-02-05 15:55:14 -080027#include <utils/Vector.h>
28
29#include <vector>
30
Andreas Huber5a04bf32012-03-29 16:41:38 -070031#include "android_runtime/AndroidRuntime.h"
32#include "jni.h"
Steven Moreland2279b252017-07-19 09:50:45 -070033#include <nativehelper/JNIHelp.h>
Jooyung Hancb1e8962019-02-21 14:18:11 +090034#include "android_media_Streams.h"
Andreas Huber5a04bf32012-03-29 16:41:38 -070035
36using namespace android;
37
Lajos Molnardd6125c2019-02-05 15:55:14 -080038/**
39 * This object unwraps codec aliases into individual codec infos as the Java interface handles
40 * aliases in this way.
41 */
42class JavaMediaCodecListWrapper {
43public:
44 struct Info {
45 sp<MediaCodecInfo> info;
46 AString alias;
47 };
48
49 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 */ };
Lajos Molnarcd5e3f92014-08-06 12:18:34 -070055 }
Lajos Molnardd6125c2019-02-05 15:55:14 -080056
57 size_t countCodecs() const {
58 return mInfoList.size();
59 }
60
61 sp<IMediaCodecList> getCodecList() const {
62 return mCodecList;
63 }
64
65 size_t findCodecByName(AString name) const {
66 auto it = mInfoIndex.find(name);
67 return it == mInfoIndex.end() ? -ENOENT : it->second;
68 }
69
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
90private:
91 sp<IMediaCodecList> mCodecList;
92 std::vector<Info> mInfoList;
93 std::map<AString, size_t> mInfoIndex;
94};
95
96static std::mutex sMutex;
97static std::unique_ptr<JavaMediaCodecListWrapper> sListWrapper;
98
99static 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();
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700112}
113
Lajos Molnardd6125c2019-02-05 15:55:14 -0800114static JavaMediaCodecListWrapper::Info getCodecInfo(JNIEnv *env, jint index) {
115 const JavaMediaCodecListWrapper *mcl = getCodecList(env);
116 if (mcl == nullptr) {
Lajos Molnard2a7f472018-11-15 12:49:20 -0800117 // Runtime exception already pending.
Lajos Molnardd6125c2019-02-05 15:55:14 -0800118 return JavaMediaCodecListWrapper::Info { nullptr /* info */, "(none)" /* alias */ };
Lajos Molnard2a7f472018-11-15 12:49:20 -0800119 }
120
Lajos Molnardd6125c2019-02-05 15:55:14 -0800121 JavaMediaCodecListWrapper::Info info = mcl->getCodecInfo(index);
122 if (info.info == NULL) {
Lajos Molnard2a7f472018-11-15 12:49:20 -0800123 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
124 }
125
126 return info;
127}
128
Andreas Huber60d610b2012-05-02 16:06:09 -0700129static jint android_media_MediaCodecList_getCodecCount(
Andreas Gampe5a15d0d2014-11-10 18:19:40 -0800130 JNIEnv *env, jobject /* thiz */) {
Lajos Molnardd6125c2019-02-05 15:55:14 -0800131 const JavaMediaCodecListWrapper *mcl = getCodecList(env);
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700132 if (mcl == NULL) {
133 // Runtime exception already pending.
134 return 0;
135 }
Lajos Molnardd6125c2019-02-05 15:55:14 -0800136
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700137 return mcl->countCodecs();
Andreas Huber5a04bf32012-03-29 16:41:38 -0700138}
139
140static jstring android_media_MediaCodecList_getCodecName(
Andreas Gampe5a15d0d2014-11-10 18:19:40 -0800141 JNIEnv *env, jobject /* thiz */, jint index) {
Lajos Molnardd6125c2019-02-05 15:55:14 -0800142 JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
143 if (info.info == NULL) {
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700144 // Runtime exception already pending.
145 return NULL;
146 }
Andreas Huber5a04bf32012-03-29 16:41:38 -0700147
Lajos Molnardd6125c2019-02-05 15:55:14 -0800148 const char *name = info.alias.c_str();
Lajos Molnard2a7f472018-11-15 12:49:20 -0800149 return env->NewStringUTF(name);
150}
151
152static jstring android_media_MediaCodecList_getCanonicalName(
153 JNIEnv *env, jobject /* thiz */, jint index) {
Lajos Molnardd6125c2019-02-05 15:55:14 -0800154 JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
155 if (info.info == NULL) {
Lajos Molnard2a7f472018-11-15 12:49:20 -0800156 // Runtime exception already pending.
Andreas Huber5a04bf32012-03-29 16:41:38 -0700157 return NULL;
158 }
159
Lajos Molnardd6125c2019-02-05 15:55:14 -0800160 const char *name = info.info->getCodecName();
Andreas Huber5a04bf32012-03-29 16:41:38 -0700161 return env->NewStringUTF(name);
162}
163
Martin Storsjo93077a22012-09-25 11:55:25 +0300164static jint android_media_MediaCodecList_findCodecByName(
Andreas Gampe5a15d0d2014-11-10 18:19:40 -0800165 JNIEnv *env, jobject /* thiz */, jstring name) {
Martin Storsjo93077a22012-09-25 11:55:25 +0300166 if (name == NULL) {
167 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
168 return -ENOENT;
169 }
170
171 const char *nameStr = env->GetStringUTFChars(name, NULL);
Martin Storsjo93077a22012-09-25 11:55:25 +0300172 if (nameStr == NULL) {
173 // Out of memory exception already pending.
174 return -ENOENT;
175 }
176
Lajos Molnardd6125c2019-02-05 15:55:14 -0800177 const JavaMediaCodecListWrapper *mcl = getCodecList(env);
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700178 if (mcl == NULL) {
179 // Runtime exception already pending.
Lajos Molnar018b70f2014-08-12 11:37:24 -0700180 env->ReleaseStringUTFChars(name, nameStr);
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700181 return -ENOENT;
182 }
183
184 jint ret = mcl->findCodecByName(nameStr);
Martin Storsjo93077a22012-09-25 11:55:25 +0300185 env->ReleaseStringUTFChars(name, nameStr);
186 return ret;
187}
188
Lajos Molnard2a7f472018-11-15 12:49:20 -0800189static jboolean android_media_MediaCodecList_getAttributes(
Andreas Gampe5a15d0d2014-11-10 18:19:40 -0800190 JNIEnv *env, jobject /* thiz */, jint index) {
Lajos Molnardd6125c2019-02-05 15:55:14 -0800191 JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
192 if (info.info == NULL) {
Lajos Molnard2a7f472018-11-15 12:49:20 -0800193 // Runtime exception already pending.
194 return 0;
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700195 }
196
Lajos Molnardd6125c2019-02-05 15:55:14 -0800197 return info.info->getAttributes();
Andreas Huber5a04bf32012-03-29 16:41:38 -0700198}
199
200static jarray android_media_MediaCodecList_getSupportedTypes(
Andreas Gampe5a15d0d2014-11-10 18:19:40 -0800201 JNIEnv *env, jobject /* thiz */, jint index) {
Lajos Molnardd6125c2019-02-05 15:55:14 -0800202 JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
203 if (info.info == NULL) {
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700204 // Runtime exception already pending.
205 return NULL;
206 }
Andreas Huber5a04bf32012-03-29 16:41:38 -0700207
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700208 Vector<AString> types;
Lajos Molnardd6125c2019-02-05 15:55:14 -0800209 info.info->getSupportedMediaTypes(&types);
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700210
Andreas Huber5a04bf32012-03-29 16:41:38 -0700211 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
226static jobject android_media_MediaCodecList_getCodecCapabilities(
Andreas Gampe5a15d0d2014-11-10 18:19:40 -0800227 JNIEnv *env, jobject /* thiz */, jint index, jstring type) {
Andreas Huber5a04bf32012-03-29 16:41:38 -0700228 if (type == NULL) {
229 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
230 return NULL;
231 }
232
Lajos Molnardd6125c2019-02-05 15:55:14 -0800233 JavaMediaCodecListWrapper::Info info = getCodecInfo(env, index);
234 if (info.info == NULL) {
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700235 // Runtime exception already pending.
236 return NULL;
237 }
238
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700239
Andreas Huber5a04bf32012-03-29 16:41:38 -0700240 const char *typeStr = env->GetStringUTFChars(type, NULL);
Andreas Huber5a04bf32012-03-29 16:41:38 -0700241 if (typeStr == NULL) {
242 // Out of memory exception already pending.
243 return NULL;
244 }
245
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700246 Vector<MediaCodecInfo::ProfileLevel> profileLevels;
Andreas Huber5a04bf32012-03-29 16:41:38 -0700247 Vector<uint32_t> colorFormats;
Lajos Molnarb58dc312014-07-18 12:10:33 -0700248
249 sp<AMessage> defaultFormat = new AMessage();
250 defaultFormat->setString("mime", typeStr);
251
252 // TODO query default-format also from codec/codec list
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700253 const sp<MediaCodecInfo::Capabilities> &capabilities =
Lajos Molnardd6125c2019-02-05 15:55:14 -0800254 info.info->getCapabilitiesFor(typeStr);
Lajos Molnar018b70f2014-08-12 11:37:24 -0700255 env->ReleaseStringUTFChars(type, typeStr);
256 typeStr = NULL;
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700257 if (capabilities == NULL) {
Andreas Huber5a04bf32012-03-29 16:41:38 -0700258 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
259 return NULL;
260 }
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700261
262 capabilities->getSupportedColorFormats(&colorFormats);
263 capabilities->getSupportedProfileLevels(&profileLevels);
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700264 sp<AMessage> details = capabilities->getDetails();
Lajos Molnardd6125c2019-02-05 15:55:14 -0800265 bool isEncoder = info.info->isEncoder();
Andreas Huber5a04bf32012-03-29 16:41:38 -0700266
Lajos Molnarb58dc312014-07-18 12:10:33 -0700267 jobject defaultFormatObj = NULL;
268 if (ConvertMessageToMap(env, defaultFormat, &defaultFormatObj)) {
269 return NULL;
270 }
271
272 jobject infoObj = NULL;
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700273 if (ConvertMessageToMap(env, details, &infoObj)) {
Lajos Molnarb58dc312014-07-18 12:10:33 -0700274 env->DeleteLocalRef(defaultFormatObj);
275 return NULL;
276 }
277
Andreas Huber5a04bf32012-03-29 16:41:38 -0700278 jclass capsClazz =
Andreas Huber60d610b2012-05-02 16:06:09 -0700279 env->FindClass("android/media/MediaCodecInfo$CodecCapabilities");
Andreas Huber5a04bf32012-03-29 16:41:38 -0700280 CHECK(capsClazz != NULL);
281
Andreas Huber5a04bf32012-03-29 16:41:38 -0700282 jclass profileLevelClazz =
Andreas Huber60d610b2012-05-02 16:06:09 -0700283 env->FindClass("android/media/MediaCodecInfo$CodecProfileLevel");
Andreas Huber5a04bf32012-03-29 16:41:38 -0700284 CHECK(profileLevelClazz != NULL);
285
286 jobjectArray profileLevelArray =
287 env->NewObjectArray(profileLevels.size(), profileLevelClazz, NULL);
288
289 jfieldID profileField =
Andreas Huber07ea4262012-04-11 12:21:20 -0700290 env->GetFieldID(profileLevelClazz, "profile", "I");
Andreas Huber5a04bf32012-03-29 16:41:38 -0700291
292 jfieldID levelField =
Andreas Huber07ea4262012-04-11 12:21:20 -0700293 env->GetFieldID(profileLevelClazz, "level", "I");
Andreas Huber5a04bf32012-03-29 16:41:38 -0700294
295 for (size_t i = 0; i < profileLevels.size(); ++i) {
Lajos Molnarcd5e3f92014-08-06 12:18:34 -0700296 const MediaCodecInfo::ProfileLevel &src = profileLevels.itemAt(i);
Andreas Huber5a04bf32012-03-29 16:41:38 -0700297
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
Andreas Huber5a04bf32012-03-29 16:41:38 -0700309 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
Lajos Molnarb58dc312014-07-18 12:10:33 -0700316 jmethodID capsConstructID = env->GetMethodID(capsClazz, "<init>",
Lajos Molnard2a7f472018-11-15 12:49:20 -0800317 "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZ"
Lajos Molnarb58dc312014-07-18 12:10:33 -0700318 "Ljava/util/Map;Ljava/util/Map;)V");
319
320 jobject caps = env->NewObject(capsClazz, capsConstructID,
Lajos Molnard2a7f472018-11-15 12:49:20 -0800321 profileLevelArray, colorFormatsArray, isEncoder,
Lajos Molnarb58dc312014-07-18 12:10:33 -0700322 defaultFormatObj, infoObj);
323
Lajos Molnarb58dc312014-07-18 12:10:33 -0700324 env->DeleteLocalRef(profileLevelArray);
325 profileLevelArray = NULL;
326
Andreas Huber5a04bf32012-03-29 16:41:38 -0700327 env->DeleteLocalRef(colorFormatsArray);
328 colorFormatsArray = NULL;
329
Lajos Molnarb58dc312014-07-18 12:10:33 -0700330 env->DeleteLocalRef(defaultFormatObj);
331 defaultFormatObj = NULL;
332
333 env->DeleteLocalRef(infoObj);
334 infoObj = NULL;
335
Andreas Huber5a04bf32012-03-29 16:41:38 -0700336 return caps;
337}
338
Ronghua Wuee299752015-03-25 10:53:04 -0700339static jobject android_media_MediaCodecList_getGlobalSettings(JNIEnv *env, jobject /* thiz */) {
Lajos Molnardd6125c2019-02-05 15:55:14 -0800340 const JavaMediaCodecListWrapper *mcl = getCodecList(env);
Ronghua Wuee299752015-03-25 10:53:04 -0700341 if (mcl == NULL) {
342 // Runtime exception already pending.
343 return NULL;
344 }
345
Lajos Molnardd6125c2019-02-05 15:55:14 -0800346 const sp<AMessage> settings = mcl->getCodecList()->getGlobalSettings();
Ronghua Wuee299752015-03-25 10:53:04 -0700347 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
Andreas Gampe5a15d0d2014-11-10 18:19:40 -0800360static void android_media_MediaCodecList_native_init(JNIEnv* /* env */) {
Andreas Huber5a04bf32012-03-29 16:41:38 -0700361}
362
Daniel Micay76f6a862015-09-19 17:31:01 -0400363static const JNINativeMethod gMethods[] = {
Lajos Molnarb58dc312014-07-18 12:10:33 -0700364 { "native_getCodecCount", "()I", (void *)android_media_MediaCodecList_getCodecCount },
Lajos Molnard2a7f472018-11-15 12:49:20 -0800365
366 { "getCanonicalName", "(I)Ljava/lang/String;",
367 (void *)android_media_MediaCodecList_getCanonicalName },
368
Andreas Huber5a04bf32012-03-29 16:41:38 -0700369 { "getCodecName", "(I)Ljava/lang/String;",
370 (void *)android_media_MediaCodecList_getCodecName },
Lajos Molnard2a7f472018-11-15 12:49:20 -0800371
372 { "getAttributes", "(I)I", (void *)android_media_MediaCodecList_getAttributes },
373
Andreas Huber5a04bf32012-03-29 16:41:38 -0700374 { "getSupportedTypes", "(I)[Ljava/lang/String;",
375 (void *)android_media_MediaCodecList_getSupportedTypes },
376
377 { "getCodecCapabilities",
Andreas Huber60d610b2012-05-02 16:06:09 -0700378 "(ILjava/lang/String;)Landroid/media/MediaCodecInfo$CodecCapabilities;",
Andreas Huber5a04bf32012-03-29 16:41:38 -0700379 (void *)android_media_MediaCodecList_getCodecCapabilities },
380
Ronghua Wuee299752015-03-25 10:53:04 -0700381 { "native_getGlobalSettings",
382 "()Ljava/util/Map;",
383 (void *)android_media_MediaCodecList_getGlobalSettings },
384
Martin Storsjo93077a22012-09-25 11:55:25 +0300385 { "findCodecByName", "(Ljava/lang/String;)I",
386 (void *)android_media_MediaCodecList_findCodecByName },
387
Andreas Huber5a04bf32012-03-29 16:41:38 -0700388 { "native_init", "()V", (void *)android_media_MediaCodecList_native_init },
389};
390
391int register_android_media_MediaCodecList(JNIEnv *env) {
392 return AndroidRuntime::registerNativeMethods(env,
393 "android/media/MediaCodecList", gMethods, NELEM(gMethods));
394}
395