Skip to content

Commit cd15113

Browse files
fix: playbackState is Playing only while a running session has rendered, on both platforms
1 parent 9220b10 commit cd15113

4 files changed

Lines changed: 27 additions & 12 deletions

File tree

‎android/src/main/java/com/margelo/nitro/webmplayer/HybridWebmPlayer.kt‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ class HybridWebmPlayer : HybridWebmPlayerSpec() {
4141
private var surface: PlayerView? = null
4242
private var context: Context? = null
4343
private var key: String? = null
44-
private var source: WorkletSource? = null
44+
// Read by the playbackState getter on the JS thread.
45+
@Volatile private var source: WorkletSource? = null
4546
private var broadcastEnded = false
4647
private var focusLost = false
4748
private var rejoinAttempts = 0
@@ -71,7 +72,9 @@ class HybridWebmPlayer : HybridWebmPlayerSpec() {
7172
get() = when {
7273
stats.failed.get() -> WebmPlaybackState.FAILED
7374
!active.get() -> WebmPlaybackState.IDLE
74-
stats.rendered.get() -> WebmPlaybackState.PLAYING
75+
// A re-attached surface re-renders the last frame, so rendered alone
76+
// would read Playing while the source is stopped for a focus loss.
77+
stats.rendered.get() && source != null -> WebmPlaybackState.PLAYING
7578
else -> WebmPlaybackState.BUFFERING
7679
}
7780

@@ -157,6 +160,9 @@ class HybridWebmPlayer : HybridWebmPlayerSpec() {
157160
private fun stopSource() {
158161
source?.stop()
159162
source = null
163+
// Nothing renders until the next session's first frame: a pending rejoin
164+
// and a focus loss read Buffering, not Playing.
165+
stats.rendered.set(false)
160166
}
161167

162168
private fun fail(detail: String) {

‎docs/DEVICE_VALIDATION.md‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,16 @@ broadcast as the iPhone runs, over `adb logcat -s WebmPlayer ReactNativeJS`.
253253
reported buffering every two seconds and the worklet's lag reached 13 s,
254254
227 fragments, and rejoined; playback was clean before and after. Not
255255
chased: Android tuning is outside this round.
256-
- **`playbackState` reads Playing while nothing plays**, on both platforms:
257-
it is "something has rendered this session", so it stays Playing through
258-
an interruption, a focus loss and, on iOS, a rejoin (Android's
259-
`newSession` clears it there). Health is right in all three cases. A
260-
follow-up for the spec: Buffering whenever the source is stopped or a
261-
rejoin is pending.
256+
- **`playbackState` read Playing while nothing played**, on both platforms:
257+
it was "something has rendered this session", so it stayed Playing through
258+
an interruption, a focus loss and, on iOS, a rejoin. Now Playing only
259+
while the session's source is running and has rendered (iOS: a source and
260+
`_clockStarted`; Android: `rendered` cleared when the source stops, plus a
261+
running source, since a re-attached surface re-renders the last frame and
262+
set `rendered` again). Verified the same evening: Android read Buffering
263+
through a YouTube focus loss and after returning to the app (log); the
264+
iPhone read Buffering from "stream stalled" through the rejoin and Playing
265+
after "stream resumed" (on screen; the console dropped with the Wi-Fi).
262266

263267
## Still to run on hardware
264268

‎ios/WebmPlaybackEngine.mm‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,10 @@ - (WebmPlaybackState)playbackState {
288288
// Checked before _running so a failure survives stop(), as on Android; play() clears it.
289289
if (_failed.load()) return WebmPlaybackStateFailed;
290290
if (!_running) return WebmPlaybackStateIdle;
291-
// From the decoders, not mirrored counters: an audio-only stream must leave Buffering too.
292-
if (_videoDecoder.framesPresented == 0 && _audioDecoder.packetsDecoded == 0) {
293-
return WebmPlaybackStateBuffering;
294-
}
291+
// Playing only while a session's source is running and has decoded its
292+
// first sample: the join, a pending rejoin and an interruption are all
293+
// Buffering, as on Android. _clockStarted is per session (reset re-arms it).
294+
if (!_source || !_clockStarted.load()) return WebmPlaybackStateBuffering;
295295
return WebmPlaybackStatePlaying;
296296
}
297297

‎src/specs/webm-player.nitro.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ export interface WebmPlayer extends HybridObject<{
6666
play(key: string): void
6767
stop(): void
6868

69+
/**
70+
* Playing only while a joined session is running and has rendered. Joining,
71+
* a pending rejoin, an interruption and a focus loss are Buffering; a
72+
* finished broadcast is Idle; Failed survives stop() until the next play().
73+
*/
6974
readonly playbackState: WebmPlaybackState
7075
muted: boolean
7176

0 commit comments

Comments
 (0)