This prevents PlaybackManager's seek while enabling an audio track from
causing the AudioMixingSink to push audio blocks forward unnecessarily.
Previously, the seek would cause the initial block or blocks to repeat
from the perspective of AudioMixingSink, so it would think that it
needs to shift the first block after the seek forward by a few samples.
By moving this to the AudioDataProvider, we can clear the last sample
index every time the decoder is flushed, ensuring that the block
shifting always makes sense.
By doing this in AudioMixingSink instead of the Decoder
implementations, we avoid having to duplicate this shifting logic
across multiple implementations.
This also fixes an issue where multiple audio blocks occupying the same
timestamp would be skipped while seeking, causing a significant break
in audio.
With the previous setup setting the time directly on the main thread,
the following could occur:
- HTMLMediaElement temporarily pauses playback to begin a seek.
- AudioMixingSink starts an audio task to drain audio and suspend.
- HTMLMediaElement starts PlaybackManager seeking to a new position.
- AudioDataProvider completes the seek to the new position.
- The PlaybackManager tells AudioMixingSink to set the media time,
which it does so synchronously on the main thread.
- At this point, the time provider corresponds to the new position.
- The pause completes, and a deferred invocation sets media time to
its old position again.
This would result in the timeline showing the wrong position after a
seek on rare occasions.
Instead, always queue up a drain and suspend when setting the sink's
media time. This ensures that updating the time always occurs after the
pause has completed.
Also, since setting the time is asynchronous now, we need to store the
target time until the seeking drain completes. Otherwise, we still
briefly see the previous playback position after a seek.
This commit implements the functionality to play back audio through
PlaybackManager.
To decode the audio data, AudioDataProviders are created for each track
in the provided media data. These providers will fill their audio block
queue, then sit idle until their corresponding tracks are enabled.
In order to output the audio, one AudioMixingSink is created which
manages a PlaybackStream which requests audio blocks from multiple
AudioDataProviders and mixes them into one buffer with sample-perfect
precision.