티스토리 뷰

2023.01.17 - [iOS개발/Swift 기본] - Swift 커스텀 동영상 플레이어 만들기

 

Swift 커스텀 동영상 플레이어 만들기

2023.01.13 - [iOS개발/Swift 기본] - Swift 기본 기능으로 동영상 플레이하기 Swift 기본 기능으로 동영상 플레이하기 이번에는 동영상에 대해 올려볼까 합니다. 이번편은 iOS에서 기본적으로 사용할 수

world-of-larooly.tistory.com

이 포스트는 전체 코드만 올려드립니다. 

자세한 내용은 위를 참고해주세요.

이거 코드입니다.

extension AVPlayer{
    var isPlaying: Bool {
           return rate != 0 && error == nil
       }
}
import Foundation
import UIKit

import AVFoundation
import AVKit



class CustomPlayerViewController : UIViewController {
    
    @IBOutlet weak var totalBtnsView: UIView! // 전체 UIView
    @IBOutlet weak var timeLineSlider: UISlider! // 시간 표시줄
    @IBOutlet weak var timeLineLbl: UILabel! // 00:00 / 00:00
    @IBOutlet weak var playBtn: UIButton! // 재생버튼
    @IBOutlet weak var speedDisplayLbl: UILabel! // x n 배속
    
