ios AVPlayerLayer 사용해보기

ios AVPlayerLayer 사용해보기

 

 

 



import UIKit
import AVFoundation

class ViewController: UIViewController {

    @IBOutlet weak var videoView: UIView!
    
    var player = AVPlayer()
    var playerLayer = AVPlayerLayer()
    var isVideoPlaying = false
    
    @IBOutlet weak var playButton: UIButton!
    @IBOutlet weak var currentTimeLabel: UILabel!
    @IBOutlet weak var durationLabel: UILabel!
    @IBOutlet weak var timeSlider: UISlider!
        
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //재생할 파일 URL
        guard let url = URL(string:"https://file-examples.com/storage/fef1706276640fa2f99a5a4/2017/04/file_example_MP4_640_3MG.mp4") else {
            return
        }

        ///AVPlayer 초기화
        self.player = AVPlayer(url: url)
        
        self.player.volume = 0.0
        self.timeSlider.value = 0
        self.timeSlider.minimumValue = 0
        
        
        self.playerLayer = AVPlayerLayer(player: player)
        self.playerLayer.videoGravity = .resizeAspectFill
        videoView.layer.addSublayer(playerLayer)
        
        
        /// 재생준비 완료를 위한 옵저버
        if let curItem = self.player.currentItem{
            curItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: [.old, .new], context: nil)
        }
        
        /// AVPlayer는 시간 변화를 관찰하기 위해 addPeriodicTimeObserver 메소드를 제공한다.
        let interval = CMTime(seconds: 0.01, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
        self.player.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main) { [weak self] currentTime in
            
            /// 재생 후 0.01초 간격으로 Slider를 업데이트 함.
            if let curTime = self?.player.currentItem?.currentTime(){
                self?.updateSlider(curTime)
                self?.updateCurrentTimeText(curTime)
            }
        }
        
        /// 비디오 종료 체크를 위한 콜백 함수 등록
        NotificationCenter.default.addObserver(self, selector: #selector(videoFinish), name: .AVPlayerItemDidPlayToEndTime, object: nil)
        
    }
    
    

    //MARK: 슬라이더 이동
    /// 슬라이더 이동 시키기
    func updateSlider(_ currentTime:CMTime){
        if let currentItem = self.player.currentItem{
            let duration = currentItem.duration
            self.timeSlider.value = Float(CMTimeGetSeconds(currentTime))
            self.timeSlider.maximumValue = Float(CMTimeGetSeconds(duration))
        }
    }

    //MARK: 진행시간표시
    /// 진행시간 표시
    func updateCurrentTimeText(_ currentTime:CMTime){
        /// 현재시간 구하기
        self.currentTimeLabel.text = self.getTimeString(roundedSeconds: CMTimeGetSeconds(currentTime))
    }
    
    
    //MARK: 준비
    /// 준비완료 체크 옵저버
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        
        if keyPath == #keyPath(AVPlayerItem.status) {
            
            let status: AVPlayerItem.Status
            
            if let statusNumber = change?[.newKey] as? NSNumber {
                status = AVPlayerItem.Status(rawValue: statusNumber.intValue)!
            } else {
                status = .unknown
            }
            
            switch status {
            case .readyToPlay:

                print("readyToPlay")
                /// 전체 시간 구하기
                if let currentItem = self.player.currentItem{
                    let duration = currentItem.duration
                    self.durationLabel.text = self.getTimeString(roundedSeconds: CMTimeGetSeconds(duration))
                }
            case .failed:
                print("Fail")
            case .unknown:
                print("unknown")
            @unknown default:
                fatalError()
            }
        }
    }

    //MARK: 비디오 재생끝
    @objc func videoFinish(_ notification: Notification){
        print("비디오 재생 끝")
        let seekTime = CMTimeMake(value: Int64(0.0) * 1000, timescale: 1000)
        self.player.seek(to: seekTime)
        self.player.pause()
        self.currentTimeLabel.text = "00:00"
        playButton.setTitle("재생", for: .normal)
    }
    
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        self.playerLayer.frame = self.videoView.bounds
    }
    
    
    
    //MARK: 슬라이더 이벤트
    /// 슬라이더 이벤트
    @IBAction func didChangedProgressBar(_ sender: UISlider) {
        
        /// 재생이 안되어 있는 상태에서 슬라이드 이동시 재생 처리
        if !isVideoPlaying {
            self.player.play()
            playButton.setTitle("정지", for: .normal)
            isVideoPlaying = true
        }
        
        let seekTime = CMTimeMake(value: Int64(sender.value) * 1000, timescale: 1000)
        self.player.seek(to: seekTime)
    }
    
    
    
    //MARK: 재생버튼
    @IBAction func playAction(_ sender: UIButton) {
            
        if isVideoPlaying {
            self.player.pause()
            sender.setTitle("재생", for: .normal)
        }else{
            
            self.player.play()
            sender.setTitle("정지", for: .normal)
        }
            
        isVideoPlaying.toggle()
        
    }
    
    
    //MARK: 시간->문자
    func getTimeString(roundedSeconds:Float64) -> String{
        
        var hours:  Int { return Int(roundedSeconds / 3600) }
        var minute: Int { return Int(roundedSeconds.truncatingRemainder(dividingBy: 3600) / 60) }
        var second: Int { return Int(roundedSeconds.truncatingRemainder(dividingBy: 60)) }
        var positionalTime: String {
            return hours > 0 ?
                String(format: "%d:%02d:%02d",
                       hours, minute, second) :
                String(format: "%02d:%02d",
                       minute, second)
        }
        
        return positionalTime
    }
    
}

 

 

PlayerLayoutView.zip
0.05MB

//참고
 https://qteveryday.tistory.com/188
 https://velog.io/@wannabe_eung/iOS-AVFoundation%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EC%98%81%EC%83%81-%EC%A7%84%ED%96%89%EB%B0%94UISlider-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0

 https://wlgusdn700.tistory.com/82