Skip to content

Instantly share code, notes, and snippets.

@quantcon
Created January 22, 2019 14:16
Show Gist options
  • Save quantcon/adff27a826a2e03ad76ab14878fb31ca to your computer and use it in GitHub Desktop.
Save quantcon/adff27a826a2e03ad76ab14878fb31ca to your computer and use it in GitHub Desktop.
//
// VideoLoopView.swift
// Fobo
//
import UIKit
import AVFoundation
class VideoLoopView: UIView {
typealias OnPlayBlock = () -> ()
var onPlay: OnPlayBlock? = nil
fileprivate var player: AVQueuePlayer?
fileprivate var playerLooper: AVPlayerLooper?
fileprivate var playerLayer: AVPlayerLayer?
fileprivate var observerContext = 0
fileprivate static let dataParseQueue = DispatchQueue.global(qos: .utility)
fileprivate var wantsToPlay: Bool = false
var resourceName: String = ""
init(named: String) {
resourceName = named
super.init(frame: .zero)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
func setup() {
if let url = Bundle.main.url(forResource: resourceName, withExtension: ".mp4") {
self.loadFrom(url: url)
}
}
deinit {
self.player?.removeObserver(self, forKeyPath: "status", context: &observerContext)
}
}
extension VideoLoopView {
fileprivate func loadFrom(url: URL) {
self.player?.pause()
self.playerLayer?.removeFromSuperlayer()
self.player = nil
self.playerLayer = nil
let asset = AVAsset(url: url)
let item = AVPlayerItem(asset: asset)
let player = AVQueuePlayer(items: [item])
self.player = player
self.playerLooper = AVPlayerLooper(player: player, templateItem: item)
player.addObserver(
self,
forKeyPath: "status",
options: .new,
context: &self.observerContext
)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVVideoScalingModeResize
self.playerLayer = playerLayer
player.seek(to: kCMTimeZero)
playerLayer.frame = self.layer.bounds
self.layer.insertSublayer(playerLayer, at: 0)
player.actionAtItemEnd = .none
NotificationCenter.default.addObserver(
self,
selector: #selector(playerItemDidReachEnd(notification:)),
name: .AVPlayerItemDidPlayToEndTime,
object: player.currentItem
)
}
@objc func playerItemDidReachEnd(notification: Notification) {
if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: kCMTimeZero)
}
}
public override func layoutSubviews() {
super.layoutSubviews()
self.playerLayer?.frame = self.bounds
self.playerLayer?.videoGravity = AVVideoScalingModeResize
}
public func play(andThen: OnPlayBlock? = nil) {
self.wantsToPlay = true
guard let player = player else {
if andThen != nil {
onPlay = andThen
}
return
}
switch player.status {
case .readyToPlay:
player.play()
onPlay?()
onPlay = nil
case .failed:
return
case .unknown:
return
}
}
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &observerContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
if (wantsToPlay && keyPath == "status") {
if let newValue = change![.newKey] as? Int, AVPlayer.Status(rawValue: newValue) == AVPlayer.Status.readyToPlay {
self.play()
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment