Commit 6931e21b7e for ffmpeg

commit 6931e21b7e74bc156a32d2c7addfd56e9ab57bf6
Author: Jun Zhao <barryjzhao@tencent.com>
Date:   Sun Oct 4 08:02:12 2026 +0800

    avcodec/hevc_mp4toannexb: avoid duplicating parameter sets when present in-band

    When an input packet already contains in-band VPS/SPS/PPS parameter sets,
    hevc_mp4toannexb still prepended extradata parameter sets before IRAP NALUs,
    causing duplicate parameter sets at keyframes.

    Avoid duplication by indexing extradata parameter sets in a hash table and
    checking in-band parameter sets against it, suppressing any extradata
    parameter set whose payload is already present in-band. Declarative SEIs
    from extradata are always preserved. Packet-side matching attempts are
    bounded to prevent CPU amplification, and redundant linear scans are
    pruned with an emitted_types mask. Topological ordering dependencies
    (e.g. PPS after SPS, and Prefix SEI barrier before pending parameter sets)
    are strictly preserved.

    Extradata is parsed in two passes with a single allocation, capping the
    total number of NAL units to prevent memory amplification from malformed inputs.

    Update the fate-hevc-bsf-mp4toannexb test reference accordingly.

    Fixes: ticket #24637
    Signed-off-by: Jun Zhao <barryjzhao@tencent.com>

diff --git a/libavcodec/bsf/hevc_mp4toannexb.c b/libavcodec/bsf/hevc_mp4toannexb.c
index 79633499b4..345a7ebbc0 100644
--- a/libavcodec/bsf/hevc_mp4toannexb.c
+++ b/libavcodec/bsf/hevc_mp4toannexb.c
@@ -21,6 +21,8 @@

 #include <string.h>

+#include "libavutil/avassert.h"
+#include "libavutil/common.h"
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mem.h"

@@ -32,35 +34,91 @@
 #include "libavcodec/hevc/hevc.h"

 #define MIN_HEVCC_LENGTH 23
