PR #24308 opened by Forgejo_Fairy
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24308
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24308.patch

Fixes #21362.

The ASF muxer's historical 3,200-byte default packet size can split an audio 
media object at an offset that is not a multiple of the stream's WAVEFORMATEX 
Block Alignment. Section 9.1.2 of the Advanced Systems Format Specification 
requires every audio payload size to be a multiple of that value. Windows Media 
readers reject such output for WMA blocks larger than the available packet 
payload.

This change:

- uses an automatic packet-size default starting at 3,200 bytes and enlarges it 
enough to hold at least one audio block;
- rejects explicitly selected packet sizes that cannot hold one complete audio 
block;
- aligns fragmented audio payload lengths to the codec block size;
- documents the automatic behavior; and
- adds a FATE remux test using the existing `Californication_cover.wma` sample, 
whose WMA block alignment is 4,459 bytes.

For the issue sample, the automatic size becomes 4,485 bytes: 4,459 bytes of 
audio plus 26 bytes of ASF single-payload overhead.

Tested with:

- `make fate-asf-remux-audio-block-align SAMPLES=/opt/fate-suite`
- `make fate-microsoft SAMPLES=/opt/fate-suite`
- `make fate-lavf-asf SAMPLES=/opt/fate-suite`
- `make fate-generic-tags-remux-asf SAMPLES=/opt/fate-suite`
- `make fate-wmav1-encode fate-wmav2-encode SAMPLES=/opt/fate-suite`


>From 71d5b9636b7446bf5080e25d61d7d8e790612e9f Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Fri, 28 Aug 2026 16:56:21 +0000
Subject: [PATCH] avformat/asfenc: honor audio block alignment

Choose a default packet size large enough to hold one audio block and align 
fragmented audio payloads to the codec block size. This avoids producing 
invalid ASF when a WMA block exceeds the historical 3200-byte packet default.

Assisted-by: Fairy
---
 doc/muxers.texi                            |  5 ++-
 libavformat/asfenc.c                       | 43 +++++++++++++++++++++-
 tests/fate/microsoft.mak                   |  8 +++-
 tests/ref/fate/asf-remux-audio-block-align | 19 ++++++++++
 4 files changed, 70 insertions(+), 5 deletions(-)
 create mode 100644 tests/ref/fate/asf-remux-audio-block-align

diff --git a/doc/muxers.texi b/doc/muxers.texi
index 99584e2b8d..f110797297 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -841,8 +841,9 @@ muxer too.
 @item packet_size @var{size}
 Set the muxer packet size as a number of bytes. By tuning this setting
 you may reduce data fragmentation or muxer overhead depending on your
-source. Default value is @code{3200}, minimum is @code{100}, maximum
-is @code{64Ki}.
+source. By default, the muxer starts with a size of @code{3200} and
+increases it when necessary to fit at least one audio block. The minimum
+explicit value is @code{100}, and the maximum is @code{64Ki}.
 @end table
 
 @section ass
diff --git a/libavformat/asfenc.c b/libavformat/asfenc.c
index 59638e7b22..ba56998c8e 100644
--- a/libavformat/asfenc.c
+++ b/libavformat/asfenc.c
@@ -188,6 +188,7 @@
 
 #define DATA_HEADER_SIZE 50
 
+#define PACKET_SIZE_DEFAULT 3200
 #define PACKET_SIZE_MAX 65536
 #define PACKET_SIZE_MIN 100
 
