Playing Video

Brian Voong :: Let’s Bild That App 튜토리얼을 통해, YoutubeApp 처럼 Video를 Control (재생, 정지, progress) 하는 기능 구현해보기

🎬 Core Process

AVPlayer

let urlString = "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
if let url = URL(string: urlString) {
	player = AVPlayer(url: url)
}

PeriodicTimeObserver

let interval = CMTime(value: 1, timescale: 2)
player?.addPeriodicTimeObserver(forInterval: interval,
                                queue: DispatchQueue.main,
                                using: { (progressTime) in
	                                    
	                                    // Label : track player progress
	                                    let seconds = CMTimeGetSeconds(progressTime)
	                                    let secondsString = String(format: "%02d", Int(seconds.truncatingRemainder(dividingBy: 60)))
	                                    let minutesString = String(format: "%02d", Int(seconds / 60))
	                                    self.currentTimeLabel.text = "\(minutesString):\(secondsString)"
	                                    
	                                    // Moving Slider
	                                    if let duration = self.player?.currentItem?.duration {
	                                        let durationSeconds = CMTimeGetSeconds(duration)
	                                        self.videoSlider.value = Float(seconds / durationSeconds)
	                                    }
									})

handlePause()

var isPlaying = false
    
func handlePause() {
    if isPlaying {
        player?.pause()
        pausePlayButton.setImage(UIImage(named: "play"), for: UIControlState())
    } else {
        player?.play()
        pausePlayButton.setImage(UIImage(named: "pause"), for: UIControlState())
    }
    
    isPlaying = !isPlaying
}

handleSliderChange()

slider.addTarget(self, action: #selector(handleSliderChange), for: .valueChanged)

func handleSliderChange() {
    if let duration = player?.currentItem?.duration {
        let totalSeconds = CMTimeGetSeconds(duration)
        let value = Float64(videoSlider.value) * totalSeconds
        let seekTime = CMTime(value: Int64(value), timescale: 1)
        player?.seek(to: seekTime, completionHandler: { (completedSeek) in
       		// Do something!
        })
    }
}

observeValue()

player?.addObserver(self, forKeyPath: "currentItem.Loaded", options: .new, context: nil)
override func observeValue(forKeyPath keyPath: String?,
                           of object: Any?,
                           change: [NSKeyValueChangeKey : Any]?,
                           context: UnsafeMutableRawPointer?) {
    
    if keyPath == "currentItem.Loaded" {
        activityIndicatorView.stopAnimating()
        controlsContainerView.backgroundColor = .clear
        pausePlayButton.isHidden = false
        isPlaying = true
        
        if let duration = player?.currentItem?.duration {
            let seconds = CMTimeGetSeconds(duration)
            
            let secondsText = Int(seconds) % 60
            let minutesText = String(format: "%02d", Int(seconds) / 60)
            videoLengthLabel.text = "\(minutesText):\(secondsText)"
        }
    }
    
}

UI Frame Sample

let height = keyWindow.frame.width * 9 / 16
let videoPlayerFrame = CGRect(x: 0, y: 0, width: keyWindow.frame.width, height: height)
let videoPlayerView = VideoPlayerView(frame: videoPlayerFrame)
fileprivate func setupGradientLayer() {
    let gradientLayer = CAGradientLayer()
    gradientLayer.frame = bounds
    gradientLayer.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
    gradientLayer.locations = [0.7, 1.0]
    controlsContainerView.layer.addSublayer(gradientLayer)
}