React-native-track-player: Recommended way to work with single-track queue [IOS/Android]

Created on 10 Apr 2019  Â·  6Comments  Â·  Source: react-native-kit/react-native-track-player

Its now 3 months that I am beta testing my streaming service, and it was just last week that I finally got it working in a way that confidently I can release to production.

For Android

  • Its impossible to use this https://github.com/react-native-kit/react-native-track-player-demo because the way most people use this library is by fetching musics from a server dynamically, and the way the demo shows is static. For that reason, because my app need to wait for the URL that is coming from a API many are the challenges that I am dealing with

  • Because I am managing my own queue, the only way I have to detect end of song or end of queue is from playback-track-changed because playback-queue-ended fires when the app is opened by the first time, it also fires when we call reset(), and it creates many challenges that are almost impossible to overcome.

MY SOLUTION FOR ANDROID

  • I control song changes from playback-track-changed BUT it only going to fire if I add the song twice. My strategy is, I add the song twice, and I only allow the 'business logic' that control the queue in playback-track-changed to be executed when the status of the player equals to TrackPlayer.STATE_PLAYING, right after this condition I call TrackPlayer.stop() (to prevent the same song to play) otherwise it will create an infinite loop, because if I keep the status of the player equal to TrackPlayer.STATE_PLAYING the business logic will be executed and go to the next song before the current song play.

For IOS

  • IOS does not fire playback-queue-ended when we open the app from the first time or when we call reset() which make ok to manage my own queue in playback-queue-ended

I will make and example with redux and fetching the songs' URLs from a server to show you the way I am doing in practice, maybe this week.

The version I am referring is 1.1.3

Question

Most helpful comment

The reason why we implemented an internal queue is to work around issues like those. Not only it prevents the notification from flashing, but it also allows a gapless playback since the next track starts buffering before it even starts. It also has some benefits internally, since we don't need to dispose resources to initialize everything again right after.

To solve the problem, there are two things you can do:

  • Query and add the next track as soon as the current one starts, ensuring you'll have a smooth playback
  • Query and add the next track a few seconds before the current one finishes playing (you'll have to setup a timer for that)

All 6 comments

In our app we still use playback-queue-ended event in android, just checking if player was actually playing something before.

let wasPlayingBeforeStop = false

then when handling playback-state events

      if (data.state === TrackPlayer.STATE_PLAYING) {
        wasPlayingBeforeStop = true
      } else if (data.state !== TrackPlayer.STATE_STOPPED && data.state !== TrackPlayer.STATE_BUFFERING) {
        wasPlayingBeforeStop = false
      }

and then in handling playback-queue-ended event

      if (wasPlayingBeforeStop) {
         // initiate loading and playing next track here (we dispatch redux action)
      }

That works fine except that sometimes notification is not recreated when queue is reset and new track is added. It looks like foreground service is not being started (as playback stops when you turn off the screen), but I think that it's not related to event handling here, as it happens even if switching to next track manually (via resetting queue and adding new track).

P.S. I'd suggest using a bit more precise name for issue, as current one is not helpful. Probably 'Recommended way to work with single-track queue'? Or something like that

@biomancer thanks for the suggestion. I will try the way you are doing and see how it goes.

By the way I've solved missing notification issue by actually moving away from strict one-track queue — instead of resetting player after each track I just add track to queue and call TrackPlayer.skipToNext() to ensure that last track is playing. Previous tracks are just left in player queue. That works much better, notification is stable now and does not disappear.

The reason why we implemented an internal queue is to work around issues like those. Not only it prevents the notification from flashing, but it also allows a gapless playback since the next track starts buffering before it even starts. It also has some benefits internally, since we don't need to dispose resources to initialize everything again right after.

To solve the problem, there are two things you can do:

  • Query and add the next track as soon as the current one starts, ensuring you'll have a smooth playback
  • Query and add the next track a few seconds before the current one finishes playing (you'll have to setup a timer for that)

@Guichaguri I'm getting small gaps between tracks? How do I make sure things are gapless?

I'm also getting half/whole second audio gaps when playing files stored on the device (cache or library) ... I am also loading all files in a track array on play, so I'm curious why the above answer about gapless playback isn't applying to my use case.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JakeMotta picture JakeMotta  Â·  3Comments

tarahiw picture tarahiw  Â·  3Comments

mckmarc picture mckmarc  Â·  4Comments

mnlbox picture mnlbox  Â·  4Comments

moduval picture moduval  Â·  4Comments