I'm setting up an AVPlayer
in a UIView
in this way:
fileprivate func setUpAVPlayer(){
self.cardModel?.urlStrings?.forEach({ (urlString) in
guard let url = URL(string: urlString) else { return }
let asset = AVAsset(url: url)
let playerItem = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: nil)
playerItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: [.old, .new], context: nil)
self.playerItemArray.append(playerItem)
})
player = AVPlayer(playerItem: playerItemArray[0])
}
In layoutSubviews
I lay out the playerLayer that displays the player:
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = .resizeAspectFill
self.playerView.layer.insertSublayer(playerLayer, at: 0)
playerLayer.frame = playerView.bounds
Upon a certain event I change the current item of the player:
fileprivate func goToNextVideo(){
counter = counter+1
counter = min(playerItemArray.count-1, counter)
self.player?.replaceCurrentItem(with: self.playerItemArray[self.counter])
self.player?.seek(to: .zero)
self.player?.play()
}
When I change the item the transition is not smooth and I can see a flash displaying the view
below. There are some things I don't understand:
Why If I preloaded all the
AVPlayerItems
at the beginning (when I loop all urls strings) I still have to wait for them to be loaded when they become thecurrentItem
of the AVPlayer?For what concern the flickering effect I searched online and didn't find a solution, so I meticulously inspected the behaviour of the
AVPlayer
whenever changing Item and I describe it in this way:
First It shows a white view (view below)
Second It shows a frame of the video it is going to display (more of less the timeframe is at half of video)
Third It actually displays the video
This behaviour is really annoying and I'm wondering how I can solve it.
The Podfile:
When you call
replaceCurrentItem
, it will clean the image buffer then wait to render the next frame. So, the key point is to keep the last frame on the screen: Instead of usingAVPlayerLayer
to render video frame, you can useAVPlayerItemVideoOutput
to get the frame and render it using OpenGL ES (or Metal). You can also decide when to clean the image buffer.