From d6d6f3013c61e2d96d33d4cac7235785585eeeed Mon Sep 17 00:00:00 2001 From: Thomas Guillem <thomas@gllm.fr> Date: Mon, 18 May 2015 18:25:35 +0200 Subject: [PATCH] mediacodec: Open/Close: separate JNI part from Decoder part Rename OpenMediaCodec to StartMediaCodec and CloseMediaCodec to StopMediaCodec. --- modules/codec/omxil/mediacodec.c | 334 ++++++++++++++++--------------- 1 file changed, 173 insertions(+), 161 deletions(-) diff --git a/modules/codec/omxil/mediacodec.c b/modules/codec/omxil/mediacodec.c index af2dfec642a8..cb1500e45b61 100644 --- a/modules/codec/omxil/mediacodec.c +++ b/modules/codec/omxil/mediacodec.c @@ -150,6 +150,7 @@ struct decoder_sys_t jobject input_buffers, output_buffers; int pixel_format; int stride, slice_height; + const char *mime; char *name; /* Codec Specific Data buffer: sent in PutInput after a start or a flush @@ -278,7 +279,7 @@ static const struct member members[] = { *****************************************************************************/ static int OpenDecoder(vlc_object_t *); static void CloseDecoder(vlc_object_t *); -static void CloseMediaCodec(decoder_t *p_dec, JNIEnv *env); +static void JniStopMediaCodec(decoder_t *p_dec, JNIEnv *env); static picture_t *DecodeVideo(decoder_t *, block_t **); @@ -538,16 +539,13 @@ static int H264SetCSD(decoder_t *p_dec, void *p_buf, size_t i_size, } static jstring GetMediaCodecName(decoder_t *p_dec, JNIEnv *env, - const char *mime, jstring jmime) + const char *mime, jstring jmime, + size_t h264_profile) { decoder_sys_t *p_sys = p_dec->p_sys; int num_codecs; - size_t fmt_profile = 0; jstring jcodec_name = NULL; - if (p_dec->fmt_in.i_codec == VLC_CODEC_H264) - h264_get_profile_level(&p_dec->fmt_in, &fmt_profile, NULL, NULL); - num_codecs = (*env)->CallStaticIntMethod(env, jfields.media_codec_list_class, jfields.get_codec_count); @@ -600,7 +598,7 @@ static jstring GetMediaCodecName(decoder_t *p_dec, JNIEnv *env, /* The mime type is matching for this component. We now check if the capabilities of the codec is matching the video format. */ - if (p_dec->fmt_in.i_codec == VLC_CODEC_H264 && fmt_profile) { + if (h264_profile) { /* This decoder doesn't expose its profiles and is high * profile capable */ if (!strncmp(name_ptr, "OMX.LUMEVideoDecoder", __MIN(20, name_len))) @@ -612,7 +610,7 @@ static jstring GetMediaCodecName(decoder_t *p_dec, JNIEnv *env, int omx_profile = (*env)->GetIntField(env, profile_level, jfields.profile_field); size_t codec_profile = convert_omx_to_profile_idc(omx_profile); (*env)->DeleteLocalRef(env, profile_level); - if (codec_profile != fmt_profile) + if (codec_profile != h264_profile) continue; /* Some encoders set the level too high, thus we ignore it for the moment. We could try to guess the actual profile based on the resolution. */ @@ -652,13 +650,11 @@ loopclean: return jcodec_name; } -/***************************************************************************** - * OpenMediaCodec: Create the mediacodec instance - *****************************************************************************/ -static int OpenMediaCodec(decoder_t *p_dec, JNIEnv *env) +static int JniStartMediaCodec(decoder_t *p_dec, JNIEnv *env, jobject jsurface, + const char *mime, int i_width, int i_height, + size_t h264_profile, int i_angle) { decoder_sys_t *p_sys = p_dec->p_sys; - const char *mime = NULL; int i_ret = VLC_EGENERIC; jstring jmime = NULL; jstring jcodec_name = NULL; @@ -669,24 +665,11 @@ static int OpenMediaCodec(decoder_t *p_dec, JNIEnv *env) jobject joutput_buffers = NULL; jobject jbuffer_info = NULL; - switch (p_dec->fmt_in.i_codec) { - case VLC_CODEC_HEVC: mime = "video/hevc"; break; - case VLC_CODEC_H264: mime = "video/avc"; break; - case VLC_CODEC_H263: mime = "video/3gpp"; break; - case VLC_CODEC_MP4V: mime = "video/mp4v-es"; break; - case VLC_CODEC_WMV3: mime = "video/x-ms-wmv"; break; - case VLC_CODEC_VC1: mime = "video/wvc1"; break; - case VLC_CODEC_VP8: mime = "video/x-vnd.on2.vp8"; break; - case VLC_CODEC_VP9: mime = "video/x-vnd.on2.vp9"; break; - default: - vlc_assert_unreachable(); - } - jmime = (*env)->NewStringUTF(env, mime); if (!jmime) return VLC_EGENERIC; - jcodec_name = GetMediaCodecName(p_dec, env, mime, jmime); + jcodec_name = GetMediaCodecName(p_dec, env, mime, jmime, h264_profile); if (!jcodec_name) { msg_Dbg(p_dec, "No suitable codec matching %s was found", mime); goto error; @@ -705,122 +688,36 @@ static int OpenMediaCodec(decoder_t *p_dec, JNIEnv *env) p_sys->allocated = true; p_sys->codec = (*env)->NewGlobalRef(env, jcodec); - /* Either we use a "csd-0" buffer that is provided before codec - * initialisation via the MediaFormat class, or use a CODEC_CONFIG buffer - * that can be provided during playback (and must be provided after a flush - * and a start). */ - if (p_dec->fmt_in.i_extra && !p_sys->p_csd) - { - if (p_dec->fmt_in.i_codec == VLC_CODEC_H264 - || p_dec->fmt_in.i_codec == VLC_CODEC_HEVC) - { - int buf_size = p_dec->fmt_in.i_extra + 20; - uint32_t size = p_dec->fmt_in.i_extra; - void *p_buf = malloc(buf_size); - - if (!p_buf) - { - msg_Warn(p_dec, "extra buffer allocation failed"); - goto error; - } - - if (p_dec->fmt_in.i_codec == VLC_CODEC_H264) - { - if (((uint8_t*)p_dec->fmt_in.p_extra)[0] == 1 - && convert_sps_pps(p_dec, p_dec->fmt_in.p_extra, - p_dec->fmt_in.i_extra, - p_buf, buf_size, &size, - &p_sys->nal_size) == VLC_SUCCESS) - H264SetCSD(p_dec, p_buf, size, NULL); - } else - { - if (convert_hevc_nal_units(p_dec, p_dec->fmt_in.p_extra, - p_dec->fmt_in.i_extra, - p_buf, buf_size, &size, - &p_sys->nal_size) == VLC_SUCCESS) - { - struct csd csd; - - csd.p_buf = p_buf; - csd.i_size = size; - CSDDup(p_dec, &csd, 1); - } - } - free(p_buf); - } - if (!p_sys->p_csd) - { - struct csd csd; - - csd.p_buf = p_dec->fmt_in.p_extra; - csd.i_size = p_dec->fmt_in.i_extra; - CSDDup(p_dec, &csd, 1); - } - - p_sys->i_csd_send = 0; - } - - if (!p_sys->i_width && !p_sys->i_height) - { - msg_Err(p_dec, "invalid size, abort MediaCodec"); - goto error; - } jformat = (*env)->CallStaticObjectMethod(env, jfields.media_format_class, jfields.create_video_format, jmime, - p_sys->i_width, p_sys->i_height); + i_width, i_height); - p_sys->direct_rendering = var_InheritBool(p_dec, CFG_PREFIX "dr"); + p_sys->direct_rendering = !!jsurface; /* There is no way to rotate the video using direct rendering (and using a * SurfaceView) before API 21 (Lollipop). Therefore, we deactivate direct * rendering if video doesn't have a normal rotation and if * get_input_buffer method is not present (This method exists since API * 21). */ - if (p_sys->direct_rendering - && p_dec->fmt_in.video.orientation != ORIENT_NORMAL - && !jfields.get_input_buffer) + if (p_sys->direct_rendering && i_angle != 0 && !jfields.get_input_buffer) p_sys->direct_rendering = false; if (p_sys->direct_rendering) { - if (p_dec->fmt_in.video.orientation != ORIENT_NORMAL) { - int i_angle; - - switch (p_dec->fmt_in.video.orientation) { - case ORIENT_ROTATED_90: - i_angle = 90; - break; - case ORIENT_ROTATED_180: - i_angle = 180; - break; - case ORIENT_ROTATED_270: - i_angle = 270; - break; - default: - i_angle = 0; - } + if (i_angle != 0) + { jrotation_string = (*env)->NewStringUTF(env, "rotation-degrees"); (*env)->CallVoidMethod(env, jformat, jfields.set_integer, jrotation_string, i_angle); } - jobject surf = jni_LockAndGetAndroidJavaSurface(); - if (surf) { - // Configure MediaCodec with the Android surface. - (*env)->CallVoidMethod(env, p_sys->codec, jfields.configure, - jformat, surf, NULL, 0); - if (CHECK_EXCEPTION()) { - msg_Warn(p_dec, "Exception occurred in MediaCodec.configure with an output surface."); - jni_UnlockAndroidSurface(); - goto error; - } - p_dec->fmt_out.i_codec = VLC_CODEC_ANDROID_OPAQUE; - jni_UnlockAndroidSurface(); - } else { - msg_Warn(p_dec, "Failed to get the Android Surface, disabling direct rendering."); - p_sys->direct_rendering = false; + // Configure MediaCodec with the Android surface. + (*env)->CallVoidMethod(env, p_sys->codec, jfields.configure, + jformat, jsurface, NULL, 0); + if (CHECK_EXCEPTION()) { + msg_Warn(p_dec, "Exception occurred in MediaCodec.configure with an output surface."); + goto error; } - } - if (!p_sys->direct_rendering) { + } else { (*env)->CallVoidMethod(env, p_sys->codec, jfields.configure, jformat, NULL, NULL, 0); if (CHECK_EXCEPTION()) { @@ -857,7 +754,6 @@ static int OpenMediaCodec(decoder_t *p_dec, JNIEnv *env) jbuffer_info = (*env)->NewObject(env, jfields.buffer_info_class, jfields.buffer_info_ctor); p_sys->buffer_info = (*env)->NewGlobalRef(env, jbuffer_info); - p_sys->b_update_format = true; i_ret = VLC_SUCCESS; @@ -880,30 +776,119 @@ error: (*env)->DeleteLocalRef(env, jbuffer_info); if (i_ret != VLC_SUCCESS) - CloseMediaCodec(p_dec, env); + JniStopMediaCodec(p_dec, env); return i_ret; } /***************************************************************************** - * CloseMediaCodec: Close the mediacodec instance + * StartMediaCodec: Create the mediacodec instance *****************************************************************************/ -static void CloseMediaCodec(decoder_t *p_dec, JNIEnv *env) +static int StartMediaCodec(decoder_t *p_dec, JNIEnv *env) { decoder_sys_t *p_sys = p_dec->p_sys; + int i_angle = 0, i_ret; + size_t h264_profile = 0; + jobject jsurface = NULL; - if (!p_sys) - return; + if (p_dec->fmt_in.i_extra && !p_sys->p_csd) + { + if (p_dec->fmt_in.i_codec == VLC_CODEC_H264 + || p_dec->fmt_in.i_codec == VLC_CODEC_HEVC) + { + int buf_size = p_dec->fmt_in.i_extra + 20; + uint32_t size = p_dec->fmt_in.i_extra; + void *p_buf = malloc(buf_size); - free(p_sys->name); - p_sys->name = NULL; + if (!p_buf) + { + msg_Warn(p_dec, "extra buffer allocation failed"); + return VLC_EGENERIC; + } - /* Invalidate all pictures that are currently in flight in order - * to prevent the vout from using destroyed output buffers. */ - if (p_sys->direct_rendering) { - InvalidateAllPictures(p_dec); - p_sys->direct_rendering = false; + if (p_dec->fmt_in.i_codec == VLC_CODEC_H264) + { + if (((uint8_t*)p_dec->fmt_in.p_extra)[0] == 1 + && convert_sps_pps(p_dec, p_dec->fmt_in.p_extra, + p_dec->fmt_in.i_extra, + p_buf, buf_size, &size, + &p_sys->nal_size) == VLC_SUCCESS) + H264SetCSD(p_dec, p_buf, size, NULL); + } else + { + if (convert_hevc_nal_units(p_dec, p_dec->fmt_in.p_extra, + p_dec->fmt_in.i_extra, + p_buf, buf_size, &size, + &p_sys->nal_size) == VLC_SUCCESS) + { + struct csd csd; + + csd.p_buf = p_buf; + csd.i_size = size; + CSDDup(p_dec, &csd, 1); + } + } + free(p_buf); + } + if (!p_sys->p_csd) + { + struct csd csd; + + csd.p_buf = p_dec->fmt_in.p_extra; + csd.i_size = p_dec->fmt_in.i_extra; + CSDDup(p_dec, &csd, 1); + } + + p_sys->i_csd_send = 0; + } + if (!p_sys->i_width || !p_sys->i_height) + { + msg_Err(p_dec, "invalid size, abort MediaCodec"); + return VLC_EGENERIC; + } + + if ( p_dec->fmt_in.video.orientation != ORIENT_NORMAL) + { + switch (p_dec->fmt_in.video.orientation) + { + case ORIENT_ROTATED_90: + i_angle = 90; + break; + case ORIENT_ROTATED_180: + i_angle = 180; + break; + case ORIENT_ROTATED_270: + i_angle = 270; + break; + default: + i_angle = 0; + } } + if (p_dec->fmt_in.i_codec == VLC_CODEC_H264) + h264_get_profile_level(&p_dec->fmt_in, &h264_profile, NULL, NULL); + + if (var_InheritBool(p_dec, CFG_PREFIX "dr")) + jsurface = jni_LockAndGetAndroidJavaSurface(); + i_ret = JniStartMediaCodec(p_dec, env, jsurface, p_sys->mime, + p_sys->i_width, p_sys->i_height, h264_profile, + i_angle); + if (jsurface) + jni_UnlockAndroidSurface(); + + if (i_ret == VLC_SUCCESS) + { + if (p_sys->direct_rendering) + p_dec->fmt_out.i_codec = VLC_CODEC_ANDROID_OPAQUE; + p_sys->b_update_format = true; + return VLC_SUCCESS; + } else + return VLC_EGENERIC; +} + +static void JniStopMediaCodec(decoder_t *p_dec, JNIEnv *env) +{ + decoder_sys_t *p_sys = p_dec->p_sys; + if (p_sys->input_buffers) { (*env)->DeleteGlobalRef(env, p_sys->input_buffers); p_sys->input_buffers = NULL; @@ -936,6 +921,26 @@ static void CloseMediaCodec(decoder_t *p_dec, JNIEnv *env) } } +/***************************************************************************** + * StopMediaCodec: Close the mediacodec instance + *****************************************************************************/ +static void StopMediaCodec(decoder_t *p_dec, JNIEnv *env) +{ + decoder_sys_t *p_sys = p_dec->p_sys; + + free(p_sys->name); + p_sys->name = NULL; + + /* Invalidate all pictures that are currently in flight in order + * to prevent the vout from using destroyed output buffers. */ + if (p_sys->direct_rendering) { + InvalidateAllPictures(p_dec); + p_sys->direct_rendering = false; + } + + JniStopMediaCodec(p_dec, env); +} + /***************************************************************************** * OpenDecoder: Create the decoder instance *****************************************************************************/ @@ -943,30 +948,37 @@ static int OpenDecoder(vlc_object_t *p_this) { decoder_t *p_dec = (decoder_t*)p_this; JNIEnv* env = NULL; + const char *mime; if (p_dec->fmt_in.i_cat != VIDEO_ES && !p_dec->b_force) return VLC_EGENERIC; switch (p_dec->fmt_in.i_codec) { - case VLC_CODEC_H264: - /* We can handle h264 without a valid video size */ - break; - case VLC_CODEC_HEVC: - case VLC_CODEC_H263: - case VLC_CODEC_MP4V: - case VLC_CODEC_WMV3: - case VLC_CODEC_VC1: - case VLC_CODEC_VP8: - case VLC_CODEC_VP9: - if (p_dec->fmt_in.video.i_width && p_dec->fmt_in.video.i_height) - break; + case VLC_CODEC_HEVC: mime = "video/hevc"; break; + case VLC_CODEC_H264: mime = "video/avc"; break; + case VLC_CODEC_H263: mime = "video/3gpp"; break; + case VLC_CODEC_MP4V: mime = "video/mp4v-es"; break; + case VLC_CODEC_WMV3: mime = "video/x-ms-wmv"; break; + case VLC_CODEC_VC1: mime = "video/wvc1"; break; + case VLC_CODEC_VP8: mime = "video/x-vnd.on2.vp8"; break; + case VLC_CODEC_VP9: mime = "video/x-vnd.on2.vp9"; break; default: - msg_Dbg(p_dec, "codec %4.4s or resolution (%dx%d) not supported", - (char *)&p_dec->fmt_in.i_codec, - p_dec->fmt_in.video.i_width, p_dec->fmt_in.video.i_height); + msg_Dbg(p_dec, "codec %4.4s not supported", + (char *)&p_dec->fmt_in.i_codec); return VLC_EGENERIC; } + if (!p_dec->fmt_in.video.i_width || !p_dec->fmt_in.video.i_height) + { + /* We can handle h264 without a valid video size */ + if (p_dec->fmt_in.i_codec != VLC_CODEC_H264) + { + msg_Dbg(p_dec, "resolution (%dx%d) not supported", + p_dec->fmt_in.video.i_width, p_dec->fmt_in.video.i_height); + return VLC_EGENERIC; + } + } + if (!(env = jni_get_env(THREAD_NAME))) goto error; @@ -991,6 +1003,7 @@ static int OpenDecoder(vlc_object_t *p_this) if (!p_dec->p_sys->timestamp_fifo) goto error; + p_dec->p_sys->mime = mime; p_dec->p_sys->b_new_block = true; switch (p_dec->fmt_in.i_codec) @@ -1011,7 +1024,7 @@ static int OpenDecoder(vlc_object_t *p_this) } break; } - return OpenMediaCodec(p_dec, env); + return StartMediaCodec(p_dec, env); error: CloseDecoder(p_this); @@ -1031,12 +1044,11 @@ static void CloseDecoder(vlc_object_t *p_this) return; if ((env = jni_get_env(THREAD_NAME))) - CloseMediaCodec(p_dec, env); + StopMediaCodec(p_dec, env); else msg_Warn(p_dec, "Can't get a JNIEnv, can't close mediacodec !"); CSDFree(p_dec); - free(p_sys->name); ArchitectureSpecificCopyHooksDestroy(p_sys->pixel_format, &p_sys->architecture_specific_data); free(p_sys->pp_inflight_pictures); if (p_sys->timestamp_fifo) @@ -1422,7 +1434,7 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block) int i_output_ret = 0; int i_input_ret = 0; bool b_error = false; - bool b_delayed_open = false; + bool b_delayed_start = false; bool b_new_block = p_block ? p_sys->b_new_block : false; if (p_sys->error_state) @@ -1464,7 +1476,7 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block) { msg_Err(p_dec, "SPS/PPS changed during playback and " "video size are different. Restart it !"); - CloseMediaCodec(p_dec, env); + StopMediaCodec(p_dec, env); } else { msg_Err(p_dec, "SPS/PPS changed during playback. Flush it"); @@ -1473,7 +1485,7 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block) } } if (!p_sys->codec) - b_delayed_open = true; + b_delayed_start = true; } /* try delayed opening if there is a new extra data */ @@ -1483,11 +1495,11 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block) { case VLC_CODEC_VC1: if (p_dec->fmt_in.i_extra) - b_delayed_open = true; + b_delayed_start = true; default: break; } - if (b_delayed_open && OpenMediaCodec(p_dec, env) != VLC_SUCCESS) + if (b_delayed_start && StartMediaCodec(p_dec, env) != VLC_SUCCESS) { b_error = true; goto endclean; -- GitLab