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

Defined mainly to signal a sample rate with a value that doesn't fit the 
standard 16bit field for formats that don't report a sample rate within private 
codec parameters (which in general takes precedence), as is the case for PCM.
For those, the spec recommends using AudioSampleEntryV1 to signal their parsing 
is required.


>From 2d5f77c5a2e0fe601eedd804dc67fef7a4708f78 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Fri, 21 Nov 2025 23:06:25 -0300
Subject: [PATCH] avformat/movenc: add support for writing srat box

Signed-off-by: James Almer <[email protected]>
---
 libavformat/movenc.c | 51 +++++++++++++++++++++++++++++++++++++++-----
 libavformat/movenc.h |  1 +
 2 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 42c8771496..443ef2866c 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -161,6 +161,18 @@ static int64_t update_size(AVIOContext *pb, int64_t pos)
     return curpos - pos;
 }
 
+static int64_t update_size_and_version(AVIOContext *pb, int64_t pos, int 
version)
+{
+    int64_t curpos = avio_tell(pb);
+    avio_seek(pb, pos, SEEK_SET);
+    avio_wb32(pb, curpos - pos); /* rewrite size */
+    avio_skip(pb, 4);
+    avio_w8(pb, version); /* rewrite version */
+    avio_seek(pb, curpos, SEEK_SET);
+
+    return curpos - pos;
+}
+
 static int co64_required(const MOVTrack *track)
 {
     if (track->entry > 0 && track->cluster[track->entry - 1].pos + 
track->data_offset > UINT32_MAX)
@@ -1344,6 +1356,18 @@ static int mov_write_pcmc_tag(AVFormatContext *s, 
AVIOContext *pb, MOVTrack *tra
     return update_size(pb, pos);
 }
 
+static int mov_write_srat_tag(AVIOContext *pb, MOVTrack *track)
+{
+    int64_t pos = avio_tell(pb);
+    avio_wb32(pb, 0); /* size */
+    ffio_wfourcc(pb, "srat");
+    avio_wb32(pb, 0); /* version & flags */
+
+    avio_wb32(pb, track->par->sample_rate);
+
+    return update_size(pb, pos);
+}
+
 static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, 
MOVMuxContext *mov, MOVTrack *track)
 {
     int64_t pos = avio_tell(pb);
@@ -1363,6 +1387,10 @@ static int mov_write_audio_tag(AVFormatContext *s, 
AVIOContext *pb, MOVMuxContex
                    track->par->codec_id == AV_CODEC_ID_QDM2) {
             version = 1;
         }
+    } else if (track->mode == MODE_MP4) {
+        if (track->par->sample_rate > UINT16_MAX &&
+            (tag == MOV_MP4_IPCM_TAG || tag == MOV_MP4_FPCM_TAG))
+            version = 1;
     }
 
     avio_wb32(pb, 0); /* size */
@@ -1395,6 +1423,8 @@ static int mov_write_audio_tag(AVFormatContext *s, 
AVIOContext *pb, MOVMuxContex
         avio_wb32(pb, track->sample_size);
         avio_wb32(pb, get_samples_per_packet(track));
     } else {
+        unsigned sample_rate = track->par->sample_rate;
+
         if (track->mode == MODE_MOV) {
             avio_wb16(pb, track->par->ch_layout.nb_channels);
             if (track->par->codec_id == AV_CODEC_ID_PCM_U8 ||
@@ -1415,6 +1445,9 @@ static int mov_write_audio_tag(AVFormatContext *s, 
AVIOContext *pb, MOVMuxContex
                 avio_wb16(pb, 16);
             }
             avio_wb16(pb, 0);
+
+            while (sample_rate > UINT16_MAX)
+                sample_rate >>= 1;
         }
 
         avio_wb16(pb, 0); /* packet size (= 0) */
@@ -1425,14 +1458,14 @@ static int mov_write_audio_tag(AVFormatContext *s, 
AVIOContext *pb, MOVMuxContex
         else if (track->par->codec_id == AV_CODEC_ID_TRUEHD)
             avio_wb32(pb, track->par->sample_rate);
         else
-            avio_wb16(pb, track->par->sample_rate <= UINT16_MAX ?
-                          track->par->sample_rate : 0);
+            avio_wb16(pb, sample_rate);
 
         if (track->par->codec_id != AV_CODEC_ID_TRUEHD)
             avio_wb16(pb, 0); /* Reserved */
     }
 
     if (version == 1) { /* SoundDescription V1 extended info */
+        if (track->mode == MODE_MOV) {
         if (mov_pcm_le_gt16(track->par->codec_id) ||
             mov_pcm_be_gt16(track->par->codec_id))
             avio_wb32(pb, 1); /*  must be 1 for  uncompressed formats */
@@ -1441,6 +1474,7 @@ static int mov_write_audio_tag(AVFormatContext *s, 
AVIOContext *pb, MOVMuxContex
         avio_wb32(pb, track->sample_size / track->par->ch_layout.nb_channels); 
/* Bytes per packet */
         avio_wb32(pb, track->sample_size); /* Bytes per frame */
         avio_wb32(pb, 2); /* Bytes per sample */
+        }
     }
 
     if (track->mode == MODE_MOV &&
@@ -1489,9 +1523,13 @@ static int mov_write_audio_tag(AVFormatContext *s, 
AVIOContext *pb, MOVMuxContex
     if (ret < 0)
         return ret;
 
-    if (track->mode == MODE_MP4 && track->par->codec_type == AVMEDIA_TYPE_AUDIO
-            && ((ret = mov_write_SA3D_tag(s, pb, track)) < 0)) {
+    if (track->mode == MODE_MP4) {
+        ret = mov_write_SA3D_tag(s, pb, track);
+        if (ret < 0)
         return ret;
+
+        if (track->par->sample_rate > UINT16_MAX)
+            mov_write_srat_tag(pb, track);
     }
 
     if (track->mode == MODE_MOV && track->par->codec_type == AVMEDIA_TYPE_AUDIO
@@ -1508,6 +1546,9 @@ static int mov_write_audio_tag(AVFormatContext *s, 
AVIOContext *pb, MOVMuxContex
             ((ret = mov_write_btrt_tag(pb, track)) < 0))
         return ret;
 
+    if (track->mode == MODE_MP4)
+        track->entry_version = version;
+
     ret = update_size(pb, pos);
     return ret;
 }
@@ -3122,7 +3163,7 @@ static int mov_write_stsd_tag(AVFormatContext *s, 
AVIOContext *pb, MOVMuxContext
 
     track->last_stsd_index = stsd_index_back;
 
-    return update_size(pb, pos);
+    return update_size_and_version(pb, pos, track->entry_version);
 }
 
 static int mov_write_ctts_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack 
*track)
diff --git a/libavformat/movenc.h b/libavformat/movenc.h
index 942ad905f7..eb12551ee5 100644
--- a/libavformat/movenc.h
+++ b/libavformat/movenc.h
@@ -86,6 +86,7 @@ typedef struct MOVFragmentInfo {
 
 typedef struct MOVTrack {
     int         mode;
+    int         entry_version;
     int         entry, entry_written;
     unsigned    timescale;
     uint64_t    time;
-- 
2.49.1

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

Reply via email to