Skip to content

Resume discards the playback rate: play() is rate 1.0, and the host re-issues it from paths a client can't see #436

Description

@rrgomes

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions