Skip to content

Dynamic scheduling causes getCurrentPosition() to be stale by up to 200ms #3286

Description

@nift4

Version

Media3 main branch

More version details

f6ad726

Devices that reproduce the issue

All

Devices that do not reproduce the issue

N/A

Reproducible in the demo app?

Yes

Reproduction steps

Use attached patch for session-demo and play any song:

diff --git a/demos/session_service/src/main/java/androidx/media3/demo/session/DemoPlaybackService.kt b/demos/session_service/src/main/java/androidx/media3/demo/session/DemoPlaybackService.kt
index 81502f5ee8..d26955c1ad 100644
--- a/demos/session_service/src/main/java/androidx/media3/demo/session/DemoPlaybackService.kt
+++ b/demos/session_service/src/main/java/androidx/media3/demo/session/DemoPlaybackService.kt
@@ -34,6 +34,7 @@ import androidx.media3.cast.CastPlayer
 import androidx.media3.common.AudioAttributes
 import androidx.media3.common.Player
 import androidx.media3.common.listenTo
+import androidx.media3.common.util.Log
 import androidx.media3.common.util.UnstableApi
 import androidx.media3.demo.session.service.R
 import androidx.media3.exoplayer.ExoPlayer
@@ -47,6 +48,7 @@ import java.io.InputStream
 import java.io.OutputStream
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.guava.await
 import kotlinx.coroutines.launch
@@ -172,6 +174,12 @@ open class DemoPlaybackService : MediaLibraryService() {
         mediaLibrarySession.setCustomShuffleModeButton()
       }
     }
+    lifecycleScope.launch {
+      while (true) {
+        Log.i("hi", "current pos = " + player.currentPosition)
+        delay(10)
+      }
+    }
   }
 
   @OptIn(UnstableApi::class)
@@ -179,6 +187,7 @@ open class DemoPlaybackService : MediaLibraryService() {
     val exoPlayer =
       ExoPlayer.Builder(this)
         .setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true)
+        //.experimentalSetDynamicSchedulingEnabled(false)
         .build()
     exoPlayer.addAnalyticsListener(EventLogger())
     return CastPlayer.Builder(/* context= */ this).setLocalPlayer(exoPlayer).build()

Expected result

Increasing timestamps:

2026-06-19 20:21:17.236 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 64954
2026-06-19 20:21:17.249 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 64976
2026-06-19 20:21:17.259 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 64986
2026-06-19 20:21:17.270 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 64996
2026-06-19 20:21:17.281 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 65006
2026-06-19 20:21:17.291 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 65016
2026-06-19 20:21:17.302 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 65027
2026-06-19 20:21:17.313 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 65038
2026-06-19 20:21:17.323 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 65048
2026-06-19 20:21:17.334 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 65059
2026-06-19 20:21:17.344 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 65071
2026-06-19 20:21:17.356 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 65082
2026-06-19 20:21:17.367 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 65094
2026-06-19 20:21:17.379 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 65106
2026-06-19 20:21:17.389 31319-31319 hi                      androidx.media3.demo.session         I  current pos = 65116

(This can be achieved by disabling dynamic scheduling, by uncommenting the line in the patch for it. Notably dynamic scheduling is disabled by default in 1.10.1 so this hits anyone upgrading from 1.10.1 to 1.11.0 alphas.)

This is important for me because I use ExoPlayer.getCurrentPosition() for A/V-synchronized rendering of lyrics, where I call getCurrentPosition() in onDraw(), that is, 60 times per second. Until dynamic scheduling was added, this worked just fine, now it no longer does.

Actual result

Polling repeatedly gets the same position and it only updates roughly 5 times per second:


2026-06-19 20:22:23.258 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.269 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.281 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.291 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.303 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.315 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.326 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.337 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.348 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.359 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.370 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.380 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.391 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.401 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.412 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.423 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.433 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.445 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1581
2026-06-19 20:22:23.455 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1840
2026-06-19 20:22:23.468 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1840
2026-06-19 20:22:23.480 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1840
2026-06-19 20:22:23.493 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1840
2026-06-19 20:22:23.504 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1840
2026-06-19 20:22:23.514 31426-31426 hi                      androidx.media3.demo.session         I  current pos = 1840

One possible fix for this issue is, it indeed does work, but I am not sure if it's correct.

diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
index d9755dc81a..dd531816f7 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
@@ -2205,10 +2205,7 @@ import java.util.function.IntConsumer;
       return Util.msToUs(maskingWindowPositionMs);
     }
 
-    long positionUs =
-        playbackInfo.sleepingForOffload
-            ? playbackInfo.getEstimatedPositionUs()
-            : playbackInfo.positionUs;
+    long positionUs = playbackInfo.getEstimatedPositionUs();
 
     if (playbackInfo.periodId.isAd()) {
       return positionUs;

Media

Anything works

Bug Report

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions