Measured on 6.45.0, Mac Catalyst, native path, on a 1080p24 H.264 MKV. Setting a
rate through setRate holds until playback is paused; the resume comes back at
1.0.
Rate survives a great deal — including two far seeks that re-aimed and restarted
the producer (producerShift=-14.056, rate 1.5 throughout). It is specifically
pause/resume that loses it:
01:47:11.937 rate=1.5 (holding, after both seeks)
01:47:11.967 [NativeAVPlayerHost] #1 rate=0.0 pause
01:47:58.069 [NativeAVPlayerHost] #1 rate=1.0 resume — not 1.5
01:47:58.325 rate=1.0 … and stays 1.0
That much is expected from AVPlayer.play(), which is rate = 1.0 by
definition, and the host latches a boolean playIntent rather than a rate.
What we did not expect is that a host-side workaround cannot hold it.
Re-applying our intended rate immediately after the resume, plus a backstop
wherever the engine settles on .playing:
02:05:09.925 resume → rate=1.0
02:05:09.925 we set rate=1.5
02:05:09.970 rate=1.0 ← reset again, 45 ms later, from a play() we did not issue
02:05:09.970 we set rate=1.5
02:05:12.934 [FreezeDiag] trigger=stall state=playing frozenFor=3.0s rate=1.5
02:05:14.370 rate=1.0 ← final, and the phase never changed so nothing re-asserted
Three things follow. The rate is reset more than once per resume, from paths a
client cannot observe, so one write cannot hold it. A client backstop keyed on
the playback phase cannot catch the last reset, because the phase stays
.playing across the whole sequence. And the resume where we wrote rate three
times in 45 ms froze the clock for three seconds at the resume instant — an
identical earlier flap did not, so we are not claiming causation, but writing
rate inside AVPlayer's resume window is a poor place for a client to be.
We have reverted our workaround rather than ship it.
The ask is the pattern the engine already has for the neighbouring case:
desiredVolume with applyDesiredVolume(to:) exists so volume is re-applied to
whatever transport is current, and there is no desiredRate. Applying the
intended rate at the engine's own play sites would put it where the re-issues
already are.
Context, in case it changes the priority: this is for a group-watch feature where
one rate is shared across peers. A peer silently returning to 1.0 on any pause —
and a room pauses often, deliberately and for buffering — diverges from everyone
else and then gets pulled back by drift correction.
Measured on 6.45.0, Mac Catalyst, native path, on a 1080p24 H.264 MKV. Setting a
rate through
setRateholds until playback is paused; the resume comes back at1.0.
Rate survives a great deal — including two far seeks that re-aimed and restarted
the producer (
producerShift=-14.056, rate 1.5 throughout). It is specificallypause/resume that loses it:
That much is expected from
AVPlayer.play(), which israte = 1.0bydefinition, and the host latches a boolean
playIntentrather than a rate.What we did not expect is that a host-side workaround cannot hold it.
Re-applying our intended rate immediately after the resume, plus a backstop
wherever the engine settles on
.playing:Three things follow. The rate is reset more than once per resume, from paths a
client cannot observe, so one write cannot hold it. A client backstop keyed on
the playback phase cannot catch the last reset, because the phase stays
.playingacross the whole sequence. And the resume where we wrote rate threetimes in 45 ms froze the clock for three seconds at the resume instant — an
identical earlier flap did not, so we are not claiming causation, but writing
rate inside AVPlayer's resume window is a poor place for a client to be.
We have reverted our workaround rather than ship it.
The ask is the pattern the engine already has for the neighbouring case:
desiredVolumewithapplyDesiredVolume(to:)exists so volume is re-applied towhatever transport is current, and there is no
desiredRate. Applying theintended rate at the engine's own play sites would put it where the re-issues
already are.
Context, in case it changes the priority: this is for a group-watch feature where
one rate is shared across peers. A peer silently returning to 1.0 on any pause —
and a room pauses often, deliberately and for buffering — diverges from everyone
else and then gets pulled back by drift correction.