SRGSSR / pillarbox-apple

A next-generation reactive media playback ecosystem for Apple platforms.
https://testflight.apple.com/join/TS6ngLqf
MIT License
45 stars 6 forks source link

Support long playlists #178

Closed defagos closed 3 months ago

defagos commented 1 year ago

As a user I want the player to avoid loading data unnecessarily and to have good performance, even when many items are loaded.

Acceptance criteria

Tasks

defagos commented 1 year ago

Let us wait until we have a concrete need for an app to load a bunch of items. Closed for now.

defagos commented 5 months ago

We have a thread explosion issue when loading content. This occurs mostly with MP3 playlists which lead to more threads being created than M3U playlists. Threads created by AVPlayer add up to those we create through Combine, and to those created by 3rd party SDKs like comScore, which likely prevent background work from progressing correctly.

The issue mostly affects the simulator or running as iPad app on macOS, though of course these use cases should not be ignored. On real devices (iPhone 14 Pro, iPhone XS, iPad Pro and iPhone SE) the issue is far less visible. In comparison to raw AVQueuePlayer behavior when several items are loaded, though, our implementation has more overhead we still have to mitigate. Here is a sample project that can be run in the simulator or on device to test AVQueuePlayer behavior when loaded with several items.

We probably need to carefully use Combine and introduce a smart look-ahead centralized strategy to load content. Of course, since we can have multiple playlists loaded in multiple players at the same time, it is likely a good idea to introduce a common PlayerItemLoader to manage item preloading for all player instances. Instead of actually performing data retrieval and AVPlayerItem creation and loading into a player, it is likely possible to queue a pending request and to execute it only when required (that means to preload the next item or to load an item when large jumps are made into a playlist). We could also imagine having a default loader and the possibility to instantiate several and provide a loader to associate with a Player or PlayerItem.

Maybe using actors / Swift concurrency could help in keeping the number of threads small, leaving some breathing room for Combine and other threads we likely have less control on (e.g. comScore).

I'll reopen this issue.

defagos commented 4 months ago

A few learnings from early attempts:

With all of the above most behaviors seem to work (some UTs fail of course but probably because we are not loading items, which is what was tested). Performance with long playlists in the simulator and on device seems fine.

Of course, if the above strategy works, it won't ensure that several player instances each preload two items. But this is likely a corner case which does not justify introducing a complex shared preloading strategy.

defagos commented 4 months ago

Here are a few facts and learnings (WIP).

Expected behaviors

The following behaviors are expected to work:

Where preloading / queue truncation should be made

Queue truncation / preloading should be made:

Remark

We must not update the queue when an item fails, otherwise lengthy playlists made of only failing items (e.g. no network) will be consumed entirely for no reason. In such cases we should just let the player exhaust the existing queue.

Preloading items

AVQueuePlayer preloads items in a different way, depending on their type:

For this mechanisms to work playable MP3 / M3U URLs must be supplied to the player.

Remark

We experimented metadata retrieval triggered by a resource loader delegate (thus based on the player itself triggering the resource loading). This approach is doomed to fail, as in this case MP3s cannot be preloaded early, leading to a gap in the playback experience.

defagos commented 3 months ago

All AirPlay issues we had were fixed in the process 🍾

defagos commented 3 months ago

About the number of preloaded items:

defagos commented 3 months ago

Found an issue (#764) when checking analytics visually but this issue is not related to this task.