CastPlayer: Scrubbing (Seeking) is Non-Responsive for Live Stream
Version
Media3 1.8.0
More version details
When casting the a live HLS live stream (with a large window), seeking (scrubbing) within the timeline does not work. Observation: In the demo-cast app, the scrub bar is simply disabled, so we cant reproduce. In our project, the receiver does not respond to any scrubbing attempts. Debugging indicates the seek command is never sent to the cast receiver.
This led us to implement a custom logic using CastTimelineTracker to enable seeking.
Devices that reproduce the issue
Any
Devices that do not reproduce the issue
No response
Reproducible in the demo app?
No
Reproduction steps
Ensure that seeking is supported by the cast-receiver. addSupportedMediaCommands(ALL_BASIC_MEDIA)
Cast the stream to the chromecast device. Once playback begins, attempt to scrub (seek) backward or forward in the timeline.
Expected result
When scrubbing in a timeline it should be received by the chromecast device.
Actual result
Scrubbing actions do not result in a seek; the command appears to not be sent to the receiver (in our app).
Media
Stream details: ~30-minute window, 6-second segments, audio only
Bug Report
- [ ] You will email the zip file produced by
adb bugreportto [email protected] after filing this issue.
Note. To make the live audio hls scrubable on the cast receiver (my chrome-cast guy said) we added this:
playbackConfig.shakaConfig = {
manifest: {
// In Shaka player, we cannot seek inside HLS. Google has implemented this by cutting the buffer short. By specifying a window override, we can allow seeking in all of the buffer
availabilityWindowOverride: 1800,
hls: {
// The duration of the segments and the date time are not totally in sync, so we need to disable this check, otherwise the playback will start looping after playing 2-3 segments
ignoreManifestProgramDateTime: true
}
},
streaming: {
rebufferingGoal: 18,
bufferingGoal: 18,
bufferBehind: 30,
}
}
Ensure that seeking is supported by the cast-receiver.
addSupportedMediaCommands(ALL_BASIC_MEDIA)
I'm pretty sure (Remote)CastPlayer can still remove the seek command(s) if it doesn't think the content it's playing is seekable. This is tested here:
https://github.com/androidx/media/blob/bfe5930f7f29c6492d60e3d01a90abd3c138b615/libraries/cast/src/test/java/androidx/media3/cast/CastPlayerTest.java#L1506-L1526
I suspect that if you call isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM) before you call seekTo it will return false.
This would also explain why the demo app disables the scrubber bar (because the command is unavailable):
In the demo-cast app, the scrub bar is simply disabled, so we cant reproduce.
So I think this issue is more about "why doesn't CastPlayer think the content is seekable?"
The command is added/removed by this code:
https://github.com/androidx/media/blob/bfe5930f7f29c6492d60e3d01a90abd3c138b615/libraries/common/src/main/java/androidx/media3/common/util/Util.java#L3685
Which depends on Player.isCurrentMediaItemSeekable(), which is implemented by BasePlayer (superclass of (Remote)CastPlayer) here:
https://github.com/androidx/media/blob/bfe5930f7f29c6492d60e3d01a90abd3c138b615/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java#L380-L384
Which leads to the CastTimeline implementation of isSeekable, which leads here:
https://github.com/androidx/media/blob/bfe5930f7f29c6492d60e3d01a90abd3c138b615/libraries/cast/src/main/java/androidx/media3/cast/CastTimeline.java#L164
"seekable" and "dynamic" are not the same concept - but it looks like cast has assumed a dynamic timeline is unseekable since 2017: https://github.com/androidx/media/commit/aafdd2267abd6c9191ef66e9cf109cc24e159e30
Assigning to @AquilesCanta to take a deeper look.
I guess this effectively makes this a feature request?
After tinkering with the code, I found I can retrieve somewhat usable data using RemoteMediaClient.getApproximateLiveSeekableRangeStart(), RemoteMediaClient.getApproximateLiveSeekableRangeEnd(), and RemoteMediaClient.getApproximateStreamPosition(). However, these values are offsets relative to the start of the stream session rather than absolute timestamps.
If I need the actual "Wall Clock" time of the content currently playing, my best option right now is to estimate it by comparing getApproximateLiveSeekableRangeEnd() against the device's current system time.
The issue is that this calculation almost always results in a slight mismatch compared to the time displayed on the Cast Receiver, presumably because the Receiver derives its time directly from the HLS timecodes.
Is there a way to retrieve this precise wall-clock time directly from the client, or would exposing that require a new feature?