PR #23285 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23285
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23285.patch

Add the same features as the framehash muxer.

This makes it much more useful as it prints important information that some 
tests may need where framehash is not an option given it prints too many lines 
(e.g., samples with tons of frames where timestamps or packet flags are 
irrelevant, and just a single hash is enough).


>From 781d913a104f7cc1a85d9707f90ffcae8cc2daa4 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Sat, 30 May 2026 23:45:10 -0300
Subject: [PATCH 1/2] avcodec/dcadec: map Lw/Rw to FLC/FRC

Some 7.1 DTS files seem to signal Lw/Rw channels that the decoder has been
mapping to SL/SR, despite the macro for the mask being called 7_1_WIDE.
This resulted in said samples reporting the same native layout as actual 7.1
samples with Lsr/Rsr/Lss/Rss (mapped to BL/BR/SL/SR).

If we were to be strict, Lw/Rw would map to WR/WL, but that would result in an
unusual native layout. Instead, lets map them to FLC/FRC, which will result in
the more common 7.1(wide) native layout.

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/dcadec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/dcadec.c b/libavcodec/dcadec.c
index f47a694746..7fbd2c6e3f 100644
--- a/libavcodec/dcadec.c
+++ b/libavcodec/dcadec.c
@@ -41,7 +41,7 @@ int ff_dca_set_channel_layout(AVCodecContext *avctx, int 
*ch_remap, int dca_mask
 
     static const uint8_t dca2wav_wide[28] = {
          2,  0, 1, 4,  5,  3,  8,  4,  5,  9, 10, 6, 7, 12,
-        13, 14, 3, 9, 10, 11, 12, 14, 16, 15, 17, 8, 4,  5,
+        13, 14, 3, 6,  7, 11, 12, 14, 16, 15, 17, 8, 4,  5,
     };
 
     DCAContext *s = avctx->priv_data;
-- 
2.52.0


>From d4ec2cea743fbee350b9281b49a180a70b59c623 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Sun, 31 May 2026 00:01:19 -0300
Subject: [PATCH 2/2] avformat/hashenc: extend output of streamhash muxer

Add the same features as the framehash muxer.

This makes it much more useful as it prints important information that some
tests may need where framehash is not an option given it prints too many lines
(e.g., samples with tons of frames where timestamps or packet flags are
irrelevant, and just a single hash is enough).

Signed-off-by: James Almer <[email protected]>
---
 libavformat/hashenc.c              | 95 +++++++++++++++++-------------
 tests/fate/dca.mak                 |  4 +-
 tests/ref/fate/dca-xll             | 11 +++-
 tests/ref/fate/dca-xll-coded       | 11 +++-
 tests/ref/fate/filter-channelsplit | 18 +++++-
 5 files changed, 93 insertions(+), 46 deletions(-)

diff --git a/libavformat/hashenc.c b/libavformat/hashenc.c
index b108eca41b..4c58536313 100644
--- a/libavformat/hashenc.c
+++ b/libavformat/hashenc.c
@@ -34,6 +34,7 @@ struct HashContext {
     const AVClass *avclass;
     struct AVHashContext **hashes;
     char *hash_name;
+    int64_t *stream_sizes;
     int per_stream;
     int format_version;
 };
@@ -48,6 +49,7 @@ struct HashContext {
 #if CONFIG_HASH_MUXER || CONFIG_STREAMHASH_MUXER
 static const AVOption hash_streamhash_options[] = {
     HASH_OPT("sha256"),
+    FORMAT_VERSION_OPT,
     { NULL },
 };
 
@@ -97,6 +99,13 @@ static int hash_init(struct AVFormatContext *s)
     av_hash_init(c->hashes[0]);
     return 0;
 }
+
+static int hash_write_packet(struct AVFormatContext *s, AVPacket *pkt)
+{
+    struct HashContext *c = s->priv_data;
+    av_hash_update(c->hashes[0], pkt->data, pkt->size);
+    return 0;
+}
 #endif
 
 #if CONFIG_STREAMHASH_MUXER
@@ -108,6 +117,9 @@ static int streamhash_init(struct AVFormatContext *s)
     c->hashes = av_calloc(s->nb_streams, sizeof(*c->hashes));
     if (!c->hashes)
         return AVERROR(ENOMEM);
+    c->stream_sizes = av_calloc(s->nb_streams, sizeof(*c->stream_sizes));
+    if (!c->stream_sizes)
+        return AVERROR(ENOMEM);
     for (i = 0; i < s->nb_streams; i++) {
         res = av_hash_alloc(&c->hashes[i], c->hash_name);
         if (res < 0) {
@@ -117,25 +129,49 @@ static int streamhash_init(struct AVFormatContext *s)
     }
     return 0;
 }
+
+static int streamhash_write_packet(struct AVFormatContext *s, AVPacket *pkt)
+{
+    struct HashContext *c = s->priv_data;
+    av_hash_update(c->hashes[pkt->stream_index], pkt->data, pkt->size);
+    c->stream_sizes[pkt->stream_index] += pkt->size;
+    return 0;
+}
+#endif
+
+#if CONFIG_STREAMHASH_MUXER || CONFIG_FRAMEHASH_MUXER || CONFIG_FRAMEMD5_MUXER
+static void hash_print_extradata(struct AVFormatContext *s)
+{
+    int i;
+
+    for (i = 0; i < s->nb_streams; i++) {
+        AVStream *st = s->streams[i];
+        AVCodecParameters *par = st->codecpar;
+        if (par->extradata) {
+            struct HashContext *c = s->priv_data;
+            char buf[AV_HASH_MAX_SIZE*2+1];
+
+            avio_printf(s->pb, "#extradata %d, %31d, ", i, 
par->extradata_size);
+            av_hash_init(c->hashes[0]);
+            av_hash_update(c->hashes[0], par->extradata, par->extradata_size);
+            av_hash_final_hex(c->hashes[0], buf, sizeof(buf));
+            avio_write(s->pb, buf, strlen(buf));
+            avio_printf(s->pb, "\n");
+        }
+    }
+}
 #endif
 
 #if CONFIG_HASH_MUXER || CONFIG_MD5_MUXER || CONFIG_STREAMHASH_MUXER
-static char get_media_type_char(enum AVMediaType type)
-{
-    switch (type) {
-    case AVMEDIA_TYPE_VIDEO:      return 'v';
-    case AVMEDIA_TYPE_AUDIO:      return 'a';
-    case AVMEDIA_TYPE_DATA:       return 'd';
-    case AVMEDIA_TYPE_SUBTITLE:   return 's';
-    case AVMEDIA_TYPE_ATTACHMENT: return 't';
-    default:                      return '?';
-    }
-}
-
-static int hash_write_packet(struct AVFormatContext *s, AVPacket *pkt)
+static int streamhash_write_header(struct AVFormatContext *s)
 {
     struct HashContext *c = s->priv_data;
-    av_hash_update(c->hashes[c->per_stream ? pkt->stream_index : 0], 
pkt->data, pkt->size);
+    avio_printf(s->pb, "#format: stream checksums\n");
+    avio_printf(s->pb, "#version: %d\n", c->format_version);
+    avio_printf(s->pb, "#hash: %s\n", av_hash_get_name(c->hashes[0]));
+    hash_print_extradata(s);
+    ff_framehash_write_header(s);
+    avio_printf(s->pb, "#stream#, size, hash\n");
     return 0;
 }
 
@@ -146,9 +182,7 @@ static int hash_write_trailer(struct AVFormatContext *s)
     for (int i = 0; i < num_hashes; i++) {
         char buf[AV_HASH_MAX_SIZE*2+128];
         if (c->per_stream) {
-            AVStream *st = s->streams[i];
-            snprintf(buf, sizeof(buf) - 200, "%d,%c,%s=", i, 
get_media_type_char(st->codecpar->codec_type),
-                     av_hash_get_name(c->hashes[i]));
+            snprintf(buf, sizeof(buf) - 200, "%d, %11"PRId64", ", i, 
c->stream_sizes[i]);
         } else {
             snprintf(buf, sizeof(buf) - 200, "%s=", 
av_hash_get_name(c->hashes[i]));
         }
@@ -171,6 +205,7 @@ static void hash_free(struct AVFormatContext *s)
         }
     }
     av_freep(&c->hashes);
