From a681da56b5b4463adc37b83a24c3573bad869a03 Mon Sep 17 00:00:00 2001
From: Tanguy Rozier <trozier@freebox.fr>
Date: Tue, 2 Sep 2025 15:11:29 +0200
Subject: [PATCH 3/6] mp4toannexb: patch cenc subsamples offsets while parsing


diff --git a/libavcodec/bsf/h264_mp4toannexb.c b/libavcodec/bsf/h264_mp4toannexb.c
index 36b09b4dd6..262b85ee4c 100644
--- a/libavcodec/bsf/h264_mp4toannexb.c
+++ b/libavcodec/bsf/h264_mp4toannexb.c
@@ -30,6 +30,7 @@
 #include "bytestream.h"
 #include "defs.h"
 #include "h264.h"
+#include "record.h"
 #include "sei.h"
 
 typedef struct H264BSFContext {
@@ -53,7 +54,8 @@ enum PsSource {
 };
 
 static void count_or_copy(uint8_t **out, uint64_t *out_size,
-                          const uint8_t *in, int in_size, enum PsSource ps, int copy)
+                          const uint8_t *in, int in_size, enum PsSource ps, int copy,
+                          AVBSFContext *ctx, struct record *r, int insert)
 {
     uint8_t start_code_size;
 
@@ -79,6 +81,14 @@ static void count_or_copy(uint8_t **out, uint64_t *out_size,
         *out  += start_code_size + in_size;
     }
     *out_size += start_code_size + in_size;
+
+    if (copy) {
+        record_insert(ctx, r, start_code_size);
+        if (insert)
+            record_insert(ctx, r, in_size);
+        else
+            record_copy(ctx, r, in_size);
+    }
 }
 
 static int h264_extradata_to_annexb(AVBSFContext *ctx,
@@ -286,6 +296,7 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt)
     int ret;
     size_t extradata_size;
     uint8_t *extradata;
+    struct record r;
 
     ret = ff_bsf_get_packet(ctx, &in);
     if (ret < 0)
@@ -308,6 +319,8 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt)
         return 0;
     }
 
+    record_start(ctx, &r, in);
+
     buf_end  = in->data + in->size;
     ret = h264_mp4toannexb_filter_ps(s, in->data, buf_end);
     if (ret < 0)
@@ -333,6 +346,9 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt)
 
             buf += s->length_size;
 
+            if (j)
+                record_skip(ctx, &r, s->length_size);
+
             /* This check requires the cast as the right side might
              * otherwise be promoted to an unsigned value. */
             if ((int64_t)nal_size > buf_end - buf) {
@@ -354,7 +370,7 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt)
                     if (!s->sps_size) {
                         LOG_ONCE(ctx, AV_LOG_WARNING, "SPS not present in the stream, nor in AVCC, stream may be unreadable\n");
                     } else {
-                        count_or_copy(&out, &out_size, s->sps, s->sps_size, PS_OUT_OF_BAND, j);
+                        count_or_copy(&out, &out_size, s->sps, s->sps_size, PS_OUT_OF_BAND, j, ctx, &r, 1);
                         sps_seen = 1;
                     }
                 }
@@ -371,11 +387,11 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt)
             if (unit_type == H264_NAL_SEI && buf[1] == SEI_TYPE_BUFFERING_PERIOD &&
                 !sps_seen && !pps_seen) {
                 if (s->sps_size) {
-                    count_or_copy(&out, &out_size, s->sps, s->sps_size, PS_OUT_OF_BAND, j);
+                    count_or_copy(&out, &out_size, s->sps, s->sps_size, PS_OUT_OF_BAND, j, ctx, &r, 1);
                     sps_seen = 1;
                 }
                 if (s->pps_size) {
-                    count_or_copy(&out, &out_size, s->pps, s->pps_size, PS_OUT_OF_BAND, j);
+                    count_or_copy(&out, &out_size, s->pps, s->pps_size, PS_OUT_OF_BAND, j, ctx, &r, 1);
                     pps_seen = 1;
                 }
             }
