1 /*****************************************************************************
2 * mediacodec_jni.c: mc_api implementation using JNI
3 *****************************************************************************
4 * Copyright © 2015 VLC authors and VideoLAN, VideoLabs
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
20
21 /*****************************************************************************
22 * Preamble
23 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <jni.h>
29 #include <stdint.h>
30 #include <assert.h>
31
32 #include <vlc_common.h>
33
34 #include <OMX_Core.h>
35 #include <OMX_Component.h>
36 #include "omxil_utils.h"
37 #include "../../packetizer/hevc_nal.h"
38
39 #include "mediacodec.h"
40
41 char* MediaCodec_GetName(vlc_object_t *p_obj, const char *psz_mime,
42 int profile, int *p_quirks);
43
44 #define THREAD_NAME "mediacodec_jni"
45
46 #define BUFFER_FLAG_CODEC_CONFIG 2
47 #define BUFFER_FLAG_END_OF_STREAM 4
48 #define INFO_OUTPUT_BUFFERS_CHANGED -3
49 #define INFO_OUTPUT_FORMAT_CHANGED -2
50 #define INFO_TRY_AGAIN_LATER -1
51
52 /*****************************************************************************
53 * JNI Initialisation
54 *****************************************************************************/
55
56 struct jfields
57 {
58 jclass media_codec_list_class, media_codec_class, media_format_class;
59 jclass buffer_info_class, byte_buffer_class;
60 jmethodID tostring;
61 jmethodID get_codec_count, get_codec_info_at, is_encoder, get_capabilities_for_type;
62 jmethodID is_feature_supported;
63 jfieldID profile_levels_field, profile_field, level_field;
64 jmethodID get_supported_types, get_name;
65 jmethodID create_by_codec_name, configure, start, stop, flush, release;
66 jmethodID get_output_format;
67 jmethodID get_input_buffers, get_input_buffer;
68 jmethodID get_output_buffers, get_output_buffer;
69 jmethodID dequeue_input_buffer, dequeue_output_buffer, queue_input_buffer;
70 jmethodID release_output_buffer;
71 jmethodID create_video_format, create_audio_format;
72 jmethodID set_integer, set_bytebuffer, get_integer;
73 jmethodID buffer_info_ctor;
74 jfieldID size_field, offset_field, pts_field, flags_field;
75 };
76 static struct jfields jfields;
77
78 enum Types
79 {
80 METHOD, STATIC_METHOD, FIELD
81 };
82
83 #define OFF(x) offsetof(struct jfields, x)
84 struct classname
85 {
86 const char *name;
87 int offset;
88 };
89 static const struct classname classes[] = {
90 { "android/media/MediaCodecList", OFF(media_codec_list_class) },
91 { "android/media/MediaCodec", OFF(media_codec_class) },
92 { "android/media/MediaFormat", OFF(media_format_class) },
93 { "android/media/MediaFormat", OFF(media_format_class) },
94 { "android/media/MediaCodec$BufferInfo", OFF(buffer_info_class) },
95 { "java/nio/ByteBuffer", OFF(byte_buffer_class) },
96 { NULL, 0 },
97 };
98
99 struct member
100 {
101 const char *name;
102 const char *sig;
103 const char *class;
104 int offset;
105 int type;
106 bool critical;
107 };
108 static const struct member members[] = {
109 { "toString", "()Ljava/lang/String;", "java/lang/Object", OFF(tostring), METHOD, true },
110
111 { "getCodecCount", "()I", "android/media/MediaCodecList", OFF(get_codec_count), STATIC_METHOD, true },
112 { "getCodecInfoAt", "(I)Landroid/media/MediaCodecInfo;", "android/media/MediaCodecList", OFF(get_codec_info_at), STATIC_METHOD, true },
113
114 { "isEncoder", "()Z", "android/media/MediaCodecInfo", OFF(is_encoder), METHOD, true },
115 { "getSupportedTypes", "()[Ljava/lang/String;", "android/media/MediaCodecInfo", OFF(get_supported_types), METHOD, true },
116 { "getName", "()Ljava/lang/String;", "android/media/MediaCodecInfo", OFF(get_name), METHOD, true },
117 { "getCapabilitiesForType", "(Ljava/lang/String;)Landroid/media/MediaCodecInfo$CodecCapabilities;", "android/media/MediaCodecInfo", OFF(get_capabilities_for_type), METHOD, true },
118 { "isFeatureSupported", "(Ljava/lang/String;)Z", "android/media/MediaCodecInfo$CodecCapabilities", OFF(is_feature_supported), METHOD, false },
119 { "profileLevels", "[Landroid/media/MediaCodecInfo$CodecProfileLevel;", "android/media/MediaCodecInfo$CodecCapabilities", OFF(profile_levels_field), FIELD, true },
120 { "profile", "I", "android/media/MediaCodecInfo$CodecProfileLevel", OFF(profile_field), FIELD, true },
121 { "level", "I", "android/media/MediaCodecInfo$CodecProfileLevel", OFF(level_field), FIELD, true },
122
123 { "createByCodecName", "(Ljava/lang/String;)Landroid/media/MediaCodec;", "android/media/MediaCodec", OFF(create_by_codec_name), STATIC_METHOD, true },
124 { "configure", "(Landroid/media/MediaFormat;Landroid/view/Surface;Landroid/media/MediaCrypto;I)V", "android/media/MediaCodec", OFF(configure), METHOD, true },
125 { "start", "()V", "android/media/MediaCodec", OFF(start), METHOD, true },
126 { "stop", "()V", "android/media/MediaCodec", OFF(stop), METHOD, true },
127 { "flush", "()V", "android/media/MediaCodec", OFF(flush), METHOD, true },
128 { "release", "()V", "android/media/MediaCodec", OFF(release), METHOD, true },
129 { "getOutputFormat", "()Landroid/media/MediaFormat;", "android/media/MediaCodec", OFF(get_output_format), METHOD, true },
130 { "getInputBuffers", "()[Ljava/nio/ByteBuffer;", "android/media/MediaCodec", OFF(get_input_buffers), METHOD, false },
131 { "getInputBuffer", "(I)Ljava/nio/ByteBuffer;", "android/media/MediaCodec", OFF(get_input_buffer), METHOD, false },
132 { "getOutputBuffers", "()[Ljava/nio/ByteBuffer;", "android/media/MediaCodec", OFF(get_output_buffers), METHOD, false },
133 { "getOutputBuffer", "(I)Ljava/nio/ByteBuffer;", "android/media/MediaCodec", OFF(get_output_buffer), METHOD, false },
134 { "dequeueInputBuffer", "(J)I", "android/media/MediaCodec", OFF(dequeue_input_buffer), METHOD, true },
135 { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I", "android/media/MediaCodec", OFF(dequeue_output_buffer), METHOD, true },
136 { "queueInputBuffer", "(IIIJI)V", "android/media/MediaCodec", OFF(queue_input_buffer), METHOD, true },
137 { "releaseOutputBuffer", "(IZ)V", "android/media/MediaCodec", OFF(release_output_buffer), METHOD, true },
138
139 { "createVideoFormat", "(Ljava/lang/String;II)Landroid/media/MediaFormat;", "android/media/MediaFormat", OFF(create_video_format), STATIC_METHOD, true },
140 { "createAudioFormat", "(Ljava/lang/String;II)Landroid/media/MediaFormat;", "android/media/MediaFormat", OFF(create_audio_format), STATIC_METHOD, true },
141 { "setInteger", "(Ljava/lang/String;I)V", "android/media/MediaFormat", OFF(set_integer), METHOD, true },
142 { "getInteger", "(Ljava/lang/String;)I", "android/media/MediaFormat", OFF(get_integer), METHOD, true },
143 { "setByteBuffer", "(Ljava/lang/String;Ljava/nio/ByteBuffer;)V", "android/media/MediaFormat", OFF(set_bytebuffer), METHOD, true },
144
145 { "<init>", "()V", "android/media/MediaCodec$BufferInfo", OFF(buffer_info_ctor), METHOD, true },
146 { "size", "I", "android/media/MediaCodec$BufferInfo", OFF(size_field), FIELD, true },
147 { "offset", "I", "android/media/MediaCodec$BufferInfo", OFF(offset_field), FIELD, true },
148 { "presentationTimeUs", "J", "android/media/MediaCodec$BufferInfo", OFF(pts_field), FIELD, true },
149 { "flags", "I", "android/media/MediaCodec$BufferInfo", OFF(flags_field), FIELD, true },
150 { NULL, NULL, NULL, 0, 0, false },
151 };
152
jstrcmp(JNIEnv * env,jobject str,const char * str2)153 static int jstrcmp(JNIEnv* env, jobject str, const char* str2)
154 {
155 jsize len = (*env)->GetStringUTFLength(env, str);
156 if (len != (jsize) strlen(str2))
157 return -1;
158 const char *ptr = (*env)->GetStringUTFChars(env, str, NULL);
159 int ret = memcmp(ptr, str2, len);
160 (*env)->ReleaseStringUTFChars(env, str, ptr);
161 return ret;
162 }
163
check_exception(JNIEnv * env)164 static inline bool check_exception(JNIEnv *env)
165 {
166 if ((*env)->ExceptionCheck(env))
167 {
168 (*env)->ExceptionClear(env);
169 return true;
170 }
171 else
172 return false;
173 }
174 #define CHECK_EXCEPTION() check_exception(env)
175 #define GET_ENV() if (!(env = android_getEnv(api->p_obj, THREAD_NAME))) return MC_API_ERROR;
176
jni_new_string(JNIEnv * env,const char * psz_string)177 static inline jstring jni_new_string(JNIEnv *env, const char *psz_string)
178 {
179 jstring jstring = (*env)->NewStringUTF(env, psz_string);
180 return !CHECK_EXCEPTION() ? jstring : NULL;
181 }
182 #define JNI_NEW_STRING(psz_string) jni_new_string(env, psz_string)
183
get_integer(JNIEnv * env,jobject obj,const char * psz_name)184 static inline int get_integer(JNIEnv *env, jobject obj, const char *psz_name)
185 {
186 jstring jname = JNI_NEW_STRING(psz_name);
187 if (jname)
188 {
189 int i_ret = (*env)->CallIntMethod(env, obj, jfields.get_integer, jname);
190 (*env)->DeleteLocalRef(env, jname);
191 /* getInteger can throw NullPointerException (when fetching the
192 * "channel-mask" property for example) */
193 if (CHECK_EXCEPTION())
194 return 0;
195 return i_ret;
196 }
197 else
198 return 0;
199 }
200 #define GET_INTEGER(obj, name) get_integer(env, obj, name)
201
set_integer(JNIEnv * env,jobject jobj,const char * psz_name,int i_value)202 static inline void set_integer(JNIEnv *env, jobject jobj, const char *psz_name,
203 int i_value)
204 {
205 jstring jname = JNI_NEW_STRING(psz_name);
206 if (jname)
207 {
208 (*env)->CallVoidMethod(env, jobj, jfields.set_integer, jname, i_value);
209 (*env)->DeleteLocalRef(env, jname);
210 }
211 }
212 #define SET_INTEGER(obj, name, value) set_integer(env, obj, name, value)
213
214 /* Initialize all jni fields.
215 * Done only one time during the first initialisation */
216 static bool
InitJNIFields(vlc_object_t * p_obj,JNIEnv * env)217 InitJNIFields (vlc_object_t *p_obj, JNIEnv *env)
218 {
219 static vlc_mutex_t lock = VLC_STATIC_MUTEX;
220 static int i_init_state = -1;
221 bool ret;
222
223 vlc_mutex_lock(&lock);
224
225 if (i_init_state != -1)
226 goto end;
227
228 i_init_state = 0;
229
230 for (int i = 0; classes[i].name; i++)
231 {
232 jclass clazz = (*env)->FindClass(env, classes[i].name);
233 if (CHECK_EXCEPTION())
234 {
235 msg_Warn(p_obj, "Unable to find class %s", classes[i].name);
236 goto end;
237 }
238 *(jclass*)((uint8_t*)&jfields + classes[i].offset) =
239 (jclass) (*env)->NewGlobalRef(env, clazz);
240 (*env)->DeleteLocalRef(env, clazz);
241 }
242
243 jclass last_class = NULL;
244 for (int i = 0; members[i].name; i++)
245 {
246 if (i == 0 || strcmp(members[i].class, members[i - 1].class))
247 {
248 if (last_class != NULL)
249 (*env)->DeleteLocalRef(env, last_class);
250 last_class = (*env)->FindClass(env, members[i].class);
251 }
252
253 if (CHECK_EXCEPTION())
254 {
255 msg_Warn(p_obj, "Unable to find class %s", members[i].class);
256 goto end;
257 }
258
259 switch (members[i].type) {
260 case METHOD:
261 *(jmethodID*)((uint8_t*)&jfields + members[i].offset) =
262 (*env)->GetMethodID(env, last_class, members[i].name, members[i].sig);
263 break;
264 case STATIC_METHOD:
265 *(jmethodID*)((uint8_t*)&jfields + members[i].offset) =
266 (*env)->GetStaticMethodID(env, last_class, members[i].name, members[i].sig);
267 break;
268 case FIELD:
269 *(jfieldID*)((uint8_t*)&jfields + members[i].offset) =
270 (*env)->GetFieldID(env, last_class, members[i].name, members[i].sig);
271 break;
272 }
273 if (CHECK_EXCEPTION())
274 {
275 msg_Warn(p_obj, "Unable to find the member %s in %s",
276 members[i].name, members[i].class);
277 if (members[i].critical)
278 goto end;
279 }
280 }
281 if (last_class != NULL)
282 (*env)->DeleteLocalRef(env, last_class);
283 /* getInputBuffers and getOutputBuffers are deprecated if API >= 21
284 * use getInputBuffer and getOutputBuffer instead. */
285 if (jfields.get_input_buffer && jfields.get_output_buffer)
286 {
287 jfields.get_output_buffers =
288 jfields.get_input_buffers = NULL;
289 }
290 else if (!jfields.get_output_buffers && !jfields.get_input_buffers)
291 {
292 msg_Err(p_obj, "Unable to find get Output/Input Buffer/Buffers");
293 goto end;
294 }
295
296 i_init_state = 1;
297 end:
298 ret = i_init_state == 1;
299 if (!ret)
300 msg_Err(p_obj, "MediaCodec jni init failed");
301
302 vlc_mutex_unlock(&lock);
303 return ret;
304 }
305
306 /****************************************************************************
307 * Local prototypes
308 ****************************************************************************/
309
310 struct mc_api_sys
311 {
312 jobject codec;
313 jobject buffer_info;
314 jobject input_buffers, output_buffers;
315 };
316
GetManufacturer(JNIEnv * env)317 static char *GetManufacturer(JNIEnv *env)
318 {
319 char *manufacturer = NULL;
320
321 jclass clazz = (*env)->FindClass(env, "android/os/Build");
322 if (CHECK_EXCEPTION())
323 return NULL;
324
325 jfieldID id = (*env)->GetStaticFieldID(env, clazz, "MANUFACTURER",
326 "Ljava/lang/String;");
327 if (CHECK_EXCEPTION())
328 goto end;
329
330 jstring jstr = (*env)->GetStaticObjectField(env, clazz, id);
331
332 if (CHECK_EXCEPTION())
333 goto end;
334
335 const char *str = (*env)->GetStringUTFChars(env, jstr, 0);
336 if (str)
337 {
338 manufacturer = strdup(str);
339 (*env)->ReleaseStringUTFChars(env, jstr, str);
340 }
341
342 end:
343 (*env)->DeleteLocalRef(env, clazz);
344 return manufacturer;
345 }
346
347 /*****************************************************************************
348 * MediaCodec_GetName
349 *****************************************************************************/
MediaCodec_GetName(vlc_object_t * p_obj,const char * psz_mime,int profile,int * p_quirks)350 char* MediaCodec_GetName(vlc_object_t *p_obj, const char *psz_mime,
351 int profile, int *p_quirks)
352 {
353 JNIEnv *env;
354 int num_codecs;
355 jstring jmime;
356 char *psz_name = NULL;
357
358 if (!(env = android_getEnv(p_obj, THREAD_NAME)))
359 return NULL;
360
361 if (!InitJNIFields(p_obj, env))
362 return NULL;
363
364 jmime = JNI_NEW_STRING(psz_mime);
365 if (!jmime)
366 return NULL;
367
368 num_codecs = (*env)->CallStaticIntMethod(env,
369 jfields.media_codec_list_class,
370 jfields.get_codec_count);
371
372 for (int i = 0; i < num_codecs; i++)
373 {
374 jobject codec_capabilities = NULL;
375 jobject profile_levels = NULL;
376 jobject info = NULL;
377 jobject name = NULL;
378 jobject types = NULL;
379 jsize name_len = 0;
380 int profile_levels_len = 0, num_types = 0;
381 const char *name_ptr = NULL;
382 bool found = false;
383 bool b_adaptive = false;
384
385 info = (*env)->CallStaticObjectMethod(env, jfields.media_codec_list_class,
386 jfields.get_codec_info_at, i);
387
388 name = (*env)->CallObjectMethod(env, info, jfields.get_name);
389 name_len = (*env)->GetStringUTFLength(env, name);
390 name_ptr = (*env)->GetStringUTFChars(env, name, NULL);
391
392 if (OMXCodec_IsBlacklisted(name_ptr, name_len))
393 goto loopclean;
394
395 if ((*env)->CallBooleanMethod(env, info, jfields.is_encoder))
396 goto loopclean;
397
398 codec_capabilities = (*env)->CallObjectMethod(env, info,
399 jfields.get_capabilities_for_type,
400 jmime);
401 if (CHECK_EXCEPTION())
402 {
403 msg_Warn(p_obj, "Exception occurred in MediaCodecInfo.getCapabilitiesForType");
404 goto loopclean;
405 }
406 else if (codec_capabilities)
407 {
408 profile_levels = (*env)->GetObjectField(env, codec_capabilities, jfields.profile_levels_field);
409 if (profile_levels)
410 profile_levels_len = (*env)->GetArrayLength(env, profile_levels);
411 if (jfields.is_feature_supported)
412 {
413 jstring jfeature = JNI_NEW_STRING("adaptive-playback");
414 b_adaptive =
415 (*env)->CallBooleanMethod(env, codec_capabilities,
416 jfields.is_feature_supported,
417 jfeature);
418 CHECK_EXCEPTION();
419 (*env)->DeleteLocalRef(env, jfeature);
420 }
421 }
422 msg_Dbg(p_obj, "Number of profile levels: %d", profile_levels_len);
423
424 types = (*env)->CallObjectMethod(env, info, jfields.get_supported_types);
425 num_types = (*env)->GetArrayLength(env, types);
426 found = false;
427
428 for (int j = 0; j < num_types && !found; j++)
429 {
430 jobject type = (*env)->GetObjectArrayElement(env, types, j);
431 if (!jstrcmp(env, type, psz_mime))
432 {
433 /* The mime type is matching for this component. We
434 now check if the capabilities of the codec is
435 matching the video format. */
436 if (profile > 0)
437 {
438 /* This decoder doesn't expose its profiles and is high
439 * profile capable */
440 if (!strncmp(name_ptr, "OMX.LUMEVideoDecoder", __MIN(20, name_len)))
441 found = true;
442
443 for (int i = 0; i < profile_levels_len && !found; ++i)
444 {
445 jobject profile_level = (*env)->GetObjectArrayElement(env, profile_levels, i);
446
447 int omx_profile = (*env)->GetIntField(env, profile_level, jfields.profile_field);
448 (*env)->DeleteLocalRef(env, profile_level);
449
450 int codec_profile = 0;
451 if (strcmp(psz_mime, "video/avc") == 0)
452 codec_profile = convert_omx_to_profile_idc(omx_profile);
453 else if (strcmp(psz_mime, "video/hevc") == 0)
454 {
455 switch (omx_profile)
456 {
457 case 0x1: /* OMX_VIDEO_HEVCProfileMain */
458 codec_profile = HEVC_PROFILE_MAIN;
459 break;
460 case 0x2: /* OMX_VIDEO_HEVCProfileMain10 */
461 case 0x1000: /* OMX_VIDEO_HEVCProfileMain10HDR10 */
462 codec_profile = HEVC_PROFILE_MAIN_10;
463 break;
464 }
465 }
466 if (codec_profile != profile)
467 continue;
468 /* Some encoders set the level too high, thus we ignore it for the moment.
469 We could try to guess the actual profile based on the resolution. */
470 found = true;
471 }
472 }
473 else
474 found = true;
475 }
476 (*env)->DeleteLocalRef(env, type);
477 }
478 if (found)
479 {
480 msg_Dbg(p_obj, "using %.*s", name_len, name_ptr);
481 psz_name = malloc(name_len + 1);
482 if (psz_name)
483 {
484 memcpy(psz_name, name_ptr, name_len);
485 psz_name[name_len] = '\0';
486
487 bool ignore_size = false;
488
489 /* The AVC/HEVC MediaCodec implementation on Amazon fire TV
490 * seems to report the Output surface size instead of the Video
491 * size. This bug is specific to Amazon devices since other MTK
492 * implementations report the correct size. The manufacturer is
493 * checked only if the codec matches the MTK one in order to
494 * avoid extra manufacturer check for other every devices.
495 * */
496 static const char mtk_dec[] = "OMX.MTK.VIDEO.DECODER.";
497 if (strncmp(psz_name, mtk_dec, sizeof(mtk_dec) - 1) == 0)
498 {
499 char *manufacturer = GetManufacturer(env);
500 if (manufacturer && strcmp(manufacturer, "Amazon") == 0)
501 ignore_size = true;
502 free(manufacturer);
503 }
504
505 if (ignore_size)
506 {
507 *p_quirks |= MC_API_VIDEO_QUIRKS_IGNORE_SIZE;
508 /* If the MediaCodec size is ignored, the adaptive mode
509 * should be disabled in order to trigger the hxxx_helper
510 * parsers that will parse the correct video size. Hence
511 * the following 'else if' */
512 }
513 else if (b_adaptive)
514 *p_quirks |= MC_API_VIDEO_QUIRKS_ADAPTIVE;
515 }
516 }
517 loopclean:
518 if (name)
519 {
520 (*env)->ReleaseStringUTFChars(env, name, name_ptr);
521 (*env)->DeleteLocalRef(env, name);
522 }
523 if (profile_levels)
524 (*env)->DeleteLocalRef(env, profile_levels);
525 if (types)
526 (*env)->DeleteLocalRef(env, types);
527 if (codec_capabilities)
528 (*env)->DeleteLocalRef(env, codec_capabilities);
529 if (info)
530 (*env)->DeleteLocalRef(env, info);
531 if (found)
532 break;
533 }
534 (*env)->DeleteLocalRef(env, jmime);
535
536 return psz_name;
537 }
538
539 /*****************************************************************************
540 * Stop
541 *****************************************************************************/
Stop(mc_api * api)542 static int Stop(mc_api *api)
543 {
544 mc_api_sys *p_sys = api->p_sys;
545 JNIEnv *env;
546
547 api->b_direct_rendering = false;
548
549 GET_ENV();
550
551 if (p_sys->input_buffers)
552 {
553 (*env)->DeleteGlobalRef(env, p_sys->input_buffers);
554 p_sys->input_buffers = NULL;
555 }
556 if (p_sys->output_buffers)
557 {
558 (*env)->DeleteGlobalRef(env, p_sys->output_buffers);
559 p_sys->output_buffers = NULL;
560 }
561 if (p_sys->codec)
562 {
563 if (api->b_started)
564 {
565 (*env)->CallVoidMethod(env, p_sys->codec, jfields.stop);
566 if (CHECK_EXCEPTION())
567 msg_Err(api->p_obj, "Exception in MediaCodec.stop");
568 api->b_started = false;
569 }
570
571 (*env)->CallVoidMethod(env, p_sys->codec, jfields.release);
572 if (CHECK_EXCEPTION())
573 msg_Err(api->p_obj, "Exception in MediaCodec.release");
574 (*env)->DeleteGlobalRef(env, p_sys->codec);
575 p_sys->codec = NULL;
576 }
577 if (p_sys->buffer_info)
578 {
579 (*env)->DeleteGlobalRef(env, p_sys->buffer_info);
580 p_sys->buffer_info = NULL;
581 }
582 msg_Dbg(api->p_obj, "MediaCodec via JNI closed");
583 return 0;
584 }
585
586 /*****************************************************************************
587 * Start
588 *****************************************************************************/
Start(mc_api * api,union mc_api_args * p_args)589 static int Start(mc_api *api, union mc_api_args *p_args)
590 {
591 mc_api_sys *p_sys = api->p_sys;
592 JNIEnv* env = NULL;
593 int i_ret = MC_API_ERROR;
594 bool b_direct_rendering = false;
595 jstring jmime = NULL;
596 jstring jcodec_name = NULL;
597 jobject jcodec = NULL;
598 jobject jformat = NULL;
599 jobject jinput_buffers = NULL;
600 jobject joutput_buffers = NULL;
601 jobject jbuffer_info = NULL;
602 jobject jsurface = NULL;
603
604 assert(api->psz_mime && api->psz_name);
605
606 GET_ENV();
607
608 jmime = JNI_NEW_STRING(api->psz_mime);
609 jcodec_name = JNI_NEW_STRING(api->psz_name);
610 if (!jmime || !jcodec_name)
611 goto error;
612
613 /* This method doesn't handle errors nicely, it crashes if the codec isn't
614 * found. (The same goes for createDecoderByType.) This is fixed in latest
615 * AOSP and in 4.2, but not in 4.1 devices. */
616 jcodec = (*env)->CallStaticObjectMethod(env, jfields.media_codec_class,
617 jfields.create_by_codec_name,
618 jcodec_name);
619 if (CHECK_EXCEPTION())
620 {
621 msg_Warn(api->p_obj, "Exception occurred in MediaCodec.createByCodecName");
622 goto error;
623 }
624 p_sys->codec = (*env)->NewGlobalRef(env, jcodec);
625
626 if (api->i_cat == VIDEO_ES)
627 {
628 assert(p_args->video.i_angle == 0 || api->b_support_rotation);
629 jformat = (*env)->CallStaticObjectMethod(env,
630 jfields.media_format_class,
631 jfields.create_video_format,
632 jmime,
633 p_args->video.i_width,
634 p_args->video.i_height);
635 jsurface = p_args->video.p_jsurface;
636 b_direct_rendering = !!jsurface;
637
638 if (p_args->video.i_angle != 0)
639 SET_INTEGER(jformat, "rotation-degrees", p_args->video.i_angle);
640
641 if (b_direct_rendering)
642 {
643 /* feature-tunneled-playback available since API 21 */
644 if (jfields.get_input_buffer && p_args->video.b_tunneled_playback)
645 SET_INTEGER(jformat, "feature-tunneled-playback", 1);
646
647 if (p_args->video.b_adaptive_playback)
648 SET_INTEGER(jformat, "feature-adaptive-playback", 1);
649 }
650 }
651 else
652 {
653 jformat = (*env)->CallStaticObjectMethod(env,
654 jfields.media_format_class,
655 jfields.create_audio_format,
656 jmime,
657 p_args->audio.i_sample_rate,
658 p_args->audio.i_channel_count);
659 }
660 /* No limits for input size */
661 SET_INTEGER(jformat, "max-input-size", 0);
662
663 if (b_direct_rendering)
664 {
665 // Configure MediaCodec with the Android surface.
666 (*env)->CallVoidMethod(env, p_sys->codec, jfields.configure,
667 jformat, jsurface, NULL, 0);
668 if (CHECK_EXCEPTION())
669 {
670 msg_Warn(api->p_obj, "Exception occurred in MediaCodec.configure "
671 "with an output surface.");
672 goto error;
673 }
674 }
675 else
676 {
677 (*env)->CallVoidMethod(env, p_sys->codec, jfields.configure,
678 jformat, NULL, NULL, 0);
679 if (CHECK_EXCEPTION())
680 {
681 msg_Warn(api->p_obj, "Exception occurred in MediaCodec.configure");
682 goto error;
683 }
684 }
685
686 (*env)->CallVoidMethod(env, p_sys->codec, jfields.start);
687 if (CHECK_EXCEPTION())
688 {
689 msg_Warn(api->p_obj, "Exception occurred in MediaCodec.start");
690 goto error;
691 }
692 api->b_started = true;
693
694 if (jfields.get_input_buffers && jfields.get_output_buffers)
695 {
696
697 jinput_buffers = (*env)->CallObjectMethod(env, p_sys->codec,
698 jfields.get_input_buffers);
699 if (CHECK_EXCEPTION())
700 {
701 msg_Err(api->p_obj, "Exception in MediaCodec.getInputBuffers");
702 goto error;
703 }
704 p_sys->input_buffers = (*env)->NewGlobalRef(env, jinput_buffers);
705
706 joutput_buffers = (*env)->CallObjectMethod(env, p_sys->codec,
707 jfields.get_output_buffers);
708 if (CHECK_EXCEPTION())
709 {
710 msg_Err(api->p_obj, "Exception in MediaCodec.getOutputBuffers");
711 goto error;
712 }
713 p_sys->output_buffers = (*env)->NewGlobalRef(env, joutput_buffers);
714 }
715 jbuffer_info = (*env)->NewObject(env, jfields.buffer_info_class,
716 jfields.buffer_info_ctor);
717 p_sys->buffer_info = (*env)->NewGlobalRef(env, jbuffer_info);
718
719 api->b_direct_rendering = b_direct_rendering;
720 i_ret = 0;
721 msg_Dbg(api->p_obj, "MediaCodec via JNI opened");
722
723 error:
724 if (jmime)
725 (*env)->DeleteLocalRef(env, jmime);
726 if (jcodec_name)
727 (*env)->DeleteLocalRef(env, jcodec_name);
728 if (jcodec)
729 (*env)->DeleteLocalRef(env, jcodec);
730 if (jformat)
731 (*env)->DeleteLocalRef(env, jformat);
732 if (jinput_buffers)
733 (*env)->DeleteLocalRef(env, jinput_buffers);
734 if (joutput_buffers)
735 (*env)->DeleteLocalRef(env, joutput_buffers);
736 if (jbuffer_info)
737 (*env)->DeleteLocalRef(env, jbuffer_info);
738
739 if (i_ret != 0)
740 Stop(api);
741 return i_ret;
742 }
743
744 /*****************************************************************************
745 * Flush
746 *****************************************************************************/
Flush(mc_api * api)747 static int Flush(mc_api *api)
748 {
749 mc_api_sys *p_sys = api->p_sys;
750 JNIEnv *env = NULL;
751
752 GET_ENV();
753
754 (*env)->CallVoidMethod(env, p_sys->codec, jfields.flush);
755 if (CHECK_EXCEPTION())
756 {
757 msg_Warn(api->p_obj, "Exception occurred in MediaCodec.flush");
758 return MC_API_ERROR;
759 }
760 return 0;
761 }
762
763 /*****************************************************************************
764 * DequeueInput
765 *****************************************************************************/
DequeueInput(mc_api * api,mtime_t i_timeout)766 static int DequeueInput(mc_api *api, mtime_t i_timeout)
767 {
768 mc_api_sys *p_sys = api->p_sys;
769 JNIEnv *env;
770 int i_index;
771
772 GET_ENV();
773
774 i_index = (*env)->CallIntMethod(env, p_sys->codec,
775 jfields.dequeue_input_buffer, i_timeout);
776 if (CHECK_EXCEPTION())
777 {
778 msg_Err(api->p_obj, "Exception occurred in MediaCodec.dequeueInputBuffer");
779 return MC_API_ERROR;
780 }
781 if (i_index >= 0)
782 return i_index;
783 else
784 return MC_API_INFO_TRYAGAIN;
785
786 }
787
788 /*****************************************************************************
789 * QueueInput
790 *****************************************************************************/
QueueInput(mc_api * api,int i_index,const void * p_buf,size_t i_size,mtime_t i_ts,bool b_config)791 static int QueueInput(mc_api *api, int i_index, const void *p_buf,
792 size_t i_size, mtime_t i_ts, bool b_config)
793 {
794 mc_api_sys *p_sys = api->p_sys;
795 JNIEnv *env;
796 uint8_t *p_mc_buf;
797 jobject j_mc_buf;
798 jsize j_mc_size;
799 jint jflags = (b_config ? BUFFER_FLAG_CODEC_CONFIG : 0)
800 | (p_buf == NULL ? BUFFER_FLAG_END_OF_STREAM : 0);
801
802 assert(i_index >= 0);
803
804 GET_ENV();
805
806 if (jfields.get_input_buffers)
807 j_mc_buf = (*env)->GetObjectArrayElement(env, p_sys->input_buffers,
808 i_index);
809 else
810 {
811 j_mc_buf = (*env)->CallObjectMethod(env, p_sys->codec,
812 jfields.get_input_buffer, i_index);
813 if (CHECK_EXCEPTION())
814 {
815 msg_Err(api->p_obj, "Exception in MediaCodec.getInputBuffer");
816 return MC_API_ERROR;
817 }
818 }
819 j_mc_size = (*env)->GetDirectBufferCapacity(env, j_mc_buf);
820 p_mc_buf = (*env)->GetDirectBufferAddress(env, j_mc_buf);
821 if (j_mc_size < 0)
822 {
823 msg_Err(api->p_obj, "Java buffer has invalid size");
824 (*env)->DeleteLocalRef(env, j_mc_buf);
825 return MC_API_ERROR;
826 }
827 if ((size_t) j_mc_size > i_size)
828 j_mc_size = i_size;
829 memcpy(p_mc_buf, p_buf, j_mc_size);
830
831 (*env)->CallVoidMethod(env, p_sys->codec, jfields.queue_input_buffer,
832 i_index, 0, j_mc_size, i_ts, jflags);
833 (*env)->DeleteLocalRef(env, j_mc_buf);
834 if (CHECK_EXCEPTION())
835 {
836 msg_Err(api->p_obj, "Exception in MediaCodec.queueInputBuffer");
837 return MC_API_ERROR;
838 }
839
840 return 0;
841 }
842
843 /*****************************************************************************
844 * DequeueOutput
845 *****************************************************************************/
DequeueOutput(mc_api * api,mtime_t i_timeout)846 static int DequeueOutput(mc_api *api, mtime_t i_timeout)
847 {
848 mc_api_sys *p_sys = api->p_sys;
849 JNIEnv *env;
850 int i_index;
851
852 GET_ENV();
853 i_index = (*env)->CallIntMethod(env, p_sys->codec,
854 jfields.dequeue_output_buffer,
855 p_sys->buffer_info, i_timeout);
856 if (CHECK_EXCEPTION())
857 {
858 msg_Warn(api->p_obj, "Exception in MediaCodec.dequeueOutputBuffer");
859 return MC_API_ERROR;
860 }
861 if (i_index >= 0)
862 return i_index;
863 else if (i_index == INFO_OUTPUT_FORMAT_CHANGED)
864 return MC_API_INFO_OUTPUT_FORMAT_CHANGED;
865 else if (i_index == INFO_OUTPUT_BUFFERS_CHANGED)
866 return MC_API_INFO_OUTPUT_BUFFERS_CHANGED;
867 else
868 return MC_API_INFO_TRYAGAIN;
869 }
870
871 /*****************************************************************************
872 * GetOutput
873 *****************************************************************************/
GetOutput(mc_api * api,int i_index,mc_api_out * p_out)874 static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
875 {
876 mc_api_sys *p_sys = api->p_sys;
877 JNIEnv *env;
878
879 GET_ENV();
880
881 if (i_index >= 0)
882 {
883 p_out->type = MC_OUT_TYPE_BUF;
884 p_out->buf.i_index = i_index;
885 p_out->buf.i_ts = (*env)->GetLongField(env, p_sys->buffer_info,
886 jfields.pts_field);
887
888 int flags = (*env)->GetIntField(env, p_sys->buffer_info,
889 jfields.flags_field);
890 p_out->b_eos = flags & BUFFER_FLAG_END_OF_STREAM;
891
892 if (api->b_direct_rendering)
893 {
894 p_out->buf.p_ptr = NULL;
895 p_out->buf.i_size = 0;
896 }
897 else
898 {
899 jobject buf;
900 uint8_t *ptr = NULL;
901 int offset = 0;
902
903 if (jfields.get_output_buffers)
904 buf = (*env)->GetObjectArrayElement(env, p_sys->output_buffers,
905 i_index);
906 else
907 {
908 buf = (*env)->CallObjectMethod(env, p_sys->codec,
909 jfields.get_output_buffer,
910 i_index);
911 if (CHECK_EXCEPTION())
912 {
913 msg_Err(api->p_obj, "Exception in MediaCodec.getOutputBuffer");
914 return MC_API_ERROR;
915 }
916 }
917 //jsize buf_size = (*env)->GetDirectBufferCapacity(env, buf);
918 /* buf can be NULL in case of EOS */
919 if (buf)
920 {
921 ptr = (*env)->GetDirectBufferAddress(env, buf);
922
923 offset = (*env)->GetIntField(env, p_sys->buffer_info,
924 jfields.offset_field);
925 }
926 p_out->buf.p_ptr = ptr + offset;
927 p_out->buf.i_size = (*env)->GetIntField(env, p_sys->buffer_info,
928 jfields.size_field);
929 (*env)->DeleteLocalRef(env, buf);
930 }
931 return 1;
932 } else if (i_index == MC_API_INFO_OUTPUT_FORMAT_CHANGED)
933 {
934 jobject format = NULL;
935 jobject format_string = NULL;
936 jsize format_len;
937 const char *format_ptr;
938
939 format = (*env)->CallObjectMethod(env, p_sys->codec,
940 jfields.get_output_format);
941 if (CHECK_EXCEPTION())
942 {
943 msg_Err(api->p_obj, "Exception in MediaCodec.getOutputFormat");
944 return MC_API_ERROR;
945 }
946
947 format_string = (*env)->CallObjectMethod(env, format, jfields.tostring);
948
949 format_len = (*env)->GetStringUTFLength(env, format_string);
950 format_ptr = (*env)->GetStringUTFChars(env, format_string, NULL);
951 msg_Dbg(api->p_obj, "output format changed: %.*s", format_len,
952 format_ptr);
953 (*env)->ReleaseStringUTFChars(env, format_string, format_ptr);
954
955 p_out->type = MC_OUT_TYPE_CONF;
956 p_out->b_eos = false;
957 if (api->i_cat == VIDEO_ES)
958 {
959 p_out->conf.video.width = GET_INTEGER(format, "width");
960 p_out->conf.video.height = GET_INTEGER(format, "height");
961 p_out->conf.video.stride = GET_INTEGER(format, "stride");
962 p_out->conf.video.slice_height = GET_INTEGER(format, "slice-height");
963 p_out->conf.video.pixel_format = GET_INTEGER(format, "color-format");
964 p_out->conf.video.crop_left = GET_INTEGER(format, "crop-left");
965 p_out->conf.video.crop_top = GET_INTEGER(format, "crop-top");
966 p_out->conf.video.crop_right = GET_INTEGER(format, "crop-right");
967 p_out->conf.video.crop_bottom = GET_INTEGER(format, "crop-bottom");
968 }
969 else
970 {
971 p_out->conf.audio.channel_count = GET_INTEGER(format, "channel-count");
972 p_out->conf.audio.channel_mask = GET_INTEGER(format, "channel-mask");
973 p_out->conf.audio.sample_rate = GET_INTEGER(format, "sample-rate");
974 }
975
976 (*env)->DeleteLocalRef(env, format);
977 return 1;
978 }
979 else if (i_index == MC_API_INFO_OUTPUT_BUFFERS_CHANGED)
980 {
981 jobject joutput_buffers;
982
983 msg_Dbg(api->p_obj, "output buffers changed");
984 if (!jfields.get_output_buffers)
985 return 0;
986 (*env)->DeleteGlobalRef(env, p_sys->output_buffers);
987
988 joutput_buffers = (*env)->CallObjectMethod(env, p_sys->codec,
989 jfields.get_output_buffers);
990 if (CHECK_EXCEPTION())
991 {
992 msg_Err(api->p_obj, "Exception in MediaCodec.getOutputBuffer");
993 p_sys->output_buffers = NULL;
994 return MC_API_ERROR;
995 }
996 p_sys->output_buffers = (*env)->NewGlobalRef(env, joutput_buffers);
997 (*env)->DeleteLocalRef(env, joutput_buffers);
998 }
999 return 0;
1000 }
1001
1002 /*****************************************************************************
1003 * ReleaseOutput
1004 *****************************************************************************/
ReleaseOutput(mc_api * api,int i_index,bool b_render)1005 static int ReleaseOutput(mc_api *api, int i_index, bool b_render)
1006 {
1007 mc_api_sys *p_sys = api->p_sys;
1008 JNIEnv *env;
1009
1010 assert(i_index >= 0);
1011
1012 GET_ENV();
1013
1014 (*env)->CallVoidMethod(env, p_sys->codec, jfields.release_output_buffer,
1015 i_index, b_render);
1016 if (CHECK_EXCEPTION())
1017 {
1018 msg_Err(api->p_obj, "Exception in MediaCodec.releaseOutputBuffer");
1019 return MC_API_ERROR;
1020 }
1021 return 0;
1022 }
1023
1024 /*****************************************************************************
1025 * SetOutputSurface
1026 *****************************************************************************/
SetOutputSurface(mc_api * api,void * p_surface,void * p_jsurface)1027 static int SetOutputSurface(mc_api *api, void *p_surface, void *p_jsurface)
1028 {
1029 (void) api; (void) p_surface; (void) p_jsurface;
1030
1031 return MC_API_ERROR;
1032 }
1033
1034 /*****************************************************************************
1035 * Clean
1036 *****************************************************************************/
Clean(mc_api * api)1037 static void Clean(mc_api *api)
1038 {
1039 free(api->psz_name);
1040 free(api->p_sys);
1041 }
1042
1043 /*****************************************************************************
1044 * Configure
1045 *****************************************************************************/
Configure(mc_api * api,int i_profile)1046 static int Configure(mc_api *api, int i_profile)
1047 {
1048 free(api->psz_name);
1049
1050 api->i_quirks = 0;
1051 api->psz_name = MediaCodec_GetName(api->p_obj, api->psz_mime,
1052 i_profile, &api->i_quirks);
1053 if (!api->psz_name)
1054 return MC_API_ERROR;
1055 api->i_quirks |= OMXCodec_GetQuirks(api->i_cat, api->i_codec, api->psz_name,
1056 strlen(api->psz_name));
1057
1058 /* Allow interlaced picture after API 21 */
1059 if (jfields.get_input_buffer && jfields.get_output_buffer)
1060 api->i_quirks |= MC_API_VIDEO_QUIRKS_SUPPORT_INTERLACED;
1061 return 0;
1062 }
1063
1064 /*****************************************************************************
1065 * MediaCodecJni_New
1066 *****************************************************************************/
MediaCodecJni_Init(mc_api * api)1067 int MediaCodecJni_Init(mc_api *api)
1068 {
1069 JNIEnv *env;
1070
1071 GET_ENV();
1072
1073 if (!InitJNIFields(api->p_obj, env))
1074 return MC_API_ERROR;
1075
1076 api->p_sys = calloc(1, sizeof(mc_api_sys));
1077 if (!api->p_sys)
1078 return MC_API_ERROR;
1079
1080 api->clean = Clean;
1081 api->configure = Configure;
1082 api->start = Start;
1083 api->stop = Stop;
1084 api->flush = Flush;
1085 api->dequeue_in = DequeueInput;
1086 api->queue_in = QueueInput;
1087 api->dequeue_out = DequeueOutput;
1088 api->get_out = GetOutput;
1089 api->release_out = ReleaseOutput;
1090 api->release_out_ts = NULL;
1091 api->set_output_surface = SetOutputSurface;
1092
1093 /* Allow rotation only after API 21 */
1094 if (jfields.get_input_buffer && jfields.get_output_buffer)
1095 api->b_support_rotation = true;
1096 return 0;
1097 }
1098