+#define MAX_EXTRADATA_NALS 2048
+#define MAX_PS_CHAIN_DEPTH 16
+#define MAX_PS_CHECKS_PER_PACKET 128
+
+/* 32-bit FNV-1a hash algorithm constants */
+#define FNV1A_32_OFFSET_BASIS 2166136261u
+#define FNV1A_32_PRIME        16777619u
+
+static inline uint32_t hash_ps(const uint8_t *data, size_t size)
+{
+    uint32_t h = FNV1A_32_OFFSET_BASIS;
+    for (size_t i = 0; i < size; i++) {
+        h ^= data[i];
+        h *= FNV1A_32_PRIME;
+    }
+    return h;
+}
+
+typedef struct HEVCNALUnit {
+    size_t   offset;
+    size_t   size;
+    int      type;
+    uint32_t hash;
+    int      matched;
+    int      emit;
+    int      next;
+} HEVCNALUnit;

 typedef struct HEVCBSFContext {
-    uint8_t *extradata;
-    size_t   extradata_size;
-    uint8_t  length_size;
-    int      extradata_parsed;
+    uint8_t     *extradata;
+    size_t       extradata_size;
+    HEVCNALUnit *nals;
+    int          nb_nals;
+    int          nb_ps;
+    int         *ps_buckets;
+    int          ps_table_size;
+    uint8_t      length_size;
+    int          extradata_parsed;
+    int          dup_ps_logged;
+    unsigned     emitted_types;
+    int          sps_seen;
 } HEVCBSFContext;

 static int hevc_extradata_to_annexb(AVBSFContext *ctx,
-                                    const uint8_t *extradata, int extradata_size,
+                                    const uint8_t *extradata, size_t extradata_size,
                                     uint8_t **out_extradata, int *out_extradata_size)
 {
     HEVCBSFContext *s = ctx->priv_data;
-    GetByteContext gb;
+    GetByteContext gb, gb_scan;
     int length_size, num_arrays, i, j;
+    int total_nals = 0;
+    size_t total_size = 0;
     int ret = 0;

-    uint8_t *new_extradata = NULL;
-    size_t   new_extradata_size = 0;
+    uint8_t     *new_extradata = NULL;
+    size_t       new_extradata_size = 0;
+    HEVCNALUnit *new_nals = NULL;
+    int          nb_nals = 0;
+    int         *ps_buckets = NULL;
+    int          ps_table_size = 0;
+    int          nb_ps = 0;
+
+    /* bytestream2 and AVCodecParameters.extradata_size are both int. */
+    if (extradata_size > INT_MAX) {
+        av_log(ctx, AV_LOG_ERROR, "Extradata size exceeds INT_MAX\n");
+        return AVERROR_INVALIDDATA;
+    }

-    bytestream2_init(&gb, extradata, extradata_size);
+    bytestream2_init(&gb, extradata, (int)extradata_size);

     bytestream2_skip(&gb, 21);
     length_size = (bytestream2_get_byte(&gb) & 3) + 1;
     num_arrays  = bytestream2_get_byte(&gb);

+    /* First pass: validate hvcC arrays and compute exact size and NAL count upfront */
+    gb_scan = gb;
     for (i = 0; i < num_arrays; i++) {
-        int type = bytestream2_get_byte(&gb) & 0x3f;
-        int cnt  = bytestream2_get_be16(&gb);
+        int type, cnt;
+
+        if (bytestream2_get_bytes_left(&gb_scan) < 3) {
+            ret = AVERROR_INVALIDDATA;
+            goto fail;
+        }
+        type = bytestream2_get_byte(&gb_scan) & 0x3f;
+        cnt  = bytestream2_get_be16(&gb_scan);

         if (!(type == HEVC_NAL_VPS || type == HEVC_NAL_SPS || type == HEVC_NAL_PPS ||
               type == HEVC_NAL_SEI_PREFIX || type == HEVC_NAL_SEI_SUFFIX)) {
@@ -70,30 +128,88 @@ static int hevc_extradata_to_annexb(AVBSFContext *ctx,
             goto fail;
         }

+        if (cnt > MAX_EXTRADATA_NALS - total_nals) {
+            av_log(ctx, AV_LOG_ERROR, "Too many NAL units in extradata: exceeds %d\n",
+                   MAX_EXTRADATA_NALS);
+            ret = AVERROR_INVALIDDATA;
+            goto fail;
+        }
+
         for (j = 0; j < cnt; j++) {
-            const int nalu_len = bytestream2_get_be16(&gb);
+            int nalu_len;

-            if (!nalu_len ||
-                nalu_len > bytestream2_get_bytes_left(&gb) ||
-                4 + AV_INPUT_BUFFER_PADDING_SIZE + nalu_len > SIZE_MAX - new_extradata_size) {
+            if (bytestream2_get_bytes_left(&gb_scan) < 2) {
                 ret = AVERROR_INVALIDDATA;
                 goto fail;
             }
-            ret = av_reallocp(&new_extradata, new_extradata_size + nalu_len + 4 + AV_INPUT_BUFFER_PADDING_SIZE);
-            if (ret < 0)
+            nalu_len = bytestream2_get_be16(&gb_scan);
+
+            if (nalu_len < 2 ||
+                nalu_len > bytestream2_get_bytes_left(&gb_scan) ||
+                4 + AV_INPUT_BUFFER_PADDING_SIZE + nalu_len > SIZE_MAX - total_size) {
+                ret = AVERROR_INVALIDDATA;
                 goto fail;
+            }
+            /* Each hvcC length prefix grows into a 4-byte start code. */
+            if (total_size > (size_t)INT_MAX - 4 - (size_t)nalu_len) {
+                av_log(ctx, AV_LOG_ERROR, "Annex B extradata size exceeds INT_MAX\n");
+                ret = AVERROR_INVALIDDATA;
+                goto fail;
+            }
+            total_size += 4 + nalu_len;
+            total_nals++;
+            bytestream2_skip(&gb_scan, nalu_len);
+        }
+    }
+
+    /* Single allocation for exact extradata buffer and NAL descriptor array */
+    if (total_nals > 0) {
+        new_extradata = av_malloc(total_size + AV_INPUT_BUFFER_PADDING_SIZE);
+        if (!new_extradata) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+        new_nals = av_malloc_array(total_nals, sizeof(*new_nals));
+        if (!new_nals) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+    }
+
+    /* Second pass: copy NAL units and fill descriptor array without reallocations */
+    for (i = 0; i < num_arrays; i++) {
+        int type = bytestream2_get_byte(&gb) & 0x3f;
+        int cnt  = bytestream2_get_be16(&gb);
+
+        for (j = 0; j < cnt; j++) {
+            const int nalu_len = bytestream2_get_be16(&gb);
+
+            new_nals[nb_nals].offset  = new_extradata_size;
+            new_nals[nb_nals].size    = 4 + nalu_len;
+            new_nals[nb_nals].type    = type;
+            new_nals[nb_nals].matched = 0;
+            new_nals[nb_nals].emit    = 1;
+            new_nals[nb_nals].hash    = 0;
+            new_nals[nb_nals].next    = -1;
+            nb_nals++;

             AV_WB32(new_extradata + new_extradata_size, 1); // add the startcode
             bytestream2_get_buffer(&gb, new_extradata + new_extradata_size + 4, nalu_len);
             new_extradata_size += 4 + nalu_len;
-            memset(new_extradata + new_extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
         }
     }
+    if (new_extradata)
+        memset(new_extradata + new_extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);

     if (!new_extradata_size)
         av_log(ctx, AV_LOG_WARNING, "No parameter sets in the extradata\n");

     if (out_extradata && out_extradata_size) {
+        if (new_extradata_size > INT_MAX) {
+            av_log(ctx, AV_LOG_ERROR, "Annex B extradata size exceeds INT_MAX\n");
+            ret = AVERROR_INVALIDDATA;
+            goto fail;
+        }
         av_freep(out_extradata);
         *out_extradata =
             av_malloc(new_extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
@@ -101,21 +217,56 @@ static int hevc_extradata_to_annexb(AVBSFContext *ctx,
             ret = AVERROR(ENOMEM);
             goto fail;
         }
-        *out_extradata_size = new_extradata_size;
+        *out_extradata_size = (int)new_extradata_size;
         memcpy(*out_extradata, new_extradata, new_extradata_size);
         memset(*out_extradata + new_extradata_size, 0,
                AV_INPUT_BUFFER_PADDING_SIZE);
     }

-    av_freep(&s->extradata);
-    s->extradata = new_extradata;
-    s->extradata_size = new_extradata_size;
+    for (i = 0; i < nb_nals; i++) {
+        if (new_nals[i].type >= HEVC_NAL_VPS && new_nals[i].type <= HEVC_NAL_PPS)
+            nb_ps++;
+    }

-    s->length_size = length_size;
+    if (nb_ps > 0) {
+        ps_table_size = 1 << av_ceil_log2(FFMAX(32, nb_ps * 2));
+        ps_buckets = av_malloc_array(ps_table_size, sizeof(*ps_buckets));
+        if (!ps_buckets) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+        for (i = 0; i < ps_table_size; i++)
+            ps_buckets[i] = -1;
+
+        for (i = 0; i < nb_nals; i++) {
+            HEVCNALUnit *nal = &new_nals[i];
+            if (nal->type >= HEVC_NAL_VPS && nal->type <= HEVC_NAL_PPS) {
+                nal->hash = hash_ps(new_extradata + nal->offset + 4, nal->size - 4);
+                int bucket = nal->hash & (ps_table_size - 1);
+                nal->next = ps_buckets[bucket];
+                ps_buckets[bucket] = i;
+            }
+        }
+    }
+
+    av_freep(&s->nals);
+    av_freep(&s->extradata);
+    av_freep(&s->ps_buckets);
+    s->extradata        = new_extradata;
+    s->extradata_size   = new_extradata_size;
+    s->nals             = new_nals;
+    s->nb_nals          = nb_nals;
+    s->nb_ps            = nb_ps;
+    s->ps_buckets       = ps_buckets;
+    s->ps_table_size    = ps_table_size;
+    s->length_size      = length_size;
     s->extradata_parsed = 1;
+    s->dup_ps_logged    = 0;
     return 0;
 fail:
     av_freep(&new_extradata);
+    av_freep(&new_nals);
+    av_freep(&ps_buckets);
     return ret;
 }

@@ -141,16 +292,83 @@ static int hevc_mp4toannexb_init(AVBSFContext *ctx)
     return 0;
 }

+static void emit_extradata_type(HEVCBSFContext *s, uint8_t *dst, size_t *out_offset, int type)
+{
+    unsigned bit = 1U << (type - HEVC_NAL_VPS);
+    if (s->emitted_types & bit)
+        return;
+    s->emitted_types |= bit;
+
+    for (int i = 0; i < s->nb_nals; i++) {
+        HEVCNALUnit *nal = &s->nals[i];
+        if (nal->emit && nal->type == type) {
+            memcpy(dst + *out_offset, s->extradata + nal->offset, nal->size);
+            *out_offset += nal->size;
+            nal->emit = 0;
+            if (type == HEVC_NAL_SPS)
+                s->sps_seen = 1;
+        }
+    }
+}
+
+static void flush_extradata_before(HEVCBSFContext *s, uint8_t *dst, size_t *out_offset,
+                                   int nalu_type, int has_irap)
+{
+    if (!has_irap)
+        return;
+
+    switch (nalu_type) {
+    case HEVC_NAL_AUD:
+        break;
+    case HEVC_NAL_VPS:
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_VPS);
+        break;
+    case HEVC_NAL_SPS:
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_VPS);
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_SPS);
+        break;
+    case HEVC_NAL_PPS:
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_VPS);
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_SPS);
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_PPS);
+        break;
+    case HEVC_NAL_SEI_PREFIX:
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_VPS);
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_SPS);
+        if (s->sps_seen)
+            emit_extradata_type(s, dst, out_offset, HEVC_NAL_PPS);
+        break;
+    case HEVC_NAL_SEI_SUFFIX:
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_VPS);
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_SPS);
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_PPS);
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_SEI_PREFIX);
+        emit_extradata_type(s, dst, out_offset, HEVC_NAL_SEI_SUFFIX);
+        break;
+    default:
+        if (nalu_type <= HEVC_NAL_RSV_VCL31) {
+            emit_extradata_type(s, dst, out_offset, HEVC_NAL_VPS);
+            emit_extradata_type(s, dst, out_offset, HEVC_NAL_SPS);
+            emit_extradata_type(s, dst, out_offset, HEVC_NAL_PPS);
+            emit_extradata_type(s, dst, out_offset, HEVC_NAL_SEI_PREFIX);
+        }
+        break;
+    }
+}
+
 static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
 {
     HEVCBSFContext *s = ctx->priv_data;
     AVPacket *in;
     GetByteContext gb;

-    int got_irap = 0;
-    int got_ps = 0, seen_irap_ps = 0;
+    int got_irap = 0, got_ps = 0, seen_irap_ps = 0;
     int i, ret = 0;
     size_t extradata_size = 0;
+    size_t in_annexb_size = 0;
+    size_t extra_insert_size = 0;
+    size_t total_out_size = 0;
+    size_t out_offset = 0;
     uint8_t *extradata = NULL;

     ret = ff_bsf_get_packet(ctx, &in);
@@ -178,77 +396,146 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)

     bytestream2_init(&gb, in->data, in->size);

