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