androidx / androidx/media

MediaController.addListener.onMediaItemTransition() may incorrect call

Open
#92 2 comments 0 reactions 1 assignee View on GitHub

@tonihei is already working on this.

Since Jun 23, 2022.

bug
Dominant language
Java
Stars
3k
Forks
955
Avg merge
12d 14h
Merged PRs (30d)
2

Description

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
  1. 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
    }
}
  1. Initialize controller in Activity.onStart(), set MediaItem 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()
    )
}
  1. when code B is call automaticaly or by hand, a problem occured.

  2. when controller.setMediaItems(mediaItems) called, the code A will be call immediately, the given mediaItem paremeter not converted by onAddMediaItems callback, them onAddMediaItems has been called, and then code A be called again, the mediaItem is converted by onAddMediaItems.

Expected result

When controller.setMediaItems(mediaItems)(code B) has been called , the method call order that I except is:

  1. MediaLibrarySession.Callback.onAddMediaItems()
  2. onMediaItemTransition(mediaItem, reason) has been set by controller.addListener(), and given mediaItem is converted by MediaLibrarySession.Callback.onAddMediaItems().
Actual result
  1. onMediaItemTransition(mediaItem, reason) has been set by controller.addListener(), and given mediaItem is not converted by MediaLibrarySession.Callback.onAddMediaItems().
  2. MediaLibrarySession.Callback.onAddMediaItems()
  3. onMediaItemTransition(mediaItem, reason) has been set by controller.addListener(), and given mediaItem is converted by MediaLibrarySession.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 dev.exoplayer@gmail.com after filing this issue.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.