-    while (!got_irap && bytestream2_get_bytes_left(&gb)) {
-        uint32_t nalu_size = 0;
-        int      nalu_type;
+    for (i = 0; i < s->nb_nals; i++)
+        s->nals[i].matched = 0;

-        if (bytestream2_get_bytes_left(&gb) < s->length_size) {
-            ret = AVERROR_INVALIDDATA;
-            goto fail;
+    {
+        int unmatched_ps = s->nb_ps;
+        int ps_checks = 0;
+        int max_inband_ps_level = 0;
+
+        while (bytestream2_get_bytes_left(&gb)) {
+            uint32_t nalu_size = 0;
+            int      nalu_type;
+
+            if (bytestream2_get_bytes_left(&gb) < s->length_size) {
+                ret = AVERROR_INVALIDDATA;
+                goto fail;
+            }
+            for (i = 0; i < s->length_size; i++)
+                nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);
+
+            if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) {
+                ret = AVERROR_INVALIDDATA;
+                goto fail;
+            }
+
+            nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f;
+            /* Prefix SEI can flush dependent extradata before later VPS/SPS. */
+            if (!got_irap && nalu_type == HEVC_NAL_SEI_PREFIX)
+                max_inband_ps_level = HEVC_NAL_PPS - HEVC_NAL_VPS;
+            if (!got_irap && nalu_type >= HEVC_NAL_VPS && nalu_type <= HEVC_NAL_PPS) {
+                int inband_level = nalu_type - HEVC_NAL_VPS;
+                if (inband_level < max_inband_ps_level) {
+                    /* Inverted topological order (e.g. PPS before SPS).
+                     * Earlier dependencies cannot be suppressed by this out-of-order PS. */
+                } else {
+                    max_inband_ps_level = inband_level;
+                    if (unmatched_ps > 0 && s->ps_buckets &&
+                        ps_checks < MAX_PS_CHECKS_PER_PACKET) {
+                        uint32_t hash;
+                        int idx, depth = 0;
+
+                        ps_checks++;
+                        hash = hash_ps(gb.buffer, nalu_size);
+                        idx  = s->ps_buckets[hash & (s->ps_table_size - 1)];
+
+                        while (idx >= 0 && depth++ < MAX_PS_CHAIN_DEPTH) {
+                            HEVCNALUnit *nal = &s->nals[idx];
+                            if (nal->type == nalu_type && (nal->size - 4) == nalu_size &&
+                                nal->hash == hash &&
+                                !memcmp(s->extradata + nal->offset + 4, gb.buffer, nalu_size)) {
+                                if (!nal->matched) {
+                                    nal->matched = 1;
+                                    unmatched_ps--;
+                                }
+                            }
+                            idx = nal->next;
+                        }
+                    }
+                }
+            }
+            if (FFMIN(INT_MAX, SIZE_MAX) < 4ULL + nalu_size ||
+                in_annexb_size > INT_MAX - 4 - nalu_size) {
+                ret = AVERROR_INVALIDDATA;
+                goto fail;
+            }
+            in_annexb_size += 4 + nalu_size;
+
+            bytestream2_skip(&gb, nalu_size);
+            got_ps   |= !got_irap && (nalu_type >= HEVC_NAL_VPS && nalu_type <= HEVC_NAL_PPS);
+            got_irap |= nalu_type >= HEVC_NAL_BLA_W_LP &&
+                        nalu_type <= HEVC_NAL_RSV_IRAP_VCL23;
         }
-        for (i = 0; i < s->length_size; i++)
-            nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);
+    }
+    seen_irap_ps = got_irap && got_ps;

