A Custom Video Player for iOS with AVFoundation

08.16.15 Posted in Uncategorized by

For video streaming on iOS, Apple’s Media Player framework provides a handy works-out-of-the-box view controller called MPMoviePlayerController complete with playback controls. It only takes a few lines of code to build a working implementation. However, it won’t be long before a savvy user (or client or UX designer) requests you to remove a button or perhaps modify the video player UI so that the look and feel matches the rest of the app. In fact, there is a good chance you are reading this now because you might already know that it isn’t possible to alter the Media Player’s UI (without some evil hack). You can, however, create your own customised video player UI with Apple’s AVFoundation framework. Here’s how.

A developer’s hesitation in not wanting to crack open AVFoundation is understandable. A quick look at the AVFoundation Programming Guide shows – in stark contrast to other iOS programming guides – a distinct lack of relevant sample code, no Swift sample code and only a brief mention of some of the most useful features. Using the framework requires a good understanding of advanced techniques such as blocks, key-value observing (KVO) and layers. Despite these shortcomings, any serious “video-playback centric” app will likely need to make use of AVFoundation’s offerings at some point.

In this article we’re going to create a simple video player with the capability to:

    1. Play Video
    2. Control Playback
    3. Display Footage Time
    4. Seek
    5. Indicate to the User When the Video is Buffering

Here is what the end result looks like. It’s not going to win any awards for UX design, but it clearly demonstrates the most common features needed in an iOS app supporting video playback.




The first thing we need to do is create an instance of AVPlayer and connect it to a view. For this example, we’ll also set the playback to start as soon as the view appears. We’ll also restrict the view to landscape orientation and create and layout the views manually so we don’t need to bother with Interface Builder.

AVPlayer is a AVFoundations media player. It is a controller but not a component that is ready to present to the user as is. An AVPlayerLayer must be linked to an AVPlayer instance and added to a view in the view hierarchy. Let’s have a look at how we get a simple AVPlayer instance working:

Not quite 3 lines of code, but still, it isn’t War and Peace either. Add the above code to a new view controller, present it modally or just full screen and sit back and enjoy the film.

It might interest you to know that the footage in the example is an HTTP live stream (HLS) of Blender Foundation’s very cool short film “Tears of Steel“.

What you may notice is that while our solution successfully plays the footage, it doesn’t offer any way to pause/restart playback. Let’s have a look at that now.



Next, let’s add a simple way control to control video playback. In a real world solution, you probably want to put some beautifully and originally styled buttons in considered places around your view. In the interest of keeping this solution simple, let’s just add a large invisible button overlay that covers the video player. Now each time the user taps on the video, we invoke pause() or play() on our AV Player to pause or restart playback.

Run the app and let the film start playing automatically just as it did before. Now tap once on the screen to pause playback. Tap again to restart.



Displaying the number of minutes and seconds left until the video ends is another important feature most users expect from standard video players. Without it, the user is left in the dark as to how much time they should allow themselves to finish watching their favourite movie or cartoon.

To achieve this, we make use of the AVPlayer’s time observer feature. We configure the observer so that it fires every 1 second. The interval is configurable but be careful, making the checking period too frequent may force the CPU to work too hard and drain the device’s battery quickly.

Add the following code:

Run the app in Xcode and look at the log output. You’ll notice that the video’s elapsed time (in seconds) is printed to the log every second. Now let’s calculate the time remaining and add a UILabel that we’ll update with that information whenever the observer fires:

Run the app and look at the time display in the bottom left of the screen. Notice it counts down as the video plays. When the video is paused, the time remaining counter stops. When the video is restarted, the counter restarts. The time label is synchronised thanks to our observer.



So now we have our AVPlayer playing video, we’ve given the user the ability to stop and start the video and we are also providing them constant feedback on how much time is remaining before the end of the video. So what if the user wants to go back or forward to their favourite scene? At the moment they can’t. Let’s fix this by adding a horizontal slider that will allow the user to seek to different points in the video. When the user moves the slider, the time remaining label is updated. When the user let’s go of the slider, we use “seekToTime” to jump to different points in the video stream.

Now, run the app and use the slider to seek to different parts of the video. If the video was playing before seeking, it should begin playback once again after the video has finished seeking. Similarly, if the video was paused before seeking, it should remain paused after seeking.



Since it takes a while for the video stream to buffer before playing, our users might be left wondering if something is amiss and if the video will ever start. Let’s fix this by adding a UIActivityIndicator to the centre of the screen that animates when the video is buffering. We do this by adding a UIActivityIndicator view and linking it via a key-value observer (KVO) to the playbackLikelyToKeepUp boolean property on the AVPlayer’s currently playing item:

Run the app and the first thing you’ll notice is the UIActivityIndicator spinning. It will stop spinning once the video is ready for playback and will be displayed again whenever the buffering video falls behind.



We now have a fully functional – albeit quite basic – video player capable of playing video streams while allowing the user to pause, start and seek. You can now see how using AVFoundation’s AVPlayer controller gives the developer complete control over the video player’s UI while also providing superior playback monitoring through the use of observers.

Comments are closed.