    var moviePlayer : AVPlayer? // 비디오 재생 장치
    let videoBackView = UIView() // 비디오가 재생되는 뷰
    var UItimer : Timer? // UI 사라지는 시간 측정
    var timeObserver : Any? // 영상 시간 조절기  
    var speedMode = 2 // 속도단계 표시기
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.setVideoAnotherView()
        self.setStartUIStatus() // 시작시 UI설정
    }
    func setStartUIStatus(){// 시작시 어색한 UI 미리 수정
        totalBtnsView.isHidden = true // 처음 시작시 안보이게
        timeLineLbl.text = "00:00 / 00:00"
        timeLineSlider.value = 0.0
        self.setSpeedModeWithLevel(level: 2) // 1배속 시작
    }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.setVideoView(url: self.getSavedVideoUrl())
        
        self.resetUITimer()
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.toggleUIControls))
        self.view.addGestureRecognizer(tapGesture)
    }
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask{
        return .landscape // 화면이 자동 회전하도록
    }
    @IBAction func closeViewAction(_ sender: Any) {
        self.moviePlayer?.pause()
        self.moviePlayer = nil
        self.dismiss(animated: true)
    }
    func getSavedVideoUrl() -> URL {// 미리 저장된 영상 가져오기
        var savedViedoUrl = URL(fileURLWithPath: Bundle.main.path(forResource: "BigBuckBunny", ofType: "mp4")!)
        return savedViedoUrl
    }
    func getNetworkVideoUrl() -> URL {// 네트워크에서 영상 가져오기
        return URL(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")!
    }
    
    func setVideoView(url : URL){
        moviePlayer = AVPlayer(url: url)
        let playerLayer = AVPlayerLayer(player: moviePlayer)

        playerLayer.frame = videoBackView.frame;
        videoBackView.layer.addSublayer(playerLayer)
        moviePlayer?.play()
        setPlayBtnImage()

        let interval = CMTime(seconds: 0.01, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
        timeObserver = moviePlayer?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { elapsedTime in
            self.updateVideoPlayerSlider()
        })
    }
    func setVideoAnotherView() {// 바탕 뷰 만들기
        videoBackView.backgroundColor = UIColor.purple
        videoBackView.translatesAutoresizingMaskIntoConstraints = false
        let topConstraint = NSLayoutConstraint(item: videoBackView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: 0)
        let bottomConstraint = NSLayoutConstraint(item: videoBackView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
        let leadingConstraint = NSLayoutConstraint(item: videoBackView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0)
        let trailingConstraint = NSLayoutConstraint(item: videoBackView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0)
        view.addSubview(videoBackView)
        view.sendSubviewToBack(videoBackView)
        view.addConstraints([topConstraint, bottomConstraint, leadingConstraint, trailingConstraint])
    }
    //MARK: UI 타이머
    func resetUITimer(){
        UItimer?.invalidate()
        UItimer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(hideUIControls), userInfo: nil, repeats: false)
    }
    @objc func hideUIControls(){
        totalBtnsView.isHidden = true
    }
    @objc func toggleUIControls(){
        totalBtnsView.isHidden = !totalBtnsView.isHidden
        self.resetUITimer()
    }
   
    //MARK: 영상이 진행되는 동안 할일
    func updateVideoPlayerSlider() {// 타임라인에 연결할 함수입니다.
        guard let currentTime = moviePlayer?.currentTime() else { return }
        let currentTimeInSeconds = CMTimeGetSeconds(currentTime)
        timeLineSlider.value = Float(currentTimeInSeconds)
        if let currentItem = moviePlayer?.currentItem {
            let duration = currentItem.duration
            if (CMTIME_IS_INVALID(duration)) {
                return;
            }
            let currentTime = currentItem.currentTime()
            timeLineSlider.value = Float(CMTimeGetSeconds(currentTime) / CMTimeGetSeconds(duration))
        }
        self.setTimeLabel() // 시간 표시하기
    }
    func setTimeLabel(){
        if(getVideoTotalTime() != nil && getVideoCurrentTime() != nil){
            timeLineLbl.text = getVideoCurrentTime()! + " / " + getVideoTotalTime()!
        }else{
            timeLineLbl.text = "00:00 / 00:00"
        }
    }
    func getVideoTotalTime() -> String? {// 전체시간 가져오기
        let urlValue = self.getSavedVideoUrl()
        let asset: AVAsset = AVAsset(url: urlValue)
        let minute = Int(floor(asset.duration.seconds / 60))
        let second = Int(asset.duration.seconds) % 60
        return setTimeString(times: minute) + ":" + setTimeString(times: second)
    }
    func getVideoCurrentTime() -> String? {//현재 재생된 시간 가져오기
        guard let timeValue = moviePlayer?.currentItem?.currentTime() else {return nil}
        let minute = Int(floor(timeValue.seconds / 60))
        let second = Int(timeValue.seconds) % 60
        return setTimeString(times: minute) + ":" + setTimeString(times: second)
    }
    func setTimeString(times : Int) -> String {
        if(times < 10){
            return "0"+String(times)
        }else{
            return String(times)
        }
    }
    //MARK: Slider 터치시
    @IBAction func timeLineValueChanged(_ sender: Any) {
        moviePlayer?.pause() // 일단 정지
        guard let duration = moviePlayer?.currentItem?.duration else { return }
        let value = Float64(timeLineSlider.value) * CMTimeGetSeconds(duration)
        let seekTime = CMTime(value: CMTimeValue(value), timescale: 1)
            moviePlayer?.seek(to: seekTime, completionHandler: {okay in
                self.moviePlayer?.play() // 해당시간대로 이동이 가능하면 다시 플레이 시작
                self.setPlayBtnImage()
                self.setSpeedModeWithLevel(level: self.speedMode)
            })
        return
    }
    //MARK: play 여부에 따른 버튼 이미지 변화
    @IBAction func clickPlayAction(_ sender: Any) {
        if((self.moviePlayer?.isPlaying) ?? false){
            self.moviePlayer?.pause()
        }else{// 영상 정지시
            self.moviePlayer?.play()
            self.setSpeedModeWithLevel(level: self.speedMode)
        }
        self.setPlayBtnImage()
    }
    func setPlayBtnImage(){// 영상 플레이시
        if((self.moviePlayer?.isPlaying) ?? false){
            self.playBtn.setImage(UIImage(systemName: "pause.fill"), for: .normal)
        }else{// 영상 정지시
            self.playBtn.setImage(UIImage(systemName: "play.fill"), for: .normal)
        }
    }
    //MARK: 영상 10초 전후 이동
    @IBAction func goPlusVideoAction(_ sender: Any) {
        self.setActionMoveSeconds(goSecond: 10.0)
    }
    
    @IBAction func goMinusVideoAction(_ sender: Any) {
        self.setActionMoveSeconds(goSecond: -10.0)
    }
    func setActionMoveSeconds(goSecond : Double){
        self.moviePlayer?.pause()
        guard let currentTime = self.moviePlayer?.currentTime() else { return }
        let currentTimeInSecondsMove =  CMTimeGetSeconds(currentTime).advanced(by: goSecond) // 시간 계산
        let seekTime = CMTime(value: CMTimeValue(currentTimeInSecondsMove), timescale: 1)
        self.moviePlayer?.seek(to: seekTime, completionHandler: {okay in
            self.moviePlayer?.play()
            self.setPlayBtnImage()
            self.setSpeedModeWithLevel(level: self.speedMode)
        })
    }
    //MARK: 스피드 모드 제어
    @IBAction func clickSpeedAction(_ sender: Any) {
        switch speedMode{
        case 1:
            self.setSpeedModeWithLevel(level: 2)
        case 2:
            self.setSpeedModeWithLevel(level: 3)
        case 3:
            self.setSpeedModeWithLevel(level: 1)
        default:
            break
        }
        
    }
    func setSpeedModeWithLevel(level: Int){
        switch level{
        case 1: // 0.5  배속
            speedMode = 1
            speedDisplayLbl.text = "x 0.5 배속"
            self.moviePlayer?.playImmediately(atRate: 0.5)
            break
        case 2: // 1 배속
            speedMode = 2
            speedDisplayLbl.text = "x 1.0 배속"
            self.moviePlayer?.playImmediately(atRate: 1.0)
            break
        case 3: // 2 배속
            speedMode = 3
            speedDisplayLbl.text = "x 2.0 배속"
            self.moviePlayer?.playImmediately(atRate: 2.0)
            break
        default:
            break
        }
    }
    
    
}

생각보다 많이 안 길죠?

오늘도 파이팅입니다.

댓글