-        if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) {
-            ret = AVERROR_INVALIDDATA;
-            goto fail;
+    s->emitted_types = 0;
+    s->sps_seen = 0;
+
+    if (got_irap) {
+        int omitted = 0;
+
+        for (i = 0; i < s->nb_nals; i++) {
+            HEVCNALUnit *nal = &s->nals[i];
+
+            nal->emit = 1;
+            if (seen_irap_ps && nal->matched &&
+                nal->type >= HEVC_NAL_VPS && nal->type <= HEVC_NAL_PPS) {
+                nal->emit = 0;
+                omitted++;
+            }
+
+            if (nal->emit)
+                extra_insert_size += nal->size;
         }
+        if (omitted && !s->dup_ps_logged) {
+            av_log(ctx, AV_LOG_VERBOSE,
+                   "Omitting %d extradata parameter sets already present in-band\n",
+                   omitted);
+            s->dup_ps_logged = 1;
+        }
+    } else {
+        for (i = 0; i < s->nb_nals; i++)
+            s->nals[i].emit = 0;
+    }

-        nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f;
-        bytestream2_skip(&gb, nalu_size);
-        got_irap |= nalu_type >= HEVC_NAL_BLA_W_LP &&
-                    nalu_type <= HEVC_NAL_RSV_IRAP_VCL23;
-        got_ps   |= nalu_type >= HEVC_NAL_VPS && nalu_type <= HEVC_NAL_PPS;
+    if (extra_insert_size > INT_MAX - in_annexb_size) {
+        ret = AVERROR_INVALIDDATA;
+        goto fail;
     }
-    seen_irap_ps = got_irap && got_ps;
-    got_irap = got_ps = 0;
+    total_out_size = in_annexb_size + extra_insert_size;
+
+    ret = av_new_packet(out, total_out_size);
+    if (ret < 0)
+        goto fail;

     bytestream2_init(&gb, in->data, in->size);

     while (bytestream2_get_bytes_left(&gb)) {
         uint32_t nalu_size = 0;
         int      nalu_type;
-        int is_irap, is_ps, add_extradata, extra_size, prev_size;

-        if (bytestream2_get_bytes_left(&gb) < s->length_size) {
-            ret = AVERROR_INVALIDDATA;
-            goto fail;
-        }
         for (i = 0; i < s->length_size; i++)
             nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);

-        if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) {
-            ret = AVERROR_INVALIDDATA;
-            goto fail;
-        }
-
         nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f;

