Commit 727c312a71 for ffmpeg
commit 727c312a71b615f9a04875e6054b7a3285933c7c
Author: Michael Niedermayer <michael@niedermayer.cc>
Date: Sun Nov 8 17:17:19 2026 +0100
avcodec/encode: derive the last packet's padding from the padded frame
Fixes: wrong skip samples side data and duration on the last packet
Testcase: ffmpeg -f lavfi -i "sine=r=48000,atrim=end_sample=85" -c:a dfpwm -f framecrc -
(discard padding of the last packet 4011 before, 3 after)
Testcase: ffmpeg -f lavfi -i "sine=r=48000,atrim=end_sample=85" -c:a dfpwm -movie_timescale 48000 out.mov
(edit list duration 85 before and after, 4093 with the padding change alone)
diff --git a/libavcodec/encode.c b/libavcodec/encode.c
index f0e68915a5..6785d75e4d 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -258,9 +258,11 @@ static int encode_set_packet_props(AVCodecContext *avctx, AVPacket *avpkt, const
int discard_padding = AV_RL32(frame_sd->data + 4);
if (discard_padding > 0 && avctx->frame_size && ec->last_audio_frame) {
+ if (!frame->duration)
+ avpkt->duration = ff_samples_to_time_base(avctx, frame->nb_samples - discard_padding);
avpkt->duration = av_sat_add64(avpkt->duration, ff_samples_to_time_base(avctx, avctx->initial_padding));
- avpkt->duration = FFMIN(avpkt->duration, ff_samples_to_time_base(avctx, avctx->frame_size));
- discard_padding = avctx->frame_size - ff_samples_from_time_base(avctx, avpkt->duration);
+ avpkt->duration = FFMIN(avpkt->duration, ff_samples_to_time_base(avctx, frame->nb_samples));
+ discard_padding = FFMAX(0, discard_padding - avctx->initial_padding);
}
if (skip_samples > 0 || discard_padding > 0) {
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index ed49b361af..f36072470f 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -7511,7 +7511,10 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
sd = av_packet_side_data_get(pkt->side_data, pkt->side_data_elems, AV_PKT_DATA_SKIP_SAMPLES);
if (sd && sd->size >= 10 && trk->par->frame_size) {
- duration = FFMAX(av_rescale_q(trk->par->frame_size, (AVRational){ 1, trk->par->sample_rate },
+ int coded_samples = av_get_audio_frame_duration2(trk->par, pkt->size);
+ if (coded_samples <= 0)
+ coded_samples = trk->par->frame_size;
+ duration = FFMAX(av_rescale_q(coded_samples, (AVRational){ 1, trk->par->sample_rate },
trk->st->time_base), pkt->duration);
if (mov->use_editlist)
pkt->duration = duration;
diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak
index 2a4de3f463..01b1346cec 100644
--- a/tests/fate/mov.mak
+++ b/tests/fate/mov.mak
@@ -414,6 +414,9 @@ fate-mov-pcm-remux: REF = 389df40a487ac8397ba20bda472434d7
FATE_MOV_FFMPEG-$(call TRANSCODE, RAWVIDEO, MOV, TESTSRC_FILTER SETPTS_FILTER) += fate-mov-vfr
fate-mov-vfr: CMD = md5 -filter_complex testsrc=size=2x2:duration=1,setpts=N*N:strip_fps=1 -c rawvideo -fflags +bitexact -f mov
+
+FATE_MOV_FFMPEG-$(call TRANSCODE, DFPWM, MOV, LAVFI_INDEV SINE_FILTER ATRIM_FILTER PCM_S16LE_DECODER ARESAMPLE_FILTER) += fate-mov-dfpwm-last-packet
+fate-mov-dfpwm-last-packet: CMD = transcode lavfi "sine=r=48000,atrim=end_sample=85" mov "-auto_conversion_filters -c:a dfpwm -movie_timescale 48000" "-c copy"
fate-mov-vfr: CMP = oneline
fate-mov-vfr: REF = 1558b4a9398d8635783c93f84eb5a60d
diff --git a/tests/ref/fate/mov-dfpwm-last-packet b/tests/ref/fate/mov-dfpwm-last-packet
new file mode 100644
index 0000000000..745a210232
--- /dev/null
+++ b/tests/ref/fate/mov-dfpwm-last-packet
@@ -0,0 +1,8 @@
+e79b0af25dc089c090f0d9defb2a4e0d *tests/data/fate/mov-dfpwm-last-packet.mov
+656 tests/data/fate/mov-dfpwm-last-packet.mov
+#tb 0: 1/48000
+#media_type 0: audio
+#codec_id 0: dfpwm
+#sample_rate 0: 48000
+#channel_layout_name 0: mono
+0, 0, 0, 88, 11, 0x266a0606, S=1, Skip Samples, 10, 0x00120003