Don't use AVPlayerLooper to loop HLS video streams in iOS
If you want to load an HLS video stream that loops in iOS, you might be tempted to use
AVPlayerLooper
.
From the documentation, you would get the idea that this is the ideal class
for this:
You can manually implement looping playback in your app using AVQueuePlayer, but AVPlayerLooper provides a much simpler interface to loop a single AVPlayerItem. You create a player looper by passing it a reference to your AVQueuePlayer and a template AVPlayerItem and the looper automatically manages the looping playback of this content
However, using AVPlayerLooper
, at least for HLS streams, is problematic and
I’d recommend you avoid it and instead rely in a traditional solution.
I won’t be providing examples for you to play with and see with your own eyes given that this bug was found in the app I develop for work and can’t share the private details. Hopefully you can take my word for it 🙂
Looping videos with AVPlayerLooper
#
NOTE: SwiftUI still doesn’t offer a very powerful video player. This solution will be based on UIKit
So, you look at the docs and a few Stack Overflow solutions and write an initial implementation like this one:
Looks pretty good, right?
The problem #
When following the above approach, I noticed a couple of problems:
-
Data usage off the charts: Using HLS video streams, with multiple resolutions available, would cause the app to continuously and excesively download tons of data. You can get a lot of negative App Store reviews because of this 😅.
-
Battery drain, higher CPU usage: This also causes higher CPU usage which incidentally makes your app feel less responsive. Using the proposed solution made the app I work on feel snappier.
-
No configuration of AVPlayerItem:
AVPlayerItem
has a bunch of different properties that can be used to configure playback of a video, such as:var preferredPeakBitRate: Double
var preferredForwardBufferDuration: TimeInterval
var preferredMaximumResolution: CGSize
All of these properties, and more, can be used to limit the amount of data
that the video playback is going to require.
However, setting these properties seems to have no effect when using AVPlayerLooper
.
My guess is that this a bug from Apple, an oversight. Reading the documentation
of AVPlayerLooper
you can learn that it works by making multiple copies
of the AVPlayerItem
and adding them to the AVQueuePlayer
for them to play
one after the other. My guess is that these configuration properties are not
being copied or respected.
I would also assume that once an AVPlayerItem
gets created, it starts loading
the asset immediately, even though it’s not actually visible and playing. Given
that multiple items get created, you are essentially downloading the same stream
multiple times.
The solution #
Don’t use AVPlayerLooper
! As much as I would love to use the simpler API, this
bug is a blocker. However, the alternative is not too bad:
- Video playback feels just as good, if not better
- Configuring the
AVPlayerItem
actually works: limiting the bitrate, the resolution, the forward buffer, etc. - The code is still lightweight and simple. In fact, its a bit more customizable. Maybe you want the next time the video plays to start at a different position. You are in control.
Conclusion #
Hopefully Apple fixes this bug. In the meantime, the simpler approach of seeking the single video stream to the beginning works just as well.
This is my first blog post, I hope you found it useful 😁