media
media copied to clipboard
MediaController.addListener.onMediaItemTransition() may incorrect call
Media3 Version
1.0.0-beta01
Devices that reproduce the issue
Samsung galaxy note 10 5G running Android 11
Devices that do not reproduce the issue
No response
Reproducible in the demo app?
Not tested
Reproduction steps
- In service LibrarySessionCallback.onAddMediaItems() method like this, just transfer uri to
MediaItem
:
override fun onAddMediaItems(
mediaSession: MediaSession,
controller: MediaSession.ControllerInfo,
mediaItems: MutableList<MediaItem>
): ListenableFuture<MutableList<MediaItem>> {
return MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()).submit<MutableList<MediaItem>> {
val infoList = contentResolver.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
arrayOf(
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.ARTIST,
),
"${MediaStore.Audio.Media.DATA} IN (${mediaItems.joinToString { "'${it.requestMetadata.mediaUri.toString()}'" }})",
null,
null
)?.use { cursor ->
val columnIndexOfId = cursor.getColumnIndex(MediaStore.Audio.Media._ID)
val columnIndexOfData = cursor.getColumnIndex(MediaStore.Audio.Media.DATA)
val columnIndexOfTitle = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)
val columnIndexOfDuration = cursor.getColumnIndex(MediaStore.Audio.Media.DURATION)
val columnIndexOfArtist = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)
val list = mutableListOf<Array<String>>()
while (cursor.moveToNext()) {
list.add(
arrayOf(
cursor.getLong(columnIndexOfId).toString(),
cursor.getString(columnIndexOfData).orEmpty(),
cursor.getString(columnIndexOfTitle).orEmpty(),
cursor.getLong(columnIndexOfDuration).formatToVideoDuration(),
cursor.getString(columnIndexOfArtist).orEmpty(),
)
)
}
list
} ?: return@submit mutableListOf()
val resultItems = mediaItems.map { mediaItem ->
val infoArray = infoList.find { it[1] == mediaItem.requestMetadata.mediaUri.toString() } ?: return@map null
val metadataBuilder = MediaMetadata.Builder().apply {
setTitle(infoArray[2])
setDurationStr(infoArray[3])
setArtist(infoArray[4])
setFolderType(MediaMetadata.FOLDER_TYPE_NONE)
setIsPlayable(true)
}
mediaItem.buildUpon()
.setMediaMetadata(metadataBuilder.build())
.setMediaId(infoArray[0])
.setUri(mediaItem.requestMetadata.mediaUri)
.build()
}.filterNotNull().toMutableList()
resultItems
}
}
- Initialize controller in
Activity.onStart()
, setMediaItem
list in controller future`s listener:
private fun initController() {
vm.controllerFuture =
MediaController.Builder(
this,
SessionToken(this, ComponentName(this, AudioPlayerService::class.java))
).buildAsync()
vm.controllerFuture.addListener(
{
val controller = vm.controller ?: return@addListener
controller.addListener(object : Player.Listener {
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
// code A
}
})
val mediaItems = uriList.map { uriString ->
MediaItem.Builder()
.setRequestMetadata(
MediaItem.RequestMetadata.Builder()
.setMediaUri(Uri.parse(uriString))
.build()
)
.build()
}
// code B
controller.setMediaItems(mediaItems)
},
MoreExecutors.directExecutor()
)
}
-
when code B is call automaticaly or by hand, a problem occured.
-
when
controller.setMediaItems(mediaItems)
called, thecode A
will be call immediately, the given mediaItem paremeter not converted byonAddMediaItems
callback, themonAddMediaItems
has been called, and thencode A
be called again, the mediaItem is converted byonAddMediaItems
.
Expected result
When controller.setMediaItems(mediaItems)
(code B) has been called , the method call order that I except is:
-
MediaLibrarySession.Callback.onAddMediaItems()
-
onMediaItemTransition(mediaItem, reason)
has been set bycontroller.addListener()
, and given mediaItem is converted byMediaLibrarySession.Callback.onAddMediaItems()
.
Actual result
-
onMediaItemTransition(mediaItem, reason)
has been set bycontroller.addListener()
, and given mediaItem is not converted byMediaLibrarySession.Callback.onAddMediaItems()
. -
MediaLibrarySession.Callback.onAddMediaItems()
-
onMediaItemTransition(mediaItem, reason)
has been set bycontroller.addListener()
, and given mediaItem is converted byMediaLibrarySession.Callback.onAddMediaItems()
now.
PS: when I set a Listener to ExoPlayer instance in service, it's onMediaItemTransition()
method has called as I expected. So different behaviors exist between MediaController
and ExoPlayer
This is the log I print manually, after controller.setMediaItems(mediaItems)
called, may be useful.
2022-06-21 17:10:17.293 12071-12071/myPackageName D/Log-to-File: Thread: main
├ myPackageName.ui.player.audio.AudioPlayerViewModel$setController$1.onMediaItemTransition(AudioPlayerViewModel.kt:154)
├ androidx.media3.session.MediaControllerImplBase.lambda$updatePlayerInfo$46(MediaControllerImplBase.java:2114)
├ androidx.media3.session.MediaControllerImplBase$$ExternalSyntheticLambda41.invoke(Unknown Source:6)
├ androidx.media3.common.util.ListenerSet$ListenerHolder.invoke(ListenerSet.java:283)
├ androidx.media3.common.util.ListenerSet.lambda$queueEvent$0(ListenerSet.java:192)
├ androidx.media3.common.util.ListenerSet$$ExternalSyntheticLambda1.run(Unknown Source:6)
├ androidx.media3.common.util.ListenerSet.flushEvents(ListenerSet.java:213)
├ androidx.media3.session.MediaControllerImplBase.updatePlayerInfo(MediaControllerImplBase.java:2154)
├ androidx.media3.session.MediaControllerImplBase.setMediaItemsInternal(MediaControllerImplBase.java:1924)
├ androidx.media3.session.MediaControllerImplBase.setMediaItems(MediaControllerImplBase.java:931)
├ androidx.media3.session.MediaController.setMediaItems(MediaController.java:984)
├ myPackageName.ui.player.audio.AudioPlayerActivity.setMediaItemsAndPlay(AudioPlayerActivity.kt:266)
├ myPackageName.ui.player.audio.AudioPlayerActivity.access$setMediaItemsAndPlay(AudioPlayerActivity.kt:101)
├ myPackageName.ui.player.audio.AudioPlayerActivity$content$1$1$1.invoke(AudioPlayerActivity.kt:157)
├ myPackageName.ui.player.audio.AudioPlayerActivity$content$1$1$1.invoke(AudioPlayerActivity.kt:156)
├ myPackageName.ui.player.audio.AudioPlayerActivityKt$AudioPlayerScreen$1$1$4.invoke(AudioPlayerActivity.kt:303)
├ myPackageName.ui.player.audio.AudioPlayerActivityKt$AudioPlayerScreen$1$1$4.invoke(AudioPlayerActivity.kt:295)
├ myPackageName.utils.popup.MenuPopupWithTextAndIconKt$MenuPopupWithTextAndIcon$1$1$1$1.invoke(MenuPopupWithTextAndIcon.kt:96)
├ myPackageName.utils.popup.MenuPopupWithTextAndIconKt$MenuPopupWithTextAndIcon$1$1$1$1.invoke(MenuPopupWithTextAndIcon.kt:96)
└ androidx.compose.foundation.ClickableKt$clickable$4$gesture$1$2.invoke-k-4lQ0M(Clickable.kt:153)
**// Here is called by controller's onMediaItemTransition()**
onMediaItemTransition: title=null mediaId=null /storage/emulated/0/Music/好多首歌/大島ミチル - Patema Inverse-copy6-copy-copy.mp3 reason=3
2022-06-21 17:10:17.385 12071-12071/myPackageName D/Log-to-File: Thread: main
├ myPackageName.ui.player.audio.AudioPlayerService$librarySessionCallback$1.onPlayerCommandRequest(AudioPlayerService.kt:213)
├ androidx.media3.session.MediaSessionImpl.onPlayerCommandRequestOnHandler(MediaSessionImpl.java:447)
├ androidx.media3.session.MediaSessionStub.lambda$getSessionTaskWithPlayerCommandRunnable$4$androidx-media3-session-MediaSessionStub(MediaSessionStub.java:261)
├ androidx.media3.session.MediaSessionStub$$ExternalSyntheticLambda71.run(Unknown Source:14)
├ androidx.media3.session.MediaSessionStub.lambda$flushCommandQueue$67(MediaSessionStub.java:1460)
├ androidx.media3.session.MediaSessionStub$$ExternalSyntheticLambda78.run(Unknown Source:2)
├ androidx.media3.common.util.Util.postOrRun(Util.java:548)
├ androidx.media3.session.MediaSessionStub.flushCommandQueue(MediaSessionStub.java:1454)
├ androidx.media3.session.MediaControllerImplBase$FlushCommandQueueHandler.handleMessage(MediaControllerImplBase.java:3059)
├ android.os.Handler.dispatchMessage(Handler.java:106)
├ android.os.Looper.loop(Looper.java:246)
├ android.app.ActivityThread.main(ActivityThread.java:8653)
├ java.lang.reflect.Method.invoke(Native Method)
├ com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
└ com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
onPlayerCommandRequest: playerCommand=20
2022-06-21 17:10:17.546 12071-13653/myPackageName D/Log-to-File: Thread: pool-13-thread-1
├ myPackageName.ui.player.audio.AudioPlayerService$librarySessionCallback$1.onAddMediaItems$lambda-6(AudioPlayerService.kt:192)
├ myPackageName.ui.player.audio.AudioPlayerService$librarySessionCallback$1.$r8$lambda$dZTOHF_a25TC4PIQE_k1kOcWRAE(Unknown Source:0)
├ myPackageName.ui.player.audio.AudioPlayerService$librarySessionCallback$1$$ExternalSyntheticLambda0.call(Unknown Source:4)
├ com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:131)
├ com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:74)
├ com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:82)
├ java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
├ java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
└ java.lang.Thread.run(Thread.java:923)
onAddMediaItems: listSize=1536
2022-06-21 17:10:17.724 12071-12071/myPackageName D/Log-to-File: Thread: main
├ myPackageName.ui.player.audio.AudioPlayerService$initSessionAndPlayer$1.onMediaItemTransition(AudioPlayerService.kt:285)
├ androidx.media3.exoplayer.ExoPlayerImpl.lambda$updatePlaybackInfo$14(ExoPlayerImpl.java:1890)
├ androidx.media3.exoplayer.ExoPlayerImpl$$ExternalSyntheticLambda25.invoke(Unknown Source:6)
├ androidx.media3.common.util.ListenerSet$ListenerHolder.invoke(ListenerSet.java:283)
├ androidx.media3.common.util.ListenerSet.lambda$queueEvent$0(ListenerSet.java:192)
├ androidx.media3.common.util.ListenerSet$$ExternalSyntheticLambda1.run(Unknown Source:6)
├ androidx.media3.common.util.ListenerSet.flushEvents(ListenerSet.java:213)
├ androidx.media3.exoplayer.ExoPlayerImpl.updatePlaybackInfo(ExoPlayerImpl.java:1963)
├ androidx.media3.exoplayer.ExoPlayerImpl.setMediaSourcesInternal(ExoPlayerImpl.java:2175)
├ androidx.media3.exoplayer.ExoPlayerImpl.setMediaSources(ExoPlayerImpl.java:605)
├ androidx.media3.exoplayer.ExoPlayerImpl.setMediaItems(ExoPlayerImpl.java:563)
├ androidx.media3.common.ForwardingPlayer.setMediaItems(ForwardingPlayer.java:76)
├ androidx.media3.session.PlayerWrapper.setMediaItems(PlayerWrapper.java:389)
├ androidx.media3.session.MediaSessionStub.lambda$setMediaItemsWithStartIndex$36(MediaSessionStub.java:1011)
├ androidx.media3.session.MediaSessionStub$$ExternalSyntheticLambda22.run(Unknown Source:4)
├ androidx.media3.session.MediaSessionStub.lambda$handleMediaItemsWhenReady$1(MediaSessionStub.java:164)
├ androidx.media3.session.MediaSessionStub$$ExternalSyntheticLambda70.run(Unknown Source:6)
├ android.os.Handler.handleCallback(Handler.java:938)
├ android.os.Handler.dispatchMessage(Handler.java:99)
└ android.os.Looper.loop(Looper.java:246)
**// Here is called by player's onMediaItemTransition()**
onMediaItemTransition: title=Patema Inverse mediaId=148808 /storage/emulated/0/Music/好多首歌/大島ミチル - Patema Inverse-copy6-copy-copy.mp3 reason=3
2022-06-21 17:10:18.132 12071-12071/myPackageName D/Log-to-File: Thread: main
├ myPackageName.ui.player.audio.AudioPlayerViewModel$setController$1.onMediaItemTransition(AudioPlayerViewModel.kt:154)
├ androidx.media3.session.MediaControllerImplBase.lambda$onPlayerInfoChanged$59$androidx-media3-session-MediaControllerImplBase(MediaControllerImplBase.java:2388)
├ androidx.media3.session.MediaControllerImplBase$$ExternalSyntheticLambda34.invoke(Unknown Source:6)
├ androidx.media3.common.util.ListenerSet$ListenerHolder.invoke(ListenerSet.java:283)
├ androidx.media3.common.util.ListenerSet.lambda$queueEvent$0(ListenerSet.java:192)
├ androidx.media3.common.util.ListenerSet$$ExternalSyntheticLambda1.run(Unknown Source:6)
├ androidx.media3.common.util.ListenerSet.flushEvents(ListenerSet.java:213)
├ androidx.media3.session.MediaControllerImplBase.onPlayerInfoChanged(MediaControllerImplBase.java:2520)
├ androidx.media3.session.MediaControllerStub.lambda$onPlayerInfoChanged$9(MediaControllerStub.java:180)
├ androidx.media3.session.MediaControllerStub$$ExternalSyntheticLambda12.run(Unknown Source:4)
├ androidx.media3.session.MediaControllerStub.lambda$dispatchControllerTaskOnHandler$13(MediaControllerStub.java:280)
├ androidx.media3.session.MediaControllerStub$$ExternalSyntheticLambda5.run(Unknown Source:4)
├ androidx.media3.common.util.Util.postOrRun(Util.java:548)
├ androidx.media3.session.MediaControllerStub.dispatchControllerTaskOnHandler(MediaControllerStub.java:272)
├ androidx.media3.session.MediaControllerStub.onPlayerInfoChanged(MediaControllerStub.java:178)
├ androidx.media3.session.MediaSessionStub$Controller2Cb.onPlayerInfoChanged(MediaSessionStub.java:1723)
├ androidx.media3.session.MediaSessionImpl.dispatchOnPlayerInfoChanged(MediaSessionImpl.java:400)
├ androidx.media3.session.MediaSessionImpl.access$600(MediaSessionImpl.java:79)
├ androidx.media3.session.MediaSessionImpl$PlayerInfoChangedHandler.handleMessage(MediaSessionImpl.java:1193)
└ android.os.Handler.dispatchMessage(Handler.java:106)
**// Here is called by controller's onMediaItemTransition()**
onMediaItemTransition: title=Patema Inverse 148808 /storage/emulated/0/Music/好多首歌/大島ミチル - Patema Inverse-copy6-copy-copy.mp3 reason=3
Media
Not applicable
Bug Report
- [ ] You will email the zip file produced by
adb bugreport
to [email protected] after filing this issue.
Thanks for reporting!
The onMediaItemTransition
callback should definitely only be called once for a new item. However, I think the eventual fix will not look like your "expected result", but more like:
- onMediaItemTransition(mediaItem, reason) with the given
mediaItem
that is not converted by MediaLibrarySession.Callback.onAddMediaItems(). - MediaLibrarySession.Callback.onAddMediaItems()
- optional: another
onTimelineChanged(...)
oronMediaMetadataChanged(...)
callback notifying the change in the mediaItem information
The reason is that the media item transition (from nothing to the initial item) happens immediately at the point where you call mediaController.setMediaItems
.
I thought "the media item transition" should happens after MediaLibrarySession.Callback.onAddMediaItems(). Now I understand. thanks for your explanation.