PR #22550 opened by qianlinluo URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22550 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22550.patch
This PR contains two Android MediaCodec related memory-lifecycle fixes. 1) libavcodec/mediacodec_wrapper.c - Avoid leaking native heap in mediaformat_jni_setBuffer by replacing NewDirectByteBuffer over av_malloc-backed memory with ByteBuffer.wrap over a Java byte[]. - Improve mediaformat_jni_getBuffer by handling non-direct ByteBuffer safely (duplicate + remaining + get(byte[])), then copying into av_malloc output. 2) libavcodec/mediacodecdec.c - In hevc_set_extradata(), release SEI state parsed by ff_hevc_decode_extradata() via ff_hevc_reset_sei(&sei) on the done path. Rationale - setBuffer previously allocated native memory that had no guaranteed free path from JNI side. - getBuffer previously depended on direct buffers only. - hevc_set_extradata missed SEI cleanup on early-exit/success path, potentially retaining unregistered SEI references. Commits - 38016f9095ad16c5f281369c2172e98570b3330f - d3bae221d4376a8740202f030e6d93550ccd091c >From 38016f9095ad16c5f281369c2172e98570b3330f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E4=B9=BE=E6=9E=97?= <[email protected]> Date: Thu, 19 Mar 2026 14:54:31 +0800 Subject: [PATCH 1/2] avcodec/mediacodec_wrapper: avoid leaking setBuffer backing storage --- libavcodec/mediacodec_wrapper.c | 113 +++++++++++++++++++++++++++----- 1 file changed, 97 insertions(+), 16 deletions(-) diff --git a/libavcodec/mediacodec_wrapper.c b/libavcodec/mediacodec_wrapper.c index 283bbe72d6..1aeaa4d4d6 100644 --- a/libavcodec/mediacodec_wrapper.c +++ b/libavcodec/mediacodec_wrapper.c @@ -110,6 +110,12 @@ struct JNIAMediaFormatFields { jmethodID to_string_id; + jclass bytebuffer_class; + jmethodID bytebuffer_wrap_id; + jmethodID bytebuffer_duplicate_id; + jmethodID bytebuffer_remaining_id; + jmethodID bytebuffer_get_id; + }; #define OFFSET(x) offsetof(struct JNIAMediaFormatFields, x) @@ -134,6 +140,12 @@ static const struct FFJniField jni_amediaformat_mapping[] = { { "android/media/MediaFormat", "toString", "()Ljava/lang/String;", FF_JNI_METHOD, OFFSET(to_string_id), 1 }, + { "java/nio/ByteBuffer", NULL, NULL, FF_JNI_CLASS, OFFSET(bytebuffer_class), 1 }, + { "java/nio/ByteBuffer", "wrap", "([B)Ljava/nio/ByteBuffer;", FF_JNI_STATIC_METHOD, OFFSET(bytebuffer_wrap_id), 1 }, + { "java/nio/ByteBuffer", "duplicate", "()Ljava/nio/ByteBuffer;", FF_JNI_METHOD, OFFSET(bytebuffer_duplicate_id), 1 }, + { "java/nio/ByteBuffer", "remaining", "()I", FF_JNI_METHOD, OFFSET(bytebuffer_remaining_id), 1 }, + { "java/nio/ByteBuffer", "get", "([B)Ljava/nio/ByteBuffer;", FF_JNI_METHOD, OFFSET(bytebuffer_get_id), 1 }, + { NULL } }; #undef OFFSET @@ -921,6 +933,8 @@ static int mediaformat_jni_getBuffer(FFAMediaFormat* ctx, const char *name, void av_assert0(format != NULL); JNI_GET_ENV_OR_RETURN(env, format, 0); + *data = NULL; + *size = 0; key = ff_jni_utf_chars_to_jstring(env, name, format); if (!key) { @@ -940,18 +954,80 @@ static int mediaformat_jni_getBuffer(FFAMediaFormat* ctx, const char *name, void goto fail; } - *data = (*env)->GetDirectBufferAddress(env, result); - *size = (*env)->GetDirectBufferCapacity(env, result); + if (result) { + void *src = (*env)->GetDirectBufferAddress(env, result); + jlong src_size = (*env)->GetDirectBufferCapacity(env, result); - if (*data && *size) { - void *src = *data; - *data = av_malloc(*size); - if (!*data) { - ret = 0; - goto fail; + if (src && src_size > 0) { + *size = (size_t)src_size; + *data = av_malloc(*size); + if (!*data) { + ret = 0; + goto fail; + } + memcpy(*data, src, *size); + } else { + jobject duplicate = NULL; + jbyteArray byte_array = NULL; + jint remaining = 0; + + duplicate = (*env)->CallObjectMethod(env, result, format->jfields.bytebuffer_duplicate_id); + if ((ret = ff_jni_exception_check(env, 1, format)) < 0 || !duplicate) { + ret = 0; + goto fail; + } + + remaining = (*env)->CallIntMethod(env, duplicate, format->jfields.bytebuffer_remaining_id); + if ((ret = ff_jni_exception_check(env, 1, format)) < 0 || remaining < 0) { + ret = 0; + (*env)->DeleteLocalRef(env, duplicate); + goto fail; + } + if (!remaining) { + ret = 1; + (*env)->DeleteLocalRef(env, duplicate); + goto fail; + } + + byte_array = (*env)->NewByteArray(env, remaining); + if (!byte_array) { + ret = 0; + (*env)->DeleteLocalRef(env, duplicate); + goto fail; + } + + (*env)->CallObjectMethod(env, duplicate, format->jfields.bytebuffer_get_id, byte_array); + if ((ret = ff_jni_exception_check(env, 1, format)) < 0) { + ret = 0; + (*env)->DeleteLocalRef(env, byte_array); + (*env)->DeleteLocalRef(env, duplicate); + goto fail; + } + + *size = (size_t)remaining; + *data = av_malloc(*size); + if (!*data) { + ret = 0; + (*env)->DeleteLocalRef(env, byte_array); + (*env)->DeleteLocalRef(env, duplicate); + goto fail; + } + + (*env)->GetByteArrayRegion(env, byte_array, 0, remaining, (jbyte *)*data); + if ((ret = ff_jni_exception_check(env, 1, format)) < 0) { + ret = 0; + av_freep(data); + (*env)->DeleteLocalRef(env, byte_array); + (*env)->DeleteLocalRef(env, duplicate); + goto fail; + } + + (*env)->DeleteLocalRef(env, byte_array); + (*env)->DeleteLocalRef(env, duplicate); } - - memcpy(*data, src, *size); + } else { + ret = 0; + goto fail; } ret = 1; @@ -1115,7 +1191,7 @@ static void mediaformat_jni_setBuffer(FFAMediaFormat* ctx, const char* name, voi JNIEnv *env = NULL; jstring key = NULL; jobject buffer = NULL; - void *buffer_data = NULL; + jbyteArray byte_array = NULL; FFAMediaFormatJni *format = (FFAMediaFormatJni *)ctx; av_assert0(format != NULL); @@ -1131,15 +1207,19 @@ static void mediaformat_jni_setBuffer(FFAMediaFormat* ctx, const char* name, voi goto fail; } - buffer_data = av_malloc(size); - if (!buffer_data) { + byte_array = (*env)->NewByteArray(env, (jsize)size); + if (!byte_array) { goto fail; } - memcpy(buffer_data, data, size); + (*env)->SetByteArrayRegion(env, byte_array, 0, (jsize)size, (const jbyte *)data); + if (ff_jni_exception_check(env, 1, format) < 0) { + goto fail; + } - buffer = (*env)->NewDirectByteBuffer(env, buffer_data, size); - if (!buffer) { + buffer = (*env)->CallStaticObjectMethod(env, format->jfields.bytebuffer_class, + format->jfields.bytebuffer_wrap_id, byte_array); + if ((ff_jni_exception_check(env, 1, format) < 0) || !buffer) { goto fail; } @@ -1150,6 +1230,7 @@ static void mediaformat_jni_setBuffer(FFAMediaFormat* ctx, const char* name, voi fail: (*env)->DeleteLocalRef(env, key); + (*env)->DeleteLocalRef(env, byte_array); (*env)->DeleteLocalRef(env, buffer); } -- 2.52.0 >From d3bae221d4376a8740202f030e6d93550ccd091c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E4=B9=BE=E6=9E=97?= <[email protected]> Date: Thu, 19 Mar 2026 14:54:34 +0800 Subject: [PATCH 2/2] avcodec/mediacodecdec: free HEVC SEI parsed from extradata --- libavcodec/mediacodecdec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c index ea9362ba85..814ceddb07 100644 --- a/libavcodec/mediacodecdec.c +++ b/libavcodec/mediacodecdec.c @@ -39,6 +39,7 @@ #include "h264_parse.h" #include "h264_ps.h" #include "hevc/parse.h" +#include "hevc/sei.h" #include "hwconfig.h" #include "internal.h" #include "jni.h" @@ -274,6 +275,7 @@ static int hevc_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format) } done: + ff_hevc_reset_sei(&sei); ff_hevc_ps_uninit(&ps); av_freep(&vps_data); -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