@@ -738,8 +739,43 @@ fail:
 static int asf_write_header(AVFormatContext *s)
 {
     ASFContext *asf = s->priv_data;
+    int packet_size = asf->packet_size;
+    const int auto_packet_size = !packet_size;
     int ret;
 
+    if (auto_packet_size) {
+        packet_size = PACKET_SIZE_DEFAULT;
+    } else if (packet_size < PACKET_SIZE_MIN) {
+        av_log(s, AV_LOG_ERROR,
+               "Packet size %d is smaller than the minimum %d\n",
+               packet_size, PACKET_SIZE_MIN);
+        return AVERROR(EINVAL);
+    }
+
+    for (unsigned i = 0; i < s->nb_streams; i++) {
+        const AVCodecParameters *par = s->streams[i]->codecpar;
+
+        if (par->codec_type == AVMEDIA_TYPE_AUDIO && par->block_align) {
+            const int min_packet_size = par->block_align + 
SINGLE_PAYLOAD_HEADERS;
+
+            if (min_packet_size > PACKET_SIZE_MAX) {
+                av_log(s, AV_LOG_ERROR,
+                       "Block alignment %d is too large for an ASF packet\n",
+                       par->block_align);
+                return AVERROR(EINVAL);
+            }
+            if (auto_packet_size) {
+                packet_size = FFMAX(packet_size, min_packet_size);
+            } else if (packet_size < min_packet_size) {
+                av_log(s, AV_LOG_ERROR,
+                       "Packet size %d is too small for block alignment %d\n",
+                       packet_size, par->block_align);
+                return AVERROR(EINVAL);
+            }
+        }
+    }
+
+    asf->packet_size = packet_size;
     s->packet_size  = asf->packet_size;
     s->max_interleave_delta = 0;
     asf->nb_packets = 0;
@@ -929,6 +965,11 @@ static void put_frame(AVFormatContext *s, ASFStream 
*stream, AVStream *avst,
             }
         }
         if (frag_len1 > 0) {
+            if (payload_len > frag_len1 &&
+                avst->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
+                avst->codecpar->block_align)
+                frag_len1 -= frag_len1 % avst->codecpar->block_align;
+
             if (payload_len > frag_len1)
                 payload_len = frag_len1;
             else if (payload_len == (frag_len1 - 1))
@@ -1106,7 +1147,7 @@ static void asf_deinit(AVFormatContext *s)
 }
 
 static const AVOption asf_options[] = {
-    { "packet_size", "Packet size", offsetof(ASFContext, packet_size), 
AV_OPT_TYPE_INT, {.i64 = 3200}, PACKET_SIZE_MIN, PACKET_SIZE_MAX, 
AV_OPT_FLAG_ENCODING_PARAM },
+    { "packet_size", "Packet size", offsetof(ASFContext, packet_size), 
AV_OPT_TYPE_INT, {.i64 = 0}, 0, PACKET_SIZE_MAX, AV_OPT_FLAG_ENCODING_PARAM },
     { NULL },
 };
 
diff --git a/tests/fate/microsoft.mak b/tests/fate/microsoft.mak
index 8d8e92575a..c4d2927e95 100644
--- a/tests/fate/microsoft.mak
+++ b/tests/fate/microsoft.mak
@@ -80,10 +80,14 @@ fate-vc1-ism: CMD = framecrc -i 
$(TARGET_SAMPLES)/isom/vc1-wmapro.ism -an
 FATE_MICROSOFT += $(FATE_VC1-yes)
 fate-vc1: $(FATE_VC1-yes)
 
+FATE_ASF_REMUX-$(call REMUX, ASF) += fate-asf-remux-audio-block-align
+fate-asf-remux-audio-block-align: CMD = transcode asf 
$(TARGET_SAMPLES)/cover_art/Californication_cover.wma asf "-map 0:a -c copy 
-map_metadata -1 -frames:a 8" "-map 0:a -c copy" "-of compact -select_streams a 
-show_packets -show_entries packet=pts,size,pos -read_intervals %+\#3"
+
 FATE_MICROSOFT-$(call ALLYES, PIPE_PROTOCOL ASF_DEMUXER FRAMECRC_MUXER) += 
fate-asf-repldata
 fate-asf-repldata: CMD = framecrc -i $(TARGET_SAMPLES)/asf/bug821-2.asf -c copy
 
 FATE_MICROSOFT += $(FATE_MICROSOFT-yes)
 
-FATE_SAMPLES_FFMPEG += $(FATE_MICROSOFT)
-fate-microsoft: $(FATE_MICROSOFT)
+FATE_SAMPLES_FFMPEG         += $(FATE_MICROSOFT)
+FATE_SAMPLES_FFMPEG_FFPROBE += $(FATE_ASF_REMUX-yes)
+fate-microsoft: $(FATE_MICROSOFT) $(FATE_ASF_REMUX-yes)
diff --git a/tests/ref/fate/asf-remux-audio-block-align 
b/tests/ref/fate/asf-remux-audio-block-align
new file mode 100644
index 0000000000..1fa6db4a49
--- /dev/null
+++ b/tests/ref/fate/asf-remux-audio-block-align
@@ -0,0 +1,19 @@
+88407d736bfe38ea866d2a867a7869a5 
*tests/data/fate/asf-remux-audio-block-align.asf
+36324 tests/data/fate/asf-remux-audio-block-align.asf
+#extradata 0:       10, 0x08a50189
+#tb 0: 1/1000
+#media_type 0: audio
+#codec_id 0: wmav2
+#sample_rate 0: 44100
+#channel_layout_name 0: stereo
+0,          0,          0,      371,     4459, 0x8e6ba76d
+0,        324,        324,      371,     4459, 0x893de414
+0,        696,        696,      371,     4459, 0x69a173aa
+0,        928,        928,      371,     4459, 0xcc9baf3f
+0,       1206,       1206,      371,     4459, 0xc890aa7d
+0,       1439,       1439,      371,     4459, 0x4db6ac94
+0,       1763,       1763,      371,     4459, 0x7d2f71bb
+0,       2043,       2043,      371,     4459, 0xb0be449f
+packet|pts=0|size=4459|pos=444
+packet|pts=324|size=4459|pos=4929
+packet|pts=696|size=4459|pos=9414
-- 
2.52.0

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

Reply via email to