This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit 6397b2b5b6493ed5cb61bd18fdac18a32ae27548
Author:     Romain Beauxis <[email protected]>
AuthorDate: Wed Sep 16 15:20:09 2026 -0500
Commit:     James Almer <[email protected]>
CommitDate: Sat Sep 19 13:40:52 2026 +0000

    avformat/mp3enc: build the Xing frame header from the first audio frame
    
    Some hardware decoders reject files whose Xing frame header differs from
    the audio frames, e.g. joint stereo from libmp3lame. Fail when the first
    frame does not match the Xing frame layout.
    
    Fixes #24129.
---
 libavformat/mp3enc.c                         | 40 ++++++++++++++++++++++++----
 tests/ref/fate/cover-art-mp3-id3v2-remux     |  2 +-
 tests/ref/fate/gapless-mp3-remux             |  2 +-
 tests/ref/fate/id3v2-priv-remux              |  2 +-
 tests/ref/fate/id3v2-reenc-remux-keep        |  2 +-
 tests/ref/fate/mpegaudiodecheader-libmp3lame |  3 +--
 tests/ref/fate/mpegaudiodecheader-mp3-remux  |  3 +--
 7 files changed, 41 insertions(+), 13 deletions(-)

diff --git a/libavformat/mp3enc.c b/libavformat/mp3enc.c
index 14e56c5738..4ea54f72c3 100644
--- a/libavformat/mp3enc.c
+++ b/libavformat/mp3enc.c
@@ -127,6 +127,8 @@ typedef struct MP3Context {
     int initial_bitrate;
     int has_variable_bitrate;
     MPADecodeHeader2 *hdr;
+    MPADecodeHeader2 *xing_header;
+    MPADecodeHeader2 *first_frame;
     int delay;
     int64_t padding;
 
@@ -209,13 +211,13 @@ static int mp3_write_xing(AVFormatContext *s)
             return 0;
         header |= mask;
 
-        ret = avpriv_mpegaudio_decode_header2(&mp3->hdr, header);
+        ret = avpriv_mpegaudio_decode_header2(&mp3->xing_header, header);
         if (ret < 0)
             return ret;
-        mp3->xing_offset = xing_offtbl[mp3->hdr->lsf == 
1][mp3->hdr->nb_channels == 1] + 4;
+        mp3->xing_offset = xing_offtbl[mp3->xing_header->lsf == 
1][mp3->xing_header->nb_channels == 1] + 4;
         bytes_needed     = mp3->xing_offset + XING_SIZE;
 
-        if (bytes_needed <= mp3->hdr->frame_size)
+        if (bytes_needed <= mp3->xing_header->frame_size)
             break;
 
         header &= ~mask;
@@ -231,7 +233,7 @@ static int mp3_write_xing(AVFormatContext *s)
     ffio_wfourcc(dyn_ctx, "Xing");
     avio_wb32(dyn_ctx, 0x01 | 0x02 | 0x04 | 0x08);  // frames / size / TOC / 
vbr scale
 
-    mp3->size = mp3->hdr->frame_size;
+    mp3->size = mp3->xing_header->frame_size;
     mp3->want=1;
     mp3->seen=0;
     mp3->pos=0;
@@ -276,7 +278,7 @@ static int mp3_write_xing(AVFormatContext *s)
     avio_wb16(dyn_ctx, 0); // music crc
     avio_wb16(dyn_ctx, 0); // tag crc
 
-    ffio_fill(dyn_ctx, 0, mp3->hdr->frame_size - bytes_needed);
+    ffio_fill(dyn_ctx, 0, mp3->xing_header->frame_size - bytes_needed);
 
     mp3->xing_frame_size   = avio_close_dyn_buf(dyn_ctx, &mp3->xing_frame);
     mp3->xing_frame_offset = avio_tell(s->pb);
@@ -332,6 +334,23 @@ static int mp3_write_audio_packet(AVFormatContext *s, 
AVPacket *pkt)
                 mp3->initial_bitrate = mp3->hdr->bit_rate;
             if ((mp3->hdr->bit_rate == 0) || (mp3->initial_bitrate != 
mp3->hdr->bit_rate))
                 mp3->has_variable_bitrate = 1;
+            if (mp3->xing_offset && !mp3->first_frame) {
+                const MPADecodeHeader2 *xing = mp3->xing_header;
+
+                if (mp3->hdr->layer       != xing->layer       ||
+                    mp3->hdr->sample_rate != xing->sample_rate ||
+                    mp3->hdr->nb_channels != xing->nb_channels) {
+                    av_log(s, AV_LOG_ERROR, "First audio frame (layer %d, %d 
Hz, %d channels) "
+                           "does not match the stream (layer %d, %d Hz, %d 
channels).\n",
+                           mp3->hdr->layer, mp3->hdr->sample_rate, 
mp3->hdr->nb_channels,
+                           xing->layer, xing->sample_rate, xing->nb_channels);
+                    return AVERROR_INVALIDDATA;
+                }
+
+                ret = avpriv_mpegaudio_decode_header2(&mp3->first_frame, h);
+                if (ret < 0)
+                    return ret;
+            }
         } else if (ret == AVERROR(ENOMEM)) {
             return ret;
         } else {
@@ -443,6 +462,15 @@ static void mp3_update_xing(AVFormatContext *s)
     if (!mp3->has_variable_bitrate)
         AV_WL32(mp3->xing_frame + mp3->xing_offset, MKTAG('I', 'n', 'f', 'o'));
 
+    if (mp3->first_frame) {
+        MPADecodeHeader2 *header = mp3->first_frame;
+
+        header->error_protection = mp3->xing_header->error_protection;
+        header->bitrate_index    = mp3->xing_header->bitrate_index;
+        header->padding          = mp3->xing_header->padding;
+        AV_WB32(mp3->xing_frame, ff_mpa_encode_header(header));
+    }
+
     AV_WB32(mp3->xing_frame + mp3->xing_offset + 8,  mp3->frames);
     AV_WB32(mp3->xing_frame + mp3->xing_offset + 12, mp3->size);
 
@@ -694,6 +722,8 @@ static void mp3_deinit(struct AVFormatContext *s)
     ffio_free_dyn_buf(&mp3->id3_pb);
     av_freep(&mp3->xing_frame);
     av_freep(&mp3->hdr);
+    av_freep(&mp3->xing_header);
+    av_freep(&mp3->first_frame);
 }
 
 const FFOutputFormat ff_mp3_muxer = {
diff --git a/tests/ref/fate/cover-art-mp3-id3v2-remux 
b/tests/ref/fate/cover-art-mp3-id3v2-remux
index ddc47c4988..2be6836b76 100644
--- a/tests/ref/fate/cover-art-mp3-id3v2-remux
+++ b/tests/ref/fate/cover-art-mp3-id3v2-remux
@@ -1,4 +1,4 @@
-3a20f15563ed1d277f54562743f615de *tests/data/fate/cover-art-mp3-id3v2-remux.mp3
+eb486ac9092d44a280e4041ee354e53b *tests/data/fate/cover-art-mp3-id3v2-remux.mp3
 346486 tests/data/fate/cover-art-mp3-id3v2-remux.mp3
 #tb 0: 1/14112000
 #media_type 0: audio
diff --git a/tests/ref/fate/gapless-mp3-remux b/tests/ref/fate/gapless-mp3-remux
index 0d3d625e33..6d56043a7f 100644
--- a/tests/ref/fate/gapless-mp3-remux
+++ b/tests/ref/fate/gapless-mp3-remux
@@ -1,3 +1,3 @@
-4458dffaa3fd294d708f05965e1a3186 *tests/data/fate/gapless-mp3-remux.mp3
+a490eb522f8fd2c772d1e27a1b5054a2 *tests/data/fate/gapless-mp3-remux.mp3
 249138 tests/data/fate/gapless-mp3-remux.mp3
 stream|start_pts=353600|duration_ts=218521600
diff --git a/tests/ref/fate/id3v2-priv-remux b/tests/ref/fate/id3v2-priv-remux
index c732f36ae2..617bcf2437 100644
--- a/tests/ref/fate/id3v2-priv-remux
+++ b/tests/ref/fate/id3v2-priv-remux
@@ -1,4 +1,4 @@
-bb2816e3a05ce136e9ac14479c1ebe24 *tests/data/fate/id3v2-priv-remux.mp3
+a95cafeff7e82a72aaced6e713cc479a *tests/data/fate/id3v2-priv-remux.mp3
 8542 tests/data/fate/id3v2-priv-remux.mp3
 #tb 0: 1/14112000
 #media_type 0: audio
diff --git a/tests/ref/fate/id3v2-reenc-remux-keep 
b/tests/ref/fate/id3v2-reenc-remux-keep
index 5139f6a408..5c2def69e8 100644
--- a/tests/ref/fate/id3v2-reenc-remux-keep
+++ b/tests/ref/fate/id3v2-reenc-remux-keep
@@ -1,4 +1,4 @@
-a1712e6e4f04a9066f221157b5a55a23 *tests/data/fate/id3v2-reenc-remux-keep.mp3
+4e886ae5b38188d171a61c19deabac0a *tests/data/fate/id3v2-reenc-remux-keep.mp3
 192081 tests/data/fate/id3v2-reenc-remux-keep.mp3
 [FORMAT]
 TAG:title=7rk
diff --git a/tests/ref/fate/mpegaudiodecheader-libmp3lame 
b/tests/ref/fate/mpegaudiodecheader-libmp3lame
index 3955a43426..ddf18712e5 100644
--- a/tests/ref/fate/mpegaudiodecheader-libmp3lame
+++ b/tests/ref/fate/mpegaudiodecheader-libmp3lame
@@ -1,3 +1,2 @@
-offset=20 frames=1 header=FFFB9000 layer=3 sample_rate=44100 bit_rate=128000 
crc=0 mode=0 mode_ext=0 copyright=0 original=0 emphasis=0 private_bit=0
-offset=437 frames=231 header=FFFB9064 layer=3 sample_rate=44100 
bit_rate=128000 crc=0 mode=1 mode_ext=2 copyright=0 original=1 emphasis=0 
private_bit=0
+offset=20 frames=232 header=FFFB9064 layer=3 sample_rate=44100 bit_rate=128000 
crc=0 mode=1 mode_ext=2 copyright=0 original=1 emphasis=0 private_bit=0
 frames=232
diff --git a/tests/ref/fate/mpegaudiodecheader-mp3-remux 
b/tests/ref/fate/mpegaudiodecheader-mp3-remux
index c5d4d5cdd7..4a7602dc83 100644
--- a/tests/ref/fate/mpegaudiodecheader-mp3-remux
+++ b/tests/ref/fate/mpegaudiodecheader-mp3-remux
@@ -1,3 +1,2 @@
-offset=301 frames=1 header=FFFB9000 layer=3 sample_rate=44100 bit_rate=128000 
crc=0 mode=0 mode_ext=0 copyright=0 original=0 emphasis=0 private_bit=0
-offset=718 frames=21 header=FFFB9064 layer=3 sample_rate=44100 bit_rate=128000 
crc=0 mode=1 mode_ext=2 copyright=0 original=1 emphasis=0 private_bit=0
+offset=301 frames=22 header=FFFB9064 layer=3 sample_rate=44100 bit_rate=128000 
crc=0 mode=1 mode_ext=2 copyright=0 original=1 emphasis=0 private_bit=0
 frames=22

-- 
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to