-        /* prepend extradata to IRAP frames */
-        is_irap = nalu_type >= HEVC_NAL_BLA_W_LP &&
-                  nalu_type <= HEVC_NAL_RSV_IRAP_VCL23;
-        is_ps   = nalu_type >= HEVC_NAL_VPS && nalu_type <= HEVC_NAL_PPS && seen_irap_ps;
-        add_extradata = (is_ps || is_irap) && !got_ps && !got_irap;
-        extra_size    = add_extradata * s->extradata_size;
-        got_irap     |= is_irap;
-        got_ps       |= is_ps;
+        flush_extradata_before(s, out->data, &out_offset, nalu_type, got_irap);

-        if (FFMIN(INT_MAX, SIZE_MAX) < 4ULL + nalu_size + extra_size) {
-            ret = AVERROR_INVALIDDATA;
-            goto fail;
-        }
+        if (nalu_type == HEVC_NAL_SPS)
+            s->sps_seen = 1;

-        prev_size = out->size;
+        AV_WB32(out->data + out_offset, 1);
+        out_offset += 4;
+        bytestream2_get_buffer(&gb, out->data + out_offset, nalu_size);
+        out_offset += nalu_size;
+    }

-        ret = av_grow_packet(out, 4 + nalu_size + extra_size);
-        if (ret < 0)
-            goto fail;
+    if (got_irap)
+        emit_extradata_type(s, out->data, &out_offset, HEVC_NAL_SEI_SUFFIX);

