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]

Reply via email to