Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save freshking/eccd41361edd9381a81bbeccce3a97fb to your computer and use it in GitHub Desktop.
Save freshking/eccd41361edd9381a81bbeccce3a97fb to your computer and use it in GitHub Desktop.
import UIKit
import AVKit
class VideoView: UIView {
// video layer that will stream the url
private let videoLayer = AVPlayerLayer()
// pan gesture used for scrubbing
private let panGesture = UIPanGestureRecognizer()
// time when scrubbing began
private var scrubbingBeginTime: CMTime?
// setup view
override init(frame: CGRect) {
super.init(frame: frame)
// self
backgroundColor = .black
// setup videoPlayer
videoLayer.videoGravity = .resizeAspect
layer.addSublayer(videoLayer)
// setup panGesture
panGesture.minimumNumberOfTouches = 1
panGesture.maximumNumberOfTouches = 1
panGesture.cancelsTouchesInView = true
panGesture.addTarget(self, action: #selector(didScrub(recognizer:)))
addGestureRecognizer(panGesture)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
// resize videoLayer frame to fit bounds
videoLayer.frame = bounds
}
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer == panGesture {
// only allow pan gesture to begin when panning horizontally
let translation = panGesture.translation(in: self)
return abs(translation.x) >= 0 && abs(translation.x) > abs(translation.y)
}
return super.gestureRecognizerShouldBegin(gestureRecognizer)
}
func beginStreaming() {
// setup player for videoLayer
let url = URL(string: "https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8")!
let player = AVPlayer(url: url)
videoLayer.player = player
// begin playback
videoLayer.player?.play()
}
@objc private func didScrub(recognizer: UIPanGestureRecognizer) {
guard videoLayer.isReadyForDisplay == true, let player = videoLayer.player, let currentItem = player.currentItem else {
return
}
switch recognizer.state {
case .possible:
// nothing to do here
break
case .began:
// pause playback when user begins panning
videoLayer.player?.pause()
// set time scrubbing began
scrubbingBeginTime = currentItem.currentTime()
case .changed:
guard let scrubbingBeginTime = scrubbingBeginTime else {
return
}
let totalSeconds = currentItem.duration.seconds
// translate point of pan in view
let point = recognizer.translation(in: self)
let scrubbingBeginPercent = Double(scrubbingBeginTime.seconds/totalSeconds)
// calculate percentage of point in view
var percent = Double(point.x/bounds.width)
percent += scrubbingBeginPercent
if percent < 0 {
percent = 0
} else if percent > 1.0 {
percent = 1.0
}
// calculate time to seek to in video timeline
let seconds = Float64(percent * totalSeconds)
let time = CMTimeMakeWithSeconds(seconds, preferredTimescale: currentItem.duration.timescale)
player.seek(to: time, toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero)
case .ended, .cancelled, .failed:
// reset scrubbing begin time
scrubbingBeginTime = nil
// resume playback after user stops panning
videoLayer.player?.play()
@unknown default:
break
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment