PR #21553 opened by Jun Zhao (mypopydev)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21553
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21553.patch

Reproduce:
  ffmpeg -f lavfi -i sine=f=440:d=2 -c:a opus -strict -2 out.caf
  ffprobe out.caf  # Shows 00:00:00.25 instead of 00:00:02.00

The native Opus encoder sets frame_size=120 (2.5ms minimum input unit),
not the actual packet duration (960 samples). The muxer incorrectly used
this value for mFramesPerPacket and mNumberValidFrames calculation.

Fix by tracking actual packet durations during muxing and using the
accumulated total in the trailer.

Signed-off-by: Jun Zhao <[email protected]>


>From 599f13a2f5ad134f811149f4e4e09a2cee069bcf Mon Sep 17 00:00:00 2001
From: Jun Zhao <[email protected]>
Date: Fri, 23 Jan 2026 11:58:16 +0800
Subject: [PATCH] lavf/cafenc: fix incorrect duration for Opus in CAF container

Reproduce:
  ffmpeg -f lavfi -i sine=f=440:d=2 -c:a opus -strict -2 out.caf
  ffprobe out.caf  # Shows 00:00:00.25 instead of 00:00:02.00

The native Opus encoder sets frame_size=120 (2.5ms minimum input unit),
not the actual packet duration (960 samples). The muxer incorrectly used
this value for mFramesPerPacket and mNumberValidFrames calculation.

Fix by tracking actual packet durations during muxing and using the
accumulated total in the trailer.

Signed-off-by: Jun Zhao <[email protected]>
---
 libavformat/cafenc.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libavformat/cafenc.c b/libavformat/cafenc.c
index 89ecb51e53..d19e703270 100644
--- a/libavformat/cafenc.c
+++ b/libavformat/cafenc.c
@@ -32,6 +32,7 @@
 
 typedef struct {
     int64_t data;
+    int64_t total_duration;
     int size_buffer_size;
     int size_entries_used;
     int packets;
@@ -57,7 +58,6 @@ static uint32_t codec_flags(enum AVCodecID codec_id) {
 static uint32_t samples_per_packet(const AVCodecParameters *par) {
     enum AVCodecID codec_id = par->codec_id;
     int channels = par->ch_layout.nb_channels, block_align = par->block_align;
-    int frame_size = par->frame_size, sample_rate = par->sample_rate;
 
     switch (codec_id) {
     case AV_CODEC_ID_PCM_S8:
@@ -89,7 +89,7 @@ static uint32_t samples_per_packet(const AVCodecParameters 
*par) {
     case AV_CODEC_ID_MP1:
         return 384;
     case AV_CODEC_ID_OPUS:
-        return frame_size * 48000 / sample_rate;
+        return 0; // Variable, will be calculated in trailer from actual 
packet durations
     case AV_CODEC_ID_MP2:
     case AV_CODEC_ID_MP3:
         return 1152;
@@ -235,6 +235,7 @@ static int caf_write_packet(AVFormatContext *s, AVPacket 
*pkt)
         }
         pkt_sizes[caf->size_entries_used++] = pkt->size & 127;
         caf->packets++;
+        caf->total_duration += pkt->duration;
     }
     avio_write(s->pb, pkt->data, pkt->size);
     return 0;
@@ -255,7 +256,7 @@ static int caf_write_trailer(AVFormatContext *s)
         if (!par->block_align) {
             int packet_size = samples_per_packet(par);
             if (!packet_size) {
-                packet_size = st->duration / (caf->packets - 1);
+                packet_size = caf->total_duration / caf->packets;
                 avio_seek(pb, FRAME_SIZE_OFFSET, SEEK_SET);
                 avio_wb32(pb, packet_size);
             }
@@ -263,7 +264,7 @@ static int caf_write_trailer(AVFormatContext *s)
             ffio_wfourcc(pb, "pakt");
             avio_wb64(pb, caf->size_entries_used + 24U);
             avio_wb64(pb, caf->packets); ///< mNumberPackets
-            avio_wb64(pb, caf->packets * packet_size); ///< mNumberValidFrames
+            avio_wb64(pb, caf->total_duration); ///< mNumberValidFrames
             avio_wb32(pb, 0); ///< mPrimingFrames
             avio_wb32(pb, 0); ///< mRemainderFrames
             avio_write(pb, st->priv_data, caf->size_entries_used);
-- 
2.52.0

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

Reply via email to