@@ -383,16 +399,16 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt)
             /* prepend only to the first type 5 NAL unit of an IDR picture, if no sps/pps are already present */
             if (new_idr && unit_type == H264_NAL_IDR_SLICE && !sps_seen && !pps_seen) {
                 if (s->sps_size)
-                    count_or_copy(&out, &out_size, s->sps, s->sps_size, PS_OUT_OF_BAND, j);
+                    count_or_copy(&out, &out_size, s->sps, s->sps_size, PS_OUT_OF_BAND, j, ctx, &r, 1);
                 if (s->pps_size)
-                    count_or_copy(&out, &out_size, s->pps, s->pps_size, PS_OUT_OF_BAND, j);
+                    count_or_copy(&out, &out_size, s->pps, s->pps_size, PS_OUT_OF_BAND, j, ctx, &r, 1);
                 new_idr = 0;
             /* if only SPS has been seen, also insert PPS */
             } else if (new_idr && unit_type == H264_NAL_IDR_SLICE && sps_seen && !pps_seen) {
                 if (!s->pps_size) {
                     LOG_ONCE(ctx, AV_LOG_WARNING, "PPS not present in the stream, nor in AVCC, stream may be unreadable\n");
                 } else {
-                    count_or_copy(&out, &out_size, s->pps, s->pps_size, PS_OUT_OF_BAND, j);
+                    count_or_copy(&out, &out_size, s->pps, s->pps_size, PS_OUT_OF_BAND, j, ctx, &r, 1);
                 }
             }
 
@@ -400,7 +416,7 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt)
                 ps = PS_IN_BAND;
             else
                 ps = PS_NONE;
-            count_or_copy(&out, &out_size, buf, nal_size, ps, j);
+            count_or_copy(&out, &out_size, buf, nal_size, ps, j, ctx, &r, 0);
             if (unit_type == H264_NAL_SLICE) {
                 new_idr  = 1;
                 sps_seen = 0;
@@ -429,11 +445,14 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt)
     s->idr_sps_seen = sps_seen;
     s->idr_pps_seen = pps_seen;
 
+    record_finish(ctx, &r);
+
     ret = av_packet_copy_props(opkt, in);
     if (ret < 0)
         goto fail;
 
 fail:
+    record_cancel(ctx, &r);
     if (ret < 0)
         av_packet_unref(opkt);
     av_packet_free(&in);
diff --git a/libavcodec/bsf/hevc_mp4toannexb.c b/libavcodec/bsf/hevc_mp4toannexb.c
index 60f38ac414..9dfd6bc3f3 100644
--- a/libavcodec/bsf/hevc_mp4toannexb.c
+++ b/libavcodec/bsf/hevc_mp4toannexb.c
@@ -28,6 +28,7 @@
 #include "bsf_internal.h"
 #include "bytestream.h"
 #include "defs.h"
+#include "record.h"
 
 #include "hevc/hevc.h"
 
@@ -124,6 +125,7 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
     HEVCBSFContext *s = ctx->priv_data;
     AVPacket *in;
     GetByteContext gb;
+    struct record r;
 
     int got_irap = 0;
     int got_ps = 0, seen_irap_ps = 0;
@@ -139,6 +141,8 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
         return 0;
     }
 
+    record_start(ctx, &r, in);
+
     bytestream2_init(&gb, in->data, in->size);
 
     while (!got_irap && bytestream2_get_bytes_left(&gb)) {
@@ -180,6 +184,8 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
         for (i = 0; i < s->length_size; i++)
             nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);
 
+        record_skip(ctx, &r, s->length_size);
+
         if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) {
             ret = AVERROR_INVALIDDATA;
             goto fail;
@@ -207,17 +213,26 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
         if (ret < 0)
             goto fail;
 
-        if (extra_size)
+        if (extra_size) {
             memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size);
+            record_insert(ctx, &r, extra_size);
+        }
+
         AV_WB32(out->data + prev_size + extra_size, 1);
