Repository navigation
MediaController generates wrong onMediaItemTransition() callback on seek inside same media item #3248
Copy link
Copy link
Open
Labels
Description
Version
Media3 1.10.1
More version details
No response
Devices that reproduce the issue
API 33 emulator
Devices that do not reproduce the issue
N/A
Reproducible in the demo app?
Yes
Reproduction steps
- Apply attached patch to session-demo app
- Play any song from an artist with more than one song, choose a song that is not the first in the list
- Seek inside this same song to some other position using seekbar
- onMediaItemTransition() is called on controller at this point
Note: to reproduce this, controller must be attached before setMediaItems happens, which is why I added a new controller in the patch. Also, index of song passed to setMediaItems must be >0.
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..f49cc17e60 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
@@ -23,6 +23,7 @@ import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.os.Build
+import android.util.Log
import androidx.annotation.OptIn
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
@@ -32,6 +33,7 @@ import androidx.datastore.dataStore
import androidx.lifecycle.lifecycleScope
import androidx.media3.cast.CastPlayer
import androidx.media3.common.AudioAttributes
+import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.listenTo
import androidx.media3.common.util.UnstableApi
@@ -39,6 +41,7 @@ import androidx.media3.demo.session.service.R
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.util.EventLogger
import androidx.media3.session.CommandButton
+import androidx.media3.session.MediaController
import androidx.media3.session.MediaLibraryService
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSession.ControllerInfo
@@ -54,6 +57,7 @@ import kotlinx.coroutines.launch
open class DemoPlaybackService : MediaLibraryService() {
private lateinit var mediaLibrarySession: MediaLibrarySession
+ private lateinit var controller: MediaController
private val turnShuffleOnButton by
lazy @OptIn(UnstableApi::class) {
@@ -146,6 +150,7 @@ open class DemoPlaybackService : MediaLibraryService() {
@OptIn(UnstableApi::class)
override fun onDestroy() {
getBackStackedActivity()?.let { mediaLibrarySession.setSessionActivity(it) }
+ controller.release()
mediaLibrarySession.release()
mediaLibrarySession.player.release()
clearListener()
@@ -165,6 +170,17 @@ open class DemoPlaybackService : MediaLibraryService() {
MediaLibrarySession.Builder(this, player, createLibrarySessionCallback())
.also { builder -> getSingleTopActivity()?.let { builder.setSessionActivity(it) } }
.build()
+ controller = MediaController.Builder(this, mediaLibrarySession.token)
+ .buildAsync().get()
+ controller.addListener(object : Player.Listener {
+
+ override fun onMediaItemTransition(
+ mediaItem: MediaItem?,
+ reason: @Player.MediaItemTransitionReason Int
+ ) {
+ androidx.media3.common.util.Log.e("hi","[CONTROLLER] media item changed due to $reason")
+ }
+ })
mediaLibrarySession.setCustomShuffleModeButton()
lifecycleScope.launch {
@@ -181,6 +197,14 @@ open class DemoPlaybackService : MediaLibraryService() {
.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true)
.build()
exoPlayer.addAnalyticsListener(EventLogger())
+ exoPlayer.addListener(object : Player.Listener {
+ override fun onMediaItemTransition(
+ mediaItem: MediaItem?,
+ reason: @Player.MediaItemTransitionReason Int
+ ) {
+ Log.e("hi", "[PLAYER] media item transition due to $reason")
+ }
+ })
return CastPlayer.Builder(/* context= */ this).setLocalPlayer(exoPlayer).build()
}
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java
index f1361dedec..7ad9006381 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java
@@ -2530,6 +2530,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
}
MediaItem currentMediaItem = newPlayerInfo.getCurrentMediaItem();
if (mediaItemTransitionReason != null) {
+ Log.i("hi","dispatch mit");
listeners.queueEvent(
/* eventFlag= */ Player.EVENT_MEDIA_ITEM_TRANSITION,
listener -> listener.onMediaItemTransition(currentMediaItem, mediaItemTransitionReason));
@@ -2991,6 +2992,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|| positionDiscontinuityReason == DISCONTINUITY_REASON_SEEK)) {
if (oldPlayerInfo.newPositionInfo.mediaItemIndex
!= finalPlayerInfo.newPositionInfo.mediaItemIndex) {
+ Log.e("hi", "move from "+oldPlayerInfo.newPositionInfo.mediaItemIndex+ " to "+finalPlayerInfo.newPositionInfo.mediaItemIndex);
mediaItemTransitionReason =
positionDiscontinuityReason == DISCONTINUITY_REASON_AUTO_TRANSITION
? MEDIA_ITEM_TRANSITION_REASON_AUTOExpected result
No onMediaItemTransition() callback
Actual result
2026-05-29 18:54:24.382 12519-12519 hi androidx.media3.demo.session E move from 0 to 1
2026-05-29 18:54:24.382 12519-12519 hi androidx.media3.demo.session I dispatch mit
2026-05-29 18:54:24.382 12519-12519 hi androidx.media3.demo.session E [CONTROLLER] media item changed due to 2
Media
Anything works
Bug Report
- You will email the zip file produced by
adb bugreportto android-media-github@google.com after filing this issue.
Reactions are currently unavailable
Activity
marcbaechinger commentedon Jun 16, 2026
Thanks for your excellent report!
I can repro this. When the controller is connected before media items are set, the initial window index that is sent from the session to the controller is 0. When then
setMediaItems(items, windowIndexNotEqualZero)is called this produces the media item transition event.We provide a fix for this.
Can you confirm that only the very first seek for this controller triggers this. Once it is in sync and the incorrect event is emitted it does not emit any further events?
nift4 commentedon Jun 17, 2026
Glad to hear it was useful :)
No, I was able to generate further events by getting it out of sync again.
So old index from before the previous song was removed got stuck there somehow and it again only noticed on unrelated seek.
marcbaechinger commentedon Jun 17, 2026
Thanks! Ok, I see. I think the fix I have would address this as well. It's basically when a timeline change happens with reason
TIMELINE_CHANGED_REASON_PLAYLIST_CHANGEDwe don't updatePlayerInfo.newPositionandPlayerInfo.oldPosition. Then when a seek arrives it comparesoldPlayerInfo.newPositionwithnewPlayerinfo.newPositionand still has the oldmediaItemIndexofoldPlayerInfobecause of the inconsistent update when the timeline changed.I will validate the case you describe above as well. Many thanks!
Fix unexpected onMediaItemTransition event on seek within same item
Fix unexpected onMediaItemTransition event on seek within same item
nift4 commentedon Jul 20, 2026
Hi @marcbaechinger, thanks for the fix! I just tried it and it while it fixed the initial issue, it didn't fix the issue with song removal. Try the following steps to reproduce: