question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Backing the ExoPlayer playlist with another datatype

See original GitHub issue

My app does not leverage the typical ExoPlayer media datatype, MediaItem, as it tends to be too restrictive for my use. Instead, I have my own datatype (Song) that can be transformed into a MediaItem for use in ExoPlayer. By extension, I have my own data structure for the queue (or “playlist”) in my app that is also composed of Song instances.

I’ve been trying to leverage the ExoPlayer playlist system for some time so I could implement Gapless Playback in my app, however, the major issue stems from the way I must convert between Song and MediaItem:

  • I can’t just map the Song queue to a list of a MediaItem instances and then call setMediaItems, as such could occur during playback and cause a variety of issues.
  • The more “correct” queue methods (ex. addMediaItem or ShuffleOrder) also do not suffice, as trying to keep two mirrors of the queue in different datatypes, making sure that edits in one also effect the other, is more or less asking for bugs to appear.

Thus, is there a way to extend ExoPlayer so that the playlist is backed with my own datatype, Song? That way, I could keep one copy of the queue and simply use the correct methods to notify ExoPlayer when it changes. ExoPlayer should still try to attempt gapless playback, even with this custom playlist.

I think Timeline is the API I should be using for this, but I have no clue if that’s the case or how I could use it to do what I want. Any help on this would be greatly appreciated. Thank you.

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:6 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
christostscommented, Aug 12, 2022

For the fun of it(!), I’m almost certain you don’t have to call indexOf() in the for-loop, which would cause the quadratic complexity. You could store 2 maps that map item-to-position (or position-to-item) for before and after the shuffle (needs two iterations), and then find for each item find its previous and new position with look-ups on the maps. This obviously comes with the cost of extra space for the maps. Overall, the playlist size should be so small than it doesn’t really make a difference to the user.

0reactions
OxygenCobaltcommented, Aug 15, 2022

Okay, turns out that the idea I proposed doesn’t really work @christosts.

I made this implementation that sort of does what I want:

class QueueShuffleOrder(private val to: IntArray, private val from: IntArray) : ShuffleOrder {
    override fun getLength() = to.size

    override fun getFirstIndex() = to.getOrElse(0) { C.INDEX_UNSET }

    override fun getLastIndex() = to.getOrElse(to.lastIndex) { C.INDEX_UNSET }

    override fun getNextIndex(index: Int) =  to.getOrElse(from[index] + 1) { C.INDEX_UNSET }

    override fun getPreviousIndex(index: Int) = to.getOrElse(from[index] - 1) { C.INDEX_UNSET }

    // These can't occur since the player isn't connected to anything that could change the
    // media items.

    override fun cloneAndClear(): ShuffleOrder {
        throw UnsupportedOperationException()
    }

    override fun cloneAndInsert(insertionIndex: Int, insertionCount: Int): ShuffleOrder {
        throw UnsupportedOperationException()
    }

    override fun cloneAndRemove(indexFrom: Int, indexToExclusive: Int): ShuffleOrder {
        throw UnsupportedOperationException()
    }

    companion object {
        fun from(queue: List<Song>, mapping: Map<Long, Int>): QueueShuffleOrder {
            val to = IntArray(queue.size)
            val from = IntArray(queue.size)
            for (entry in queue.withIndex()) {
                val playerIndex = requireNotNull(mapping[entry.value.id])
                to[entry.index] = playerIndex
                from[playerIndex] = entry.index
            }
            return QueueShuffleOrder(to, from)
        }
    }
}

But there is one crucial flaw: There can be multiple instances of the same song in a queue. For example, MediaItem 3 in the player could correspond to Song 4 and 5, and the shuffle order has no way to find which index the player is truly at based on the raw player index. The only way I could fix this would be:

  1. For ShuffleOrder to use the index based on the shuffled indices, which is absurd to every use case except for mine.
  2. To use the “true” index value I have in another object, but ShuffleOrder should be stateless, so I can’t do that.

Care to elaborate on how I could leverage moveMediaItems?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Playlists - ExoPlayer
The playlist can be queried using Player.getMediaItemCount and Player.getMediaItemAt . The currently playing media item can be queried by calling Player.
Read more >
SimpleExoPlayer | Android Developers
Adds a list of media items at the given index of the playlist. ... For example when the application is in the background,...
Read more >
Dynamic playlists with ExoPlayer - Medium
Ever wanted to support media playlists in your Android app, where users can add and remove playlist items arbitrarily during playback?
Read more >
DefaultLoadErrorHandlingPolicy - androidx.* javadoc
androidx.media3.exoplayer.hls.playlist ... Returns whether a loader should fall back to using another resource on encountering an error, ...
Read more >
Brightcove Native SDK for Android Release Notes
ExoPlayer Changes: Fixed an issue where with mixed playlists with DRM and Clear content, playback failed if a Clear video is loaded after...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found