androidx / media

Jetpack Media3 support libraries for media use cases, including ExoPlayer, an extensible media player for Android
Apache License 2.0
1.38k stars 324 forks source link

Call PrepareSource method Multiple times in LazyMediaSource #1297

Open Dilavar opened 2 months ago

Dilavar commented 2 months ago
  1. I've implemented LazyMediaSource to fetch URLs from the server. Currently, when there are list of songs(Queue), it calls prepareSource for upcoming songs. Is there a way to modify it so that it calls prepareSource only when the previous song has ended?

  2. Within LazyMediaSource, we're uncertain about the type of song (HLS or Progressive) we'll receive from the API. How can I dynamically create either an HlsSource or a Progressive source after making the API call in LazyMediaSource?

marcbaechinger commented 2 months ago

Thanks for your question.

Is there a way to modify it so that it calls prepareSource only when the previous song has ended?

I don't think the call to prepareSource can be easily deferred I'm afraid.

By default the player is already preparing media sources lazily. The player is preparing a source when it gets as close to the end of the previous source that it wants to start buffering media of the next source to be able to transition without buffering. Given LoadControl is configured to buffer for instance 50 seconds, then the source may be prepared somewhere starting 50 seconds before it actually needs to be playing. In practice this is probably less than the entire buffer duration but that's how it basically works. Without doing so the player would need to buffer before starting which doesn't give a good user experience.

How can I dynamically create either an HlsSource or a Progressive source after making the API call in LazyMediaSource?

Preparing a source is async in the sense that the call immediately returns and the source needs to callback to the MediaSourceCaller that is passed into prepareSource(caller, ...). So in theory you could wait until this happens and then internally instantiate either the HLS or Progressive source to which you then delegate all further calls to the lazy source. However, you would do that soon enough to not fall into buffer state as explained above.

we're uncertain about the type of song (HLS or Progressive) we'll receive from the API

I understand what you trying to achieve. What would happen if the user skips to the next item in the playlist? In this case you also would have to call your API to get the URI (HLS or progressive). So I don't really understand why you can't just call your API a few seconds earlier like when prepareSource is called as explained above.

Dilavar commented 2 months ago

@marcbaechinger yes I understand. but the requirement is need to call api after song is finished. is there any way to implement that?

Thanks in advance

marcbaechinger commented 2 months ago

but the requirement is need to call api after song is finished.

Sure. In this case I think the requirements hinder you delivering rad stuff.

Some thoughts:

Option 1) How does the API know that the previous song is still playing? I still would insist on the use case that the user wants to skip to the next item. Here the API is also called before the previous song is finished. I understand from this that your app just doesn't support skipToNext, because if this can be implemented, the playlist feature would work as well. Please note, that at the moment your next source is prepared, all data of the previous song has been buffered. So there won't be any requests for the previous song. Once you call your API to resolve the next URI the previous song is fully buffered. So what does the API do to detect that the song is finished? As I said I don't think your API can know whether you are still playing the previous song unless you are reporting some in-band metadata back which would be a weird and harsh kind of an API.

However:

Option 2) What happens if you just wait with preparing your source until you can call your API? So you would call the MediaSourceCaller that is passed into prepareSource only after you can call the API and hence after the previous song has ended. I would guess the player then falls into STATE_BUFFERING until you call the MediaSourceCaller which then would result in calling your createPeriod method on the source.

Option 3) Only prepare the player with a single item at the time. When you detect STATE_ENDED call your API and replace the current item with the next item. You don't need a lazy source in this case. You'd have to maintain your own playlist of course as your UI can't nicely just display the ExoPlayer timeline.

Please note, that I can't really support you with your endeavour in a 1:1 manner. You need to develop some creativity yourself also (or question the API requirements) to implement this with that API requirements.