Video Streaming Tutorial for iOS: Getting Started
Learn how to build a video streaming app using AVKit and AVFoundation frameworks. By Saeed Taheri.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Video Streaming Tutorial for iOS: Getting Started
30 mins
- Getting Started
- Understanding AVKit
- Adding Local Playback
- Adding Remote Playback
- Adding a Looping Video Preview
- Understanding AVFoundation
- Writing a Custom Video View With AVPlayerLayer
- Writing the Looping Video View
- Implementing the Actual Looping
- Playing with Player Controls
- Playing Video Efficiently
- Trying Not to Steal the Show
- Bonus: Adding Picture-in-Picture
- Where to Go From Here?
Adding a Looping Video Preview
You may have noticed that black box at the top of the list. Your next task is turning that black box into a custom video player. Its purpose is to play a revolving set of clips to get users excited about all these videos.
Then, you need to add a few custom gestures, like tapping to turn on sound and double-tapping to change it to 2x speed. When you want to have very specific control over how things work, it’s better to write your own video view.
It’s your job to get things going.
Understanding AVFoundation
While AVFoundation can feel a bit intimidating, most of the objects you deal with are still pretty high-level.
The main classes you’ll need to get familiar with are:
-
AVPlayerLayer: This special
CALayer
subclass can display the playback of a givenAVPlayer
object. - AVAsset: These are static representations of a media asset. An asset object contains information such as duration and creation date.
-
AVPlayerItem: The dynamic counterpart to an
AVAsset
. This object represents the current state of a playable video. This is what you need to provide toAVPlayer
to get things going.
AVFoundation is a huge framework that goes well beyond these few classes. Fortunately, this is all you’ll need to create your looping video player.
You’ll come back to each of these in turn, so don’t worry about memorizing them.
Writing a Custom Video View With AVPlayerLayer
The first class you need to cozy up to is AVPlayerLayer
. This CALayer
subclass is like any other layer: It displays whatever is in its contents
property.
This layer just happens to fill its contents with frames from a video you’ve given it via its player
property.
The problem is that you can’t use this layer directly in SwiftUI. After all, SwiftUI doesn’t have the concept of CALayer
s. For that, you need to go back to the old and gold UIKit.
Go to LoopingPlayerView.swift, where you’ll find an empty view you’ll use to show videos. It needs an array of video URLs to play.
The first thing you need to do is add the proper import
statement, this time for AVFoundation
:
import AVFoundation
Good start! Now you can get AVPlayerLayer
into the mix.
A UIView
is simply a wrapper around a CALayer
. It provides touch handling and accessibility features but isn’t a subclass. Instead, it owns and manages an underlying layer property. One nifty trick is that you can actually specify what type of layer you would like your view subclass to own.
Add the following property override to tell LoopingPlayerView.swift that it should use an AVPlayerLayer
instead of a plain CALayer
:
override class var layerClass: AnyClass {
return AVPlayerLayer.self
}
Since you’re wrapping the player layer in a view, you need to expose a player
property.
To do so, add the following computed property so you don’t need to cast your layer subclass all the time:
var playerLayer: AVPlayerLayer {
return layer as! AVPlayerLayer
}
To be able to use this view in SwiftUI, you need to create a wrapper using UIViewRepresentable
.
Add these lines of code in the same file, outside the LoopingPlayerUIView
definition:
struct LoopingPlayerView: UIViewRepresentable {
let videoURLs: [URL]
}
UIViewRepresentable
is a protocol. You need to implement its methods to complete the bridge between UIKit and SwiftUI.
Add these inside LoopingPlayerView
:
// 1
func makeUIView(context: Context) -> LoopingPlayerUIView {
// 2
let view = LoopingPlayerUIView(urls: videoURLs)
return view
}
// 3
func updateUIView(_ uiView: LoopingPlayerUIView, context: Context) { }
- SwiftUI calls
makeUIView(context:)
when it needs a new instance of yourUIView
. - You create a new instance of
LoopingPlayerUIView
using the initializer and returning the new instance. - SwiftUI calls this method when it needs to update the underlying
UIView
. For now, leave it empty.
Now, get back to VideoFeedView.swift and add the following property to get URLs for video clips:
private let videoClips = VideoClip.urls
Inside makeEmbeddedVideoPlayer()
, replace Rectangle()
with the following code, but keep the view modifiers:
LoopingPlayerView(videoURLs: videoClips)
Build and run to see… nothing new! You just passed the video clip URLs to the view but you didn’t do anything with them yet.
Writing the Looping Video View
Next, go over to LoopingPlayerView.swift and get ready to add a player. After all, you now know you need a player to make video playing work.
To get started, add the following player property to LoopingPlayerUIView
:
private var player: AVQueuePlayer?
The discerning eye will see that this is no plain AVPlayer
instance. That’s right, this is a special subclass called AVQueuePlayer
. As you can probably guess by the name, this class allows you to provide a queue of items to play.
Replace init(urls:)
with the following to initialize the player:
init(urls: [URL]) {
allURLs = urls
player = AVQueuePlayer()
super.init(frame: .zero)
playerLayer.player = player
}
Here, you first create the player
object and then connect it to the underlying AVPlayerLayer
.
Now, it’s time to add your list of video clips to the player so it can start playing them.
Add the following method to do so:
private func addAllVideosToPlayer() {
for url in allURLs {
// 1
let asset = AVURLAsset(url: url)
// 2
let item = AVPlayerItem(asset: asset)
// 3
player?.insert(item, after: player?.items().last)
}
}
Here, you’re looping through all the clips. For each one, you:
- Create an
AVURLAsset
from theURL
of each video clip object. - Then, you create an
AVPlayerItem
with theasset
that the player can use to control playback. - Finally, you use
insert(_:after:)
to add each item to the queue.
Now, go back to init(urls:)
and call the method after super.init(frame:)
and before setting player
to playerLayer
:
addAllVideosToPlayer()
Now that you have your player set, it’s time to do some configuration.
To do this, add the following two lines in init(urls:)
after addAllVideosToPlayer()
:
player?.volume = 0.0
player?.play()
This sets your looping clip show to auto-play and audio off by default.
Build and run to see your fully working clip show!
Unfortunately, when the last clip has finished playing, the video player fades to black.