+    av_freep(&c->stream_sizes);
 }
 
 #if CONFIG_HASH_MUXER
@@ -222,7 +257,8 @@ const FFOutputFormat ff_streamhash_muxer = {
     .p.audio_codec     = AV_CODEC_ID_PCM_S16LE,
     .p.video_codec     = AV_CODEC_ID_RAWVIDEO,
     .init              = streamhash_init,
-    .write_packet      = hash_write_packet,
+    .write_header      = streamhash_write_header,
+    .write_packet      = streamhash_write_packet,
     .write_trailer     = hash_write_trailer,
     .deinit            = hash_free,
     .p.flags           = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT |
@@ -232,27 +268,6 @@ const FFOutputFormat ff_streamhash_muxer = {
 #endif
 
 #if CONFIG_FRAMEHASH_MUXER || CONFIG_FRAMEMD5_MUXER
-static void framehash_print_extradata(struct AVFormatContext *s)
-{
-    int i;
-
-    for (i = 0; i < s->nb_streams; i++) {
-        AVStream *st = s->streams[i];
-        AVCodecParameters *par = st->codecpar;
-        if (par->extradata) {
-            struct HashContext *c = s->priv_data;
-            char buf[AV_HASH_MAX_SIZE*2+1];
-
-            avio_printf(s->pb, "#extradata %d, %31d, ", i, 
par->extradata_size);
-            av_hash_init(c->hashes[0]);
-            av_hash_update(c->hashes[0], par->extradata, par->extradata_size);
-            av_hash_final_hex(c->hashes[0], buf, sizeof(buf));
-            avio_write(s->pb, buf, strlen(buf));
-            avio_printf(s->pb, "\n");
-        }
-    }
-}
-
 static int framehash_init(struct AVFormatContext *s)
 {
     int res;
@@ -273,7 +288,7 @@ static int framehash_write_header(struct AVFormatContext *s)
     avio_printf(s->pb, "#format: frame checksums\n");
     avio_printf(s->pb, "#version: %d\n", c->format_version);
     avio_printf(s->pb, "#hash: %s\n", av_hash_get_name(c->hashes[0]));
-    framehash_print_extradata(s);
+    hash_print_extradata(s);
     ff_framehash_write_header(s);
     avio_printf(s->pb, "#stream#, dts,        pts, duration,     size, 
hash\n");
     return 0;
diff --git a/tests/fate/dca.mak b/tests/fate/dca.mak
index 609f924aa3..396faecf2d 100644
--- a/tests/fate/dca.mak
+++ b/tests/fate/dca.mak
@@ -71,10 +71,10 @@ fate-dca-core: CMP = oneoff
 fate-dca-core: REF = $(SAMPLES)/dts/dts.pcm
 
 FATE_DCA-$(call DEMDEC, DTS, DCA, ARESAMPLE_FILTER PCM_S24LE_ENCODER 
PCM_S24LE_MUXER) += fate-dca-xll
-fate-dca-xll: CMD = md5 -i $(TARGET_SAMPLES)/dts/master_audio_7.1_24bit.dts -f 
s24le -af aresample
+fate-dca-xll: CMD = fmtstdout "streamhash -hash md5" -i 
$(TARGET_SAMPLES)/dts/master_audio_7.1_24bit.dts -c:a pcm_s24le -af aresample
 
 FATE_DCA-$(call DEMDEC, DTS, DCA, ARESAMPLE_FILTER PCM_S24LE_ENCODER 
PCM_S24LE_MUXER) += fate-dca-xll-coded
-fate-dca-xll-coded: CMD = md5 -channel_order coded -i 
$(TARGET_SAMPLES)/dts/master_audio_7.1_24bit.dts -f s24le -af aresample
+fate-dca-xll-coded: CMD = fmtstdout "streamhash -hash md5" -channel_order 
coded -i $(TARGET_SAMPLES)/dts/master_audio_7.1_24bit.dts -c:a pcm_s24le -af 
aresample
 
 FATE_DCA-$(call PCM, DTS, DCA, ARESAMPLE_FILTER) += fate-dts_es
 fate-dts_es: CMD = pcm -i $(TARGET_SAMPLES)/dts/dts_es.dts
diff --git a/tests/ref/fate/dca-xll b/tests/ref/fate/dca-xll
index 75b5453a87..1cacc0ffae 100644
--- a/tests/ref/fate/dca-xll
+++ b/tests/ref/fate/dca-xll
@@ -1 +1,10 @@
-5eb9a95ddaf3c803e74443a49a691686
+#format: stream checksums
+#version: 2
+#hash: MD5
+#tb 0: 1/48000
+#media_type 0: audio
+#codec_id 0: pcm_s24le
+#sample_rate 0: 48000
+#channel_layout_name 0: 7.1(wide)
+#stream#, size, hash
+0,    14401536, 5eb9a95ddaf3c803e74443a49a691686
diff --git a/tests/ref/fate/dca-xll-coded b/tests/ref/fate/dca-xll-coded
index 42e17da0d7..15f601fb4f 100644
--- a/tests/ref/fate/dca-xll-coded
+++ b/tests/ref/fate/dca-xll-coded
@@ -1 +1,10 @@
-0ae67d520a46776046ab5ab7d1c6a387
+#format: stream checksums
+#version: 2
+#hash: MD5
+#tb 0: 1/48000
+#media_type 0: audio
+#codec_id 0: pcm_s24le
+#sample_rate 0: 48000
+#channel_layout_name 0: 8 channels (FC+FL+FR+BL+BR+LFE+FLC+FRC)
+#stream#, size, hash
+0,    14401536, 0ae67d520a46776046ab5ab7d1c6a387
diff --git a/tests/ref/fate/filter-channelsplit 
b/tests/ref/fate/filter-channelsplit
index 892801d94d..81ca60ab83 100644
--- a/tests/ref/fate/filter-channelsplit
+++ b/tests/ref/fate/filter-channelsplit
@@ -1,2 +1,16 @@
-0,a,SHA256=a4f03d92f82d074d20bcc49ffcbb28911ae85b097142249a890af59422eb0da8
-1,a,SHA256=c2f021f2b2faa1629674e6126ce5f997ef2034ecd3a15df595a98aefa40614e9
+#format: stream checksums
+#version: 2
+#hash: SHA256
+#tb 0: 1/44100
+#media_type 0: audio
+#codec_id 0: pcm_s16le
+#sample_rate 0: 44100
+#channel_layout_name 0: 1 channels (FL)
+#tb 1: 1/44100
+#media_type 1: audio
+#codec_id 1: pcm_s16le
+#sample_rate 1: 44100
+#channel_layout_name 1: 1 channels (FR)
+#stream#, size, hash
+0,      529200, 
a4f03d92f82d074d20bcc49ffcbb28911ae85b097142249a890af59422eb0da8
+1,      529200, 
c2f021f2b2faa1629674e6126ce5f997ef2034ecd3a15df595a98aefa40614e9
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to