[PATCH] es_out: scale preroll clock-origin compensation by the playback rate

Ismail Degani <[email protected]> Tue, 7 Jul 2026 19:26:13 -0400
Newsgroups gmane.comp.video.videolan.vlc.devel
Message-ID <CAEvQf=RHiNc=65L5QeSoy44D_FwNbYR3YZ+SbaCzU7PaN18_kQ@mail.gmail.com>
--00000000000028c2b106560db87b
Content-Type: multipart/alternative; boundary="00000000000028c2b006560db879"

--00000000000028c2b006560db879
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

Hi devs,

Submitting my first vlc patch for an issue I've had for years (reported 7
years ago! #21890) which Claude gave me the superpowers to finally fix =3D)

If you slow a video down =E2=80=94 say to 0.25x =E2=80=94 and then seek, VL=
C freezes at the
target position for a long time before playback resumes: several seconds
with short-GOP files, and 30 seconds or more with the 5-10 second GOPs
typical of phone recordings and web video. The slower the rate, the longer
the freeze.

What happens: after a precise seek, the demuxer restarts at the previous
keyframe, and EsOutDecodersStopBuffering() rebases the clock origin by
subtracting i_buffering_duration =E2=80=94 a stream-time quantity =E2=80=94=
 from a
system-time date, without scaling by the playback rate. Timestamp
conversion then stretches the keyframe-to-target preroll by 1/rate, so
playback stalls for exactly preroll * (1/rate - 1). The pts_delay part of
that compensation is fine as-is (the input clock already compensates it on
every conversion via ClockGetTsOffset(), which is also why caching settings
don't affect the stall), so the fix scales only the preroll term. At 1.0x
the added term is zero and nothing changes; at rates above 1x the same math
currently makes the first post-seek frames late by the mirrored amount,
which this also fixes.

A note on the filing of #21890: it is labeled Demux: Adaptive because it
was reported against an HLS stream in 2019, but the stall reproduces on a
plain local mp4 =E2=80=94 the root cause is in core es_out/clock code, not =
the
adaptive demuxer. That mislabeling may be part of why it has sat unfixed
for so long. The same defect is behind the slow-motion seek freezes
reported against VLC-Android.

Measured with a headless libvlc driver polling media_player_get_time()
every 250 ms around a seek to 60s (local mp4, 1.67 s preroll, file-caching
1000; "stall" =3D wall time until the reported time advanced 200 ms past th=
e
target):

master (8115e53): unpatched patched
1.0x 1.75 s 1.75 s (unchanged)
2.0x 1.50 s 1.50 s (unchanged)
0.25x 6.25 s 2.25 s
0.1x 18.0 s 2.5 s

The patched residual at slow rates is the measurement floor itself:
advancing 200 ms of stream time takes 0.8 s / 2 s of wall time at 0.25x /
0.1x. The same fix was also verified on the 3.0.x branch with a rebuilt
libvlccore 3.0.20: the 0.25x stall dropped from ~5-6 s to under 1 s, and
0.1x from ~15-17 s to under 2 s. A formatted 3.0.x backport is ready =E2=80=
=94
happy to send it as a follow-up patch or open a [3.0] merge request.

Quick repro: generate a two-minute test clip with "ffmpeg -f lavfi -i
testsrc2=3Dduration=3D120:size=3D640x360:rate=3D30 -f lavfi -i
sine=3Dfrequency=3D440:duration=3D120 -c:v libx264 -preset ultrafast -c:a a=
ac
test120.mp4", play it, set rate 0.25, seek to ~60s. Running with
--input-fast-seek avoids the stall entirely (keyframe seeks have no
preroll), which isolates the preroll term.

The patch is attached (git format-patch against master 8115e53) rather than
inline because this message is sent through a mailer that can't guarantee
inline whitespace survives; the full commit message and measurements are
inside it, and I can resend inline via git send-email if that's preferred.

Two disclosures: I prepared and validated this with AI assistance (noted in
a Co-authored-by trailer), and I have reviewed the change and the
measurements myself. I also have a
https://www.google.com/url?q=3Dhttp://code.videolan.org&source=3Dgmail&ust=
=3D1783552830165000&sa=3DE
account pending administrator approval =E2=80=94 if a merge request is more
convenient for review, I'd gladly resubmit there once it's approved.

Thanks!
Ismail

--00000000000028c2b006560db879
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"auto"><div><div dir=3D"auto">Hi devs,</div><div dir=3D"auto"><b=
r></div><div dir=3D"auto">Submitting my first vlc patch for an issue I&#39;=
ve had for years (reported 7 years ago! #21890) which Claude gave me the su=
perpowers to finally fix =3D)<br><br>If you slow a video down =E2=80=94 say=
 to 0.25x =E2=80=94 and then seek, VLC freezes at the target position for a=
 long time before playback resumes: several seconds with short-GOP files, a=
nd 30 seconds or more with the 5-10 second GOPs typical of phone recordings=
 and web video. The slower the rate, the longer the freeze.=C2=A0<br><br>Wh=
at happens: after a precise seek, the demuxer restarts at the previous keyf=
rame, and EsOutDecodersStopBuffering() rebases the clock origin by subtract=
ing i_buffering_duration =E2=80=94 a stream-time quantity =E2=80=94 from a =
system-time date, without scaling by the playback rate. Timestamp conversio=
n then stretches the keyframe-to-target preroll by 1/rate, so playback stal=
ls for exactly preroll * (1/rate - 1). The pts_delay part of that compensat=
ion is fine as-is (the input clock already compensates it on every conversi=
on via ClockGetTsOffset(), which is also why caching settings don&#39;t aff=
ect the stall), so the fix scales only the preroll term. At 1.0x the added =
term is zero and nothing changes; at rates above 1x the same math currently=
 makes the first post-seek frames late by the mirrored amount, which this a=
lso fixes.<br><br>A note on the filing of #21890: it is labeled Demux: Adap=
tive because it was reported against an HLS stream in 2019, but the stall r=
eproduces on a plain local mp4 =E2=80=94 the root cause is in core es_out/c=
lock code, not the adaptive demuxer. That mislabeling may be part of why it=
 has sat unfixed for so long. The same defect is behind the slow-motion see=
k freezes reported against VLC-Android.<br><br>Measured with a headless lib=
vlc driver polling media_player_get_time() every 250 ms around a seek to 60=
s (local mp4, 1.67 s preroll, file-caching 1000; &quot;stall&quot; =3D wall=
 time until the reported time advanced 200 ms past the target):<br><br>  ma=
ster (8115e53):   unpatched   patched<br>    1.0x              1.75 s      =
1.75 s   (unchanged)<br>    2.0x              1.50 s      1.50 s   (unchang=
ed)<br>    0.25x             6.25 s      2.25 s<br>    0.1x              18=
.0 s      2.5 s<br><br>The patched residual at slow rates is the measuremen=
t floor itself: advancing 200 ms of stream time takes 0.8 s / 2 s of wall t=
ime at 0.25x / 0.1x. The same fix was also verified on the 3.0.x branch wit=
h a rebuilt libvlccore 3.0.20: the 0.25x stall dropped from ~5-6 s to under=
 1 s, and 0.1x from ~15-17 s to under 2 s. A formatted 3.0.x backport is re=
ady =E2=80=94 happy to send it as a follow-up patch or open a [3.0] merge r=
equest.<br><br>Quick repro: generate a two-minute test clip with &quot;ffmp=
eg -f lavfi -i testsrc2=3Dduration=3D120:size=3D640x360:rate=3D30 -f lavfi =
-i sine=3Dfrequency=3D440:duration=3D120 -c:v libx264 -preset ultrafast -c:=
a aac test120.mp4&quot;, play it, set rate 0.25, seek to ~60s. Running with=
 --input-fast-seek avoids the stall entirely (keyframe seeks have no prerol=
l), which isolates the preroll term.<br><br>The patch is attached (git form=
at-patch against master 8115e53) rather than inline because this message is=
 sent through a mailer that can&#39;t guarantee inline whitespace survives;=
 the full commit message and measurements are inside it, and I can resend i=
nline via git send-email if that&#39;s preferred.<br><br>Two disclosures: I=
 prepared and validated this with AI assistance (noted in a Co-authored-by =
trailer), and I have reviewed the change and the measurements myself. I als=
o have a <a href=3D"https://www.google.com/url?q=3Dhttp://code.videolan.org=
&amp;source=3Dgmail&amp;ust=3D1783552830165000&amp;sa=3DE" target=3D"_blank=
" rel=3D"noreferrer">https://www.google.com/url?q=3Dhttp://code.videolan.or=
g&amp;source=3Dgmail&amp;ust=3D1783552830165000&amp;sa=3DE</a> account pend=
ing administrator approval =E2=80=94 if a merge request is more convenient =
for review, I&#39;d gladly resubmit there once it&#39;s approved.<br><br>Th=
anks!<br>Ismail<br></div></div></div>

--00000000000028c2b006560db879--

--00000000000028c2b106560db87b
Content-Type: text/x-patch; charset="US-ASCII"; name="0001-es_out-scale-preroll-master.patch"
Content-Disposition: attachment; 
	filename="0001-es_out-scale-preroll-master.patch"
Content-Transfer-Encoding: base64
Content-ID: <>
X-Attachment-Id: 

RnJvbSA4MjdlMDZjY2MzYzY2ZjcwZmI4YjVjMjhhNzI3NzIxZWI5MzNmMWNmIE1vbiBTZXAgMTcg
MDA6MDA6MDAgMjAwMQpGcm9tOiBJc21haWwgRGVnYW5pIDxkZWdhbmlpQGdtYWlsLmNvbT4KRGF0
ZTogU3VuLCA1IEp1bCAyMDI2IDAxOjM4OjAxICswMDAwClN1YmplY3Q6IFtQQVRDSF0gZXNfb3V0
OiBzY2FsZSBwcmVyb2xsIGNsb2NrLW9yaWdpbiBjb21wZW5zYXRpb24gYnkgdGhlCiBwbGF5YmFj
ayByYXRlCgpBZnRlciBhIHNlZWssIEVzT3V0RGVjb2RlcnNTdG9wQnVmZmVyaW5nKCkgcmViYXNl
cyB0aGUgY2xvY2sgc3lzdGVtCm9yaWdpbiBieSBzdWJ0cmFjdGluZyBpX2J1ZmZlcmluZ19kdXJh
dGlvbiwgYSBzdHJlYW0tdGltZSBkdXJhdGlvbiwgZnJvbQphIHN5c3RlbS10aW1lIGRhdGUgd2l0
aG91dCBhY2NvdW50aW5nIGZvciB0aGUgcGxheWJhY2sgcmF0ZS4gVGltZXN0YW1wCmNvbnZlcnNp
b24gdGhlbiBzdHJldGNoZXMgdGhlIHByZXJvbGwgcG9ydGlvbiBieSAxL3JhdGUsIHNvIGF0IHJh
dGVzCmJlbG93IDEuMCBldmVyeSBwcmVjaXNlIHNlZWsgc3RhbGxzIHBsYXliYWNrIGZvcgppX3By
ZXJvbGxfZHVyYXRpb24gKiAoMS9yYXRlIC0gMSkgd2hpbGUgdGhlIHByZXJvbGwgaXMgcmVwbGF5
ZWQgaW4Kc3RyZXRjaGVkIHdhbGwgdGltZS4KCldpdGggYSAxLjY3cyBwcmVyb2xsIHRoaXMgbWVh
c3VyZXMgYXMgYSB+NXMgZnJlZXplIGF0IDAuMjV4IGFuZCBhIH4xNnMKZnJlZXplIGF0IDAuMXg7
IHdpdGggdHlwaWNhbCA1LTEwcyBHT1BzIHRoZSBmcmVlemUgcmVhY2hlcyAxNS02MHMsCm1ha2lu
ZyBzZWVraW5nIGVmZmVjdGl2ZWx5IHVudXNhYmxlIGluIHNsb3cgbW90aW9uIChpc3N1ZSAjMjE4
OTAsIGFsc28KcmVwb3J0ZWQgYWdhaW5zdCB2bGMtYW5kcm9pZCkuCgpTY2FsZSBvbmx5IHRoZSBw
cmVyb2xsIHBhcnQgb2YgdGhlIGNvbXBlbnNhdGlvbiBieSB0aGUgcmF0ZS4gVGhlCnB0c19kZWxh
eSBwYXJ0IG11c3Qgc3RheSB1bnNjYWxlZCBiZWNhdXNlIHRoZSBpbnB1dCBjbG9jayBhbHJlYWR5
CmNvbXBlbnNhdGVzIGl0IG9uIGV2ZXJ5IGNvbnZlcnNpb24gdmlhIENsb2NrR2V0VHNPZmZzZXQo
KTsgc2NhbGluZyBpdApoZXJlIHRvbyB3b3VsZCBtYWtlIHRoZSBmaXJzdCBmcmFtZXMgbGF0ZSBi
eSBwdHNfZGVsYXkgKiAoMS9yYXRlIC0gMSkuCgpWZXJpZmllZCBvbiBtYXN0ZXIgKDgxMTVlNTMp
IHdpdGggYSBoZWFkbGVzcyBsaWJ2bGMgZHJpdmVyIHBvbGxpbmcKbWVkaWFfcGxheWVyX2dldF90
aW1lKCkgZXZlcnkgMjUwbXMgYXJvdW5kIGEgc2V0X3RpbWUoKSB0byA2MHMgb24gYQpsb2NhbCBt
cDQgKDEuNjdzIHByZXJvbGwsIGZpbGUtY2FjaGluZyAxMDAwKS4gV2FsbCB0aW1lIHVudGlsIHRo
ZQpyZXBvcnRlZCB0aW1lIGFkdmFuY2VkIDIwMG1zIHBhc3QgdGhlIHNlZWsgdGFyZ2V0OgoKICBy
YXRlICAgIGJlZm9yZSAgICAgIGFmdGVyCiAgMS4weCAgICAxLjc1cyAgICAgICAxLjc1cyAgICh1
bmNoYW5nZWQpCiAgMi4weCAgICAxLjUwcyAgICAgICAxLjUwcyAgICh1bmNoYW5nZWQpCiAgMC4y
NXggICA2LjI1cyAgICAgICAyLjI1cwogIDAuMXggICAgMTguMHMgICAgICAgMi41cwoKKHRoZSBy
ZXNpZHVhbCBhdCBzbG93IHJhdGVzIGlzIHRoZSBtZWFzdXJlbWVudCBpdHNlbGY6IGFkdmFuY2lu
ZyAyMDBtcwpvZiBzdHJlYW0gdGltZSB0YWtlcyAwLjhzIC8gMnMgb2Ygd2FsbCB0aW1lIGF0IDAu
MjV4IC8gMC4xeC4pCgpUaGUgc2FtZSBmaXggd2FzIHByZXZpb3VzbHkgdmVyaWZpZWQgYWdhaW5z
dCB0aGUgMy4wLnggYnJhbmNoIHdpdGggYQpwYXRjaGVkIGxpYnZsY2NvcmUgMy4wLjIwOiBzdGFs
bCBhdCAwLjI1eCBkcm9wcGVkIGZyb20gfjUtNnMgdG8gdW5kZXIKMXMsIGF0IDAuMXggZnJvbSB+
MTUtMTdzIHRvIHVuZGVyIDJzLgoKQ28tYXV0aG9yZWQtYnk6IENsYXVkZSA8bm9yZXBseUBhbnRo
cm9waWMuY29tPgotLS0KIHNyYy9pbnB1dC9lc19vdXQuYyB8IDEwICsrKysrKysrKy0KIDEgZmls
ZSBjaGFuZ2VkLCA5IGluc2VydGlvbnMoKyksIDEgZGVsZXRpb24oLSkKCmRpZmYgLS1naXQgYS9z
cmMvaW5wdXQvZXNfb3V0LmMgYi9zcmMvaW5wdXQvZXNfb3V0LmMKaW5kZXggZGIyZmY5MC4uOTA4
MDc4ZCAxMDA2NDQKLS0tIGEvc3JjL2lucHV0L2VzX291dC5jCisrKyBiL3NyYy9pbnB1dC9lc19v
dXQuYwpAQCAtMTIxOCw3ICsxMjE4LDE1IEBAIHN0YXRpYyB2b2lkIEVzT3V0RGVjb2RlcnNTdG9w
QnVmZmVyaW5nKGVzX291dF9zeXNfdCAqcF9zeXMsIGJvb2wgYl9mb3JjZWQpCiAgICAgLyogKi8K
ICAgICBjb25zdCB2bGNfdGlja190IGlfY3VycmVudF9kYXRlID0gcF9zeXMtPmJfcGF1c2VkID8g
cF9zeXMtPmlfcGF1c2VfZGF0ZSA6IHZsY190aWNrX25vdygpOwogCi0gICAgY29uc3QgdmxjX3Rp
Y2tfdCB1cGRhdGUgPSBpX2N1cnJlbnRfZGF0ZSAtIGlfYnVmZmVyaW5nX2R1cmF0aW9uOworICAg
IC8qIFRoZSBidWZmZXJlZCBkYXRhIHdpbGwgYmUgY29uc3VtZWQgYXQgdGhlIGN1cnJlbnQgcGxh
eWJhY2sgcmF0ZTogc2NhbGUKKyAgICAgKiB0aGUgb3JpZ2luIGNvbXBlbnNhdGlvbiBmb3IgdGhl
IHByZXJvbGwgcGFydCBhY2NvcmRpbmdseSwgb3RoZXJ3aXNlIGEKKyAgICAgKiBwcmVjaXNlIHNl
ZWsgYXQgcmF0ZSAhPSAxLjAgcmVwbGF5cyB0aGUgcHJlcm9sbCAoa2V5ZnJhbWUgLT4gc2Vlawor
ICAgICAqIHRhcmdldCkgc3RyZXRjaGVkIGJ5IDEvcmF0ZSBpbiB3YWxsIHRpbWUsIHN0YWxsaW5n
IHBsYXliYWNrIGZvcgorICAgICAqIGlfcHJlcm9sbF9kdXJhdGlvbiAqICgxL3JhdGUgLSAxKSAo
c2VlIGlzc3VlICMyMTg5MCkuIFRoZSBwdHNfZGVsYXkKKyAgICAgKiBwYXJ0IG11c3QgTk9UIGJl
IHNjYWxlZCBoZXJlOiB0aGUgaW5wdXQgY2xvY2sgYWxyZWFkeSBjb21wZW5zYXRlcyBpdAorICAg
ICAqIG9uIHRpbWVzdGFtcCBjb252ZXJzaW9uIChDbG9ja0dldFRzT2Zmc2V0KS4gKi8KKyAgICBj
b25zdCB2bGNfdGlja190IHVwZGF0ZSA9IGlfY3VycmVudF9kYXRlIC0gaV9idWZmZXJpbmdfZHVy
YXRpb24KKyAgICAgICAgLSAodmxjX3RpY2tfdCkoaV9wcmVyb2xsX2R1cmF0aW9uICogKDEuMCAv
IHBfc3lzLT5yYXRlIC0gMS4wKSk7CiAKICAgICAvKiBUaGUgY2FsbCBvcmRlciBvZiB0aGVzZSAz
IGlucHV0X2Nsb2NrX3QvdmxjX2Nsb2NrX21haW5fdCBmdW5jdGlvbnMgaXMKICAgICAgKiBpbXBv
cnRhbnQ6Ci0tIAoyLjQzLjAKCg==
--00000000000028c2b106560db87b
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
vlc-devel mailing list
To unsubscribe or modify your subscription options:
https://mailman.videolan.org/listinfo/vlc-devel

--00000000000028c2b106560db87b--