+        record_insert(ctx, &r, 4);
+
         bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, nalu_size);
+        record_copy(ctx, &r, nalu_size);
     }
 
+    record_finish(ctx, &r);
+
     ret = av_packet_copy_props(out, in);
     if (ret < 0)
         goto fail;
 
 fail:
+    record_cancel(ctx, &r);
     if (ret < 0)
         av_packet_unref(out);
     av_packet_free(&in);
diff --git a/libavcodec/record.h b/libavcodec/record.h
new file mode 100644
index 0000000000..e9f2f29fb1
--- /dev/null
+++ b/libavcodec/record.h
@@ -0,0 +1,230 @@
+#ifndef _RECORD_H
+#define _RECORD_H
+
+#include <stdbool.h>
+
+#include "libavutil/encryption_info.h"
+
+struct record {
+	AVPacket *pkt;
+	AVEncryptionInfo *src_info;
+	AVEncryptionInfo *dst_info;
+	size_t src_pos;
+};
+
+static AVEncryptionInfo *get_encryption_info(const AVPacket *pkt)
+{
+	uint8_t *val;
+	size_t len;
+
+	val = av_packet_get_side_data(pkt, AV_PKT_DATA_ENCRYPTION_INFO, &len);
+	if (!val)
+		return NULL;
+
+	return av_encryption_info_get_side_data(val, len);
+}
+
+static void record_start(AVBSFContext *ctx, struct record *r, AVPacket *pkt)
+{
+	memset(r, 0, sizeof(*r));
+
+	r->pkt = pkt;
+
+	r->src_info = get_encryption_info(pkt);
+
+	if (r->src_info && (r->src_info->subsample_count == 0)) {
+		r->src_info->subsample_count = 1;
+		r->src_info->subsamples = av_malloc(sizeof (r->src_info->subsamples[0]));
+		r->src_info->subsamples[0].bytes_of_clear_data = 0;
+		r->src_info->subsamples[0].bytes_of_protected_data = pkt->size;
+	}
+
+	if (r->src_info) {
+		r->dst_info = av_encryption_info_clone(r->src_info);
+
+		av_free(r->dst_info->subsamples);
+		r->dst_info->subsamples = NULL;
+
+		r->dst_info->subsample_count = 0;
+	} else {
+		r->dst_info = NULL;
+	}
+
+	r->src_pos = 0;
+}
+
+static void record_finish(AVBSFContext *ctx, struct record *r)
+{
+	enum AVPacketSideDataType type;
+	AVPacketSideData *side_data;
+	int side_data_elems;
+	uint8_t *data;
+	size_t size;
+	int i;
+
+	if (!r->src_info)
+		goto clean;
+
+	side_data = r->pkt->side_data;
+	side_data_elems = r->pkt->side_data_elems;
+
+	r->pkt->side_data = NULL;
+	r->pkt->side_data_elems = 0;
+
+	for (i = 0; i < side_data_elems; i++) {
+		type = side_data[i].type;
+
+		if (type == AV_PKT_DATA_ENCRYPTION_INFO) {
+			side_data[i].size = 0;
+			av_free(side_data[i].data);
+			side_data[i].data = NULL;
+			data = av_encryption_info_add_side_data(r->dst_info, &size);
+		} else {
+			size = side_data[i].size;
+			data = side_data[i].data;
+		}
+
+		av_packet_add_side_data(r->pkt, type, data, size);
+	}
+
+	av_free(side_data);
+	side_data = NULL;
+
+clean:
+	r->pkt = NULL;
+
+	av_encryption_info_free(r->src_info);
+	r->src_info = NULL;
+
+	av_encryption_info_free(r->dst_info);
+	r->dst_info = NULL;
+
+	r->src_pos = 0;
+
+	memset(r, 0, sizeof(*r));
+}
+
+static void record_cancel(AVBSFContext *ctx, struct record *r)
+{
+	r->pkt = NULL;
+
+	av_encryption_info_free(r->src_info);
+	r->src_info = NULL;
+
+	av_encryption_info_free(r->dst_info);
+	r->dst_info = NULL;
+
+	r->src_pos = 0;
+
+	memset(r, 0, sizeof(*r));
+}
+
+static void record_skip(AVBSFContext *ctx, struct record *r, size_t count)
+{
+	if (!r->src_info)
+		return;
+
+	if (count == 0)
+		return;
+
+	r->src_pos += count;
+}
+
+static void record_append_clear_subsample(AVBSFContext *ctx, struct record *r, size_t count, bool squash)
+{
+	if (count == 0)
+		return;
+
+	if (squash
+	    && (r->dst_info->subsample_count > 0)
+	    && (r->dst_info->subsamples[r->dst_info->subsample_count - 1].bytes_of_protected_data == 0)) {
+		r->dst_info->subsamples[r->dst_info->subsample_count - 1].bytes_of_clear_data += count;
+	} else {
+		r->dst_info->subsamples = av_realloc(r->dst_info->subsamples,
+						     (r->dst_info->subsample_count + 1) * sizeof (r->dst_info->subsamples[0]));
+		r->dst_info->subsamples[r->dst_info->subsample_count].bytes_of_clear_data = count;
+		r->dst_info->subsamples[r->dst_info->subsample_count].bytes_of_protected_data = 0;
+		r->dst_info->subsample_count++;
+	}
+}
+
+static void record_append_protected_subsample(AVBSFContext *ctx, struct record *r, size_t count, bool squash)
+{
+	if (count == 0)
+		return;
+
+	if (squash
+	    && (r->dst_info->subsample_count > 0)) {
+		r->dst_info->subsamples[r->dst_info->subsample_count - 1].bytes_of_protected_data += count;
+	} else {
+		r->dst_info->subsamples = av_realloc(r->dst_info->subsamples,
+						     (r->dst_info->subsample_count + 1) * sizeof (r->dst_info->subsamples[0]));
+		r->dst_info->subsamples[r->dst_info->subsample_count].bytes_of_clear_data = 0;
+		r->dst_info->subsamples[r->dst_info->subsample_count].bytes_of_protected_data = count;
+		r->dst_info->subsample_count++;
+	}
+}
+
+static void record_insert(AVBSFContext *ctx, struct record *r, size_t count)
+{
+	if (!r->src_info)
+		return;
+
+	if (count == 0)
+		return;
+
+	record_append_clear_subsample(ctx, r, count, true);
+}
+
+static void record_copy(AVBSFContext *ctx, struct record *r, size_t count)
+{
+	const AVSubsampleEncryptionInfo *s;
+	AVSubsampleEncryptionInfo c;
+	size_t skip;
+	size_t op;
+
+	if (!r->src_info)
+		return;
+
+	if (count == 0)
+		return;
+
+	skip = r->src_pos;
+
+	for (s = &r->src_info->subsamples[0];
+	     s < &r->src_info->subsamples[r->src_info->subsample_count];
+	     s++) {
+
+		c = *s;
+
+		op = FFMIN(c.bytes_of_clear_data, skip);
+		skip -= op;
+		c.bytes_of_clear_data -= op;
+
+		op = FFMIN(c.bytes_of_protected_data, skip);
+		skip -= op;
+		c.bytes_of_protected_data -= op;
+
+		if (skip > 0)
+			continue;
+
+		op = FFMIN(c.bytes_of_clear_data, count);
+		count -= op;
+		c.bytes_of_clear_data -= op;
+
+		record_append_clear_subsample(ctx, r, op, true);
+		r->src_pos += op;
+
+		op = FFMIN(c.bytes_of_protected_data, count);
+		count -= op;
+		c.bytes_of_protected_data -= op;
+
+		record_append_protected_subsample(ctx, r, op, true);
+		r->src_pos += op;
+
+		if (count == 0)
+			break;
+	}
+}
+
+#endif
-- 
2.51.0