-        if (extra_size)
-            memcpy(out->data + prev_size, s->extradata, extra_size);
-        AV_WB32(out->data + prev_size + extra_size, 1);
-        bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, nalu_size);
-    }
+    av_assert1(out_offset == out->size);

     ret = av_packet_copy_props(out, in);
     if (ret < 0)
@@ -265,7 +552,9 @@ fail:
 static void hevc_mp4toannexb_close(AVBSFContext *ctx)
 {
     HEVCBSFContext *s = ctx->priv_data;
+    av_freep(&s->nals);
     av_freep(&s->extradata);
+    av_freep(&s->ps_buckets);
 }

 static const enum AVCodecID codec_ids[] = {
diff --git a/tests/fate/hevc.mak b/tests/fate/hevc.mak
index 10b6cd59ae..ba5242e0d2 100644
--- a/tests/fate/hevc.mak
+++ b/tests/fate/hevc.mak
@@ -233,7 +233,7 @@ FATE_HEVC-$(call DEMMUX, HEVC MOV, MOV HEVC, HEVC_PARSER HEVC_MP4TOANNEXB_BSF EX
 fate-hevc-bsf-mp4toannexb: tests/data/hevc-mp4.mov
 fate-hevc-bsf-mp4toannexb: CMD = md5 -i $(TARGET_PATH)/tests/data/hevc-mp4.mov -c:v copy -fflags +bitexact -f hevc
 fate-hevc-bsf-mp4toannexb: CMP = oneline
-fate-hevc-bsf-mp4toannexb: REF = 73019329ed7f81c24f9af67c34c640c0
+fate-hevc-bsf-mp4toannexb: REF = 7d05a79c7a6665ae22c0043a4d83a811

 FATE_HEVC-$(call DEMMUX, HEVC MOV, MOV HEVC, HEVC_PARSER HEVC_MP4TOANNEXB_BSF EXTRACT_EXTRADATA_BSF HEVC_METADATA_BSF SCALE_FILTER) += fate-hevc-bsf-mp4toannexb-new-extradata
 fate-hevc-bsf-mp4toannexb-new-extradata: CMD = stream_remux mov $(TARGET_SAMPLES)/hevc/extradata-reload-multi-stsd.mov "" hevc "-bsf:v hevc_mp4toannexb,hevc_metadata -map 0:v"
diff --git a/tests/ref/fate/hevc-bsf-mp4toannexb-new-extradata b/tests/ref/fate/hevc-bsf-mp4toannexb-new-extradata
index fc90beafbd..6e4c470007 100644
--- a/tests/ref/fate/hevc-bsf-mp4toannexb-new-extradata
+++ b/tests/ref/fate/hevc-bsf-mp4toannexb-new-extradata
@@ -1,5 +1,5 @@
-ff3e10277c8cb7d8ea202e1e3281ce49 *tests/data/fate/hevc-bsf-mp4toannexb-new-extradata.hevc
-4254 tests/data/fate/hevc-bsf-mp4toannexb-new-extradata.hevc
+7bb822cfd0c92613df2ffd25d162179f *tests/data/fate/hevc-bsf-mp4toannexb-new-extradata.hevc
+4252 tests/data/fate/hevc-bsf-mp4toannexb-new-extradata.hevc
 #tb 0: 1/25
 #media_type 0: video
 #codec_id 0: rawvideo