[vlc-devel] [PATCH 4/7] mediacodec: fix LocalRef leaks
Thomas Guillem
thomas at gllm.fr
Mon Apr 27 15:16:48 CEST 2015
Now that we can open and close MediaCodec from the same thread, we can reach
quickly the limit of 512 LocalRefs.
---
modules/codec/omxil/android_mediacodec.c | 150 ++++++++++++++++++++++---------
1 file changed, 106 insertions(+), 44 deletions(-)
diff --git a/modules/codec/omxil/android_mediacodec.c b/modules/codec/omxil/android_mediacodec.c
index 91bca4b..e7f1d86 100644
--- a/modules/codec/omxil/android_mediacodec.c
+++ b/modules/codec/omxil/android_mediacodec.c
@@ -266,8 +266,6 @@ static const struct member members[] = {
{ NULL, NULL, NULL, 0, 0, false },
};
-#define GET_INTEGER(obj, name) (*env)->CallIntMethod(env, obj, jfields.get_integer, (*env)->NewStringUTF(env, name))
-
/*****************************************************************************
* Local prototypes
*****************************************************************************/
@@ -300,6 +298,16 @@ vlc_module_begin ()
set_callbacks( OpenDecoder, CloseDecoder )
vlc_module_end ()
+static inline int get_integer(JNIEnv *env, jobject obj, const char *psz_name)
+{
+ int i_ret;
+ jstring jname = (*env)->NewStringUTF(env, psz_name);
+ i_ret = (*env)->CallIntMethod(env, obj, jfields.get_integer, jname);
+ (*env)->DeleteLocalRef(env, jname);
+ return i_ret;
+}
+#define GET_INTEGER(obj, name) get_integer(env, obj, name)
+
static int jstrcmp(JNIEnv* env, jobject str, const char* str2)
{
jsize len = (*env)->GetStringUTFLength(env, str);
@@ -433,6 +441,17 @@ static int OpenMediaCodec(decoder_t *p_dec, JNIEnv *env)
decoder_sys_t *p_sys = p_dec->p_sys;
const char *mime = NULL;
size_t fmt_profile = 0;
+ int i_ret = VLC_EGENERIC;
+ jstring jmime = NULL;
+ jstring jcodec_name = NULL;
+ jobject jcodec = NULL;
+ jobject jcsd0_buffer = NULL;
+ jstring jcsd0_string = NULL;
+ jobject jformat = NULL;
+ jstring jrotation_string = NULL;
+ jobject jinput_buffers = NULL;
+ jobject joutput_buffers = NULL;
+ jobject jbuffer_info = NULL;
switch (p_dec->fmt_in.i_codec) {
case VLC_CODEC_HEVC: mime = "video/hevc"; break;
@@ -447,12 +466,15 @@ static int OpenMediaCodec(decoder_t *p_dec, JNIEnv *env)
vlc_assert_unreachable();
}
+ jmime = (*env)->NewStringUTF(env, mime);
+ if (!jmime)
+ return VLC_EGENERIC;
+
if (p_dec->fmt_in.i_codec == VLC_CODEC_H264)
h264_get_profile_level(&p_dec->fmt_in, &fmt_profile, NULL, NULL);
int num_codecs = (*env)->CallStaticIntMethod(env, jfields.media_codec_list_class,
jfields.get_codec_count);
- jobject codec_name = NULL;
for (int i = 0; i < num_codecs; i++) {
jobject codec_capabilities = NULL;
@@ -479,7 +501,7 @@ static int OpenMediaCodec(decoder_t *p_dec, JNIEnv *env)
goto loopclean;
codec_capabilities = (*env)->CallObjectMethod(env, info, jfields.get_capabilities_for_type,
- (*env)->NewStringUTF(env, mime));
+ jmime);
if (CHECK_EXCEPTION()) {
msg_Warn(p_dec, "Exception occurred in MediaCodecInfo.getCapabilitiesForType");
goto loopclean;
@@ -529,11 +551,15 @@ static int OpenMediaCodec(decoder_t *p_dec, JNIEnv *env)
p_sys->name = malloc(name_len + 1);
memcpy(p_sys->name, name_ptr, name_len);
p_sys->name[name_len] = '\0';
- codec_name = name;
+ jcodec_name = name;
}
loopclean:
if (name)
+ {
(*env)->ReleaseStringUTFChars(env, name, name_ptr);
+ if (jcodec_name != name)
+ (*env)->DeleteLocalRef(env, name);
+ }
if (profile_levels)
(*env)->DeleteLocalRef(env, profile_levels);
if (types)
@@ -546,7 +572,7 @@ loopclean:
break;
}
- if (!codec_name) {
+ if (!jcodec_name) {
msg_Dbg(p_dec, "No suitable codec matching %s was found", mime);
goto error;
}
@@ -554,14 +580,15 @@ loopclean:
// This method doesn't handle errors nicely, it crashes if the codec isn't found.
// (The same goes for createDecoderByType.) This is fixed in latest AOSP and in 4.2,
// but not in 4.1 devices.
- p_sys->codec = (*env)->CallStaticObjectMethod(env, jfields.media_codec_class,
- jfields.create_by_codec_name, codec_name);
+ jcodec = (*env)->CallStaticObjectMethod(env, jfields.media_codec_class,
+ jfields.create_by_codec_name,
+ jcodec_name);
if (CHECK_EXCEPTION()) {
msg_Warn(p_dec, "Exception occurred in MediaCodec.createByCodecName.");
goto error;
}
p_sys->allocated = true;
- p_sys->codec = (*env)->NewGlobalRef(env, p_sys->codec);
+ 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
@@ -614,14 +641,12 @@ loopclean:
msg_Err(p_dec, "invalid size, abort MediaCodec");
goto error;
}
- jobject format = (*env)->CallStaticObjectMethod(env, jfields.media_format_class,
- jfields.create_video_format, (*env)->NewStringUTF(env, mime),
- p_sys->i_width, p_sys->i_height);
+ jformat = (*env)->CallStaticObjectMethod(env, jfields.media_format_class,
+ jfields.create_video_format, jmime,
+ p_sys->i_width, p_sys->i_height);
if (p_sys->p_csd0_buffer)
{
- jobject jcsd0_buffer;
-
jcsd0_buffer = (*env)->NewDirectByteBuffer( env,
p_sys->p_csd0_buffer,
p_sys->i_csd0_buffer);
@@ -632,10 +657,9 @@ loopclean:
p_sys->p_csd0_buffer = NULL;
goto error;
}
- (*env)->CallVoidMethod(env, format, jfields.set_bytebuffer,
- (*env)->NewStringUTF(env, "csd-0"),
- jcsd0_buffer);
- (*env)->DeleteLocalRef(env, jcsd0_buffer);
+ jcsd0_string = (*env)->NewStringUTF(env, "csd-0");
+ (*env)->CallVoidMethod(env, jformat, jfields.set_bytebuffer,
+ jcsd0_string, jcsd0_buffer);
}
p_sys->direct_rendering = var_InheritBool(p_dec, CFG_PREFIX "dr");
@@ -667,15 +691,16 @@ loopclean:
default:
i_angle = 0;
}
- (*env)->CallVoidMethod(env, format, jfields.set_integer,
- (*env)->NewStringUTF(env, "rotation-degrees"),
- i_angle);
+ 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, format, surf, NULL, 0);
+ (*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();
@@ -689,7 +714,8 @@ loopclean:
}
}
if (!p_sys->direct_rendering) {
- (*env)->CallVoidMethod(env, p_sys->codec, jfields.configure, format, NULL, NULL, 0);
+ (*env)->CallVoidMethod(env, p_sys->codec, jfields.configure,
+ jformat, NULL, NULL, 0);
if (CHECK_EXCEPTION()) {
msg_Warn(p_dec, "Exception occurred in MediaCodec.configure");
goto error;
@@ -704,28 +730,54 @@ loopclean:
p_sys->started = true;
if (jfields.get_input_buffers && jfields.get_output_buffers) {
- p_sys->input_buffers = (*env)->CallObjectMethod(env, p_sys->codec, jfields.get_input_buffers);
+
+ jinput_buffers = (*env)->CallObjectMethod(env, p_sys->codec,
+ jfields.get_input_buffers);
if (CHECK_EXCEPTION()) {
msg_Err(p_dec, "Exception in MediaCodec.getInputBuffers (OpenDecoder)");
goto error;
}
- p_sys->output_buffers = (*env)->CallObjectMethod(env, p_sys->codec, jfields.get_output_buffers);
+ p_sys->input_buffers = (*env)->NewGlobalRef(env, jinput_buffers);
+
+ joutput_buffers = (*env)->CallObjectMethod(env, p_sys->codec,
+ jfields.get_output_buffers);
if (CHECK_EXCEPTION()) {
msg_Err(p_dec, "Exception in MediaCodec.getOutputBuffers (OpenDecoder)");
goto error;
}
- p_sys->input_buffers = (*env)->NewGlobalRef(env, p_sys->input_buffers);
- p_sys->output_buffers = (*env)->NewGlobalRef(env, p_sys->output_buffers);
+ p_sys->output_buffers = (*env)->NewGlobalRef(env, joutput_buffers);
}
- p_sys->buffer_info = (*env)->NewObject(env, jfields.buffer_info_class, jfields.buffer_info_ctor);
- p_sys->buffer_info = (*env)->NewGlobalRef(env, p_sys->buffer_info);
- (*env)->DeleteLocalRef(env, format);
-
- return VLC_SUCCESS;
-
- error:
- CloseMediaCodec(p_dec, env);
- return VLC_EGENERIC;
+ jbuffer_info = (*env)->NewObject(env, jfields.buffer_info_class,
+ jfields.buffer_info_ctor);
+ p_sys->buffer_info = (*env)->NewGlobalRef(env, jbuffer_info);
+
+ i_ret = VLC_SUCCESS;
+
+error:
+ if (jmime)
+ (*env)->DeleteLocalRef(env, jmime);
+ if (jcodec_name)
+ (*env)->DeleteLocalRef(env, jcodec_name);
+ if (jcodec)
+ (*env)->DeleteLocalRef(env, jcodec);
+ if (jcsd0_buffer)
+ (*env)->DeleteLocalRef(env, jcsd0_buffer);
+ if (jcsd0_string)
+ (*env)->DeleteLocalRef(env, jcsd0_string);
+ if (jformat)
+ (*env)->DeleteLocalRef(env, jformat);
+ if (jrotation_string)
+ (*env)->DeleteLocalRef(env, jrotation_string);
+ if (jinput_buffers)
+ (*env)->DeleteLocalRef(env, jinput_buffers);
+ if (joutput_buffers)
+ (*env)->DeleteLocalRef(env, joutput_buffers);
+ if (jbuffer_info)
+ (*env)->DeleteLocalRef(env, jbuffer_info);
+
+ if (i_ret != VLC_SUCCESS)
+ CloseMediaCodec(p_dec, env);
+ return i_ret;
}
/*****************************************************************************
@@ -1016,6 +1068,7 @@ static int PutInput(decoder_t *p_dec, JNIEnv *env, block_t *p_block, jlong timeo
p_mc_buf = (*env)->GetDirectBufferAddress(env, j_mc_buf);
if (j_mc_size < 0) {
msg_Err(p_dec, "Java buffer has invalid size");
+ (*env)->DeleteLocalRef(env, j_mc_buf);
return -1;
}
if ((size_t) j_mc_size > i_buf)
@@ -1129,31 +1182,39 @@ static int GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t *p_pic, jlong time
}
return 1;
} else if (index == INFO_OUTPUT_BUFFERS_CHANGED) {
+ jobject joutput_buffers;
+
msg_Dbg(p_dec, "output buffers changed");
if (!jfields.get_output_buffers)
return 0;
(*env)->DeleteGlobalRef(env, p_sys->output_buffers);
- p_sys->output_buffers = (*env)->CallObjectMethod(env, p_sys->codec,
- jfields.get_output_buffers);
+ joutput_buffers = (*env)->CallObjectMethod(env, p_sys->codec,
+ jfields.get_output_buffers);
if (CHECK_EXCEPTION()) {
msg_Err(p_dec, "Exception in MediaCodec.getOutputBuffer (GetOutput)");
p_sys->output_buffers = NULL;
return -1;
}
-
- p_sys->output_buffers = (*env)->NewGlobalRef(env, p_sys->output_buffers);
+ p_sys->output_buffers = (*env)->NewGlobalRef(env, joutput_buffers);
+ (*env)->DeleteLocalRef(env, joutput_buffers);
} else if (index == INFO_OUTPUT_FORMAT_CHANGED) {
- jobject format = (*env)->CallObjectMethod(env, p_sys->codec, jfields.get_output_format);
+ jobject format = NULL;
+ jobject format_string = NULL;
+ jsize format_len;
+ const char *format_ptr;
+
+ format = (*env)->CallObjectMethod(env, p_sys->codec,
+ jfields.get_output_format);
if (CHECK_EXCEPTION()) {
msg_Err(p_dec, "Exception in MediaCodec.getOutputFormat (GetOutput)");
return -1;
}
- jobject format_string = (*env)->CallObjectMethod(env, format, jfields.tostring);
+ format_string = (*env)->CallObjectMethod(env, format, jfields.tostring);
- jsize format_len = (*env)->GetStringUTFLength(env, format_string);
- const char *format_ptr = (*env)->GetStringUTFChars(env, format_string, NULL);
+ format_len = (*env)->GetStringUTFLength(env, format_string);
+ format_ptr = (*env)->GetStringUTFChars(env, format_string, NULL);
msg_Dbg(p_dec, "output format changed: %.*s", format_len, format_ptr);
(*env)->ReleaseStringUTFChars(env, format_string, format_ptr);
@@ -1168,6 +1229,7 @@ static int GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t *p_pic, jlong time
int crop_top = GET_INTEGER(format, "crop-top");
int crop_right = GET_INTEGER(format, "crop-right");
int crop_bottom = GET_INTEGER(format, "crop-bottom");
+ (*env)->DeleteLocalRef(env, format);
const char *name = "unknown";
if (!p_sys->direct_rendering) {
--
2.1.4
More information about the vlc-devel
mailing list