Closed defagos closed 3 months ago
Let us wait until we have a concrete need for an app to load a bunch of items. Closed for now.
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.
A few learnings from early attempts:
Publishers.Publish(onOutputFrom:)
in our PlayerItem
publisher pipeline, which we can trigger when needed (e.g. when an item needs to be loaded lazily, or after a network failure). The number of threads stay small but we still have to figure out where triggering is made optimally so that everything works well (and how many items we should preload in advance, but 2 seems a pretty good number).AVPlayerItem
creation is not free so using prefix
to only produce 2 items seems a good approach as well.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.
Here are a few facts and learnings (WIP).
The following behaviors are expected to work:
Queue truncation / preloading should be made:
currentItem
changes directly, since those are triggered when updating the queue.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.
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.
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.
All AirPlay issues we had were fixed in the process 🍾
About the number of preloaded items:
Found an issue (#764) when checking analytics visually but this issue is not related to this task.
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
PlayerItem
, depending on the current position and preloading length (only a few neighboring items after the current one).prefix
used to truncate playlists generated from media lists in the demo.currentIndex
in case of an error? Reuse it when replaying?PlayerItemTracker
s are correctly deinit).