美文网首页
iOS开发之在线视频播放,进度精准拖动

iOS开发之在线视频播放,进度精准拖动

作者: iOS刘耀宗 | 来源:发表于2021-06-17 10:17 被阅读0次

本文讲解的是 iOS 视频播放, 支持本地,在线播放. 进度拖动,精准跳转,获取视频的长度等问题
demo 记得点亮 star. 3Q

iOS 视频播放主要使用的类 AVPlayerItem,AVPlayerLayer,AVPlayer
AVPlayerItem: 提供视频信息, 利用视频的 url 创建一个对象.利用 kvo 监听 status,可以获取当前视频的状态.

playerItem = AVPlayerItem(url: videoUrl)
//添加状态观察者
playerItem?.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.new, context: nil)
//添加视频播放结束通知
NotificationCenter.default.addObserver(self, selector: #selector(moviePlayDidEnd(userinfo:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: playerItem)

extension LYZPlayerView {
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        //状态
        if keyPath == "status" {
            print(change?[NSKeyValueChangeKey(rawValue: "new")] ?? "")
            let status: Int = change?[NSKeyValueChangeKey(rawValue: "new")] as! Int
            playerStatusBlock(AVPlayer.Status(rawValue: status) ?? AVPlayer.Status.unknown)
            switch status {
            case AVPlayer.Status.unknown.rawValue:
                do {
                    print("不知道状态")
                }
            case AVPlayer.Status.readyToPlay.rawValue:
                do {
                    loading.stopAnimating()
                    print("准备开始播放")
                    self.player?.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 5), queue: nil, using: {[weak self] time in
                        guard let _self = self else {
                            return
                        }
                        let second: Int64 = Int64(_self.playerItem?.currentTime().value ?? 0) / Int64(_self.playerItem?.currentTime().timescale ?? 0)
                        print("时间----\(second)")
                        _self.controlView.currentTime = second
                        _self.currentTimeBlock(second)
                    })
                }
                
            case AVPlayer.Status.failed.rawValue:
                do {
                    print("播放失败")
                    loading.stopAnimating()
                }
            default: do {
                print("123")
            }
            
            }
            
        }
        //缓冲进度
        if keyPath == "loadedTimeRanges" {
            print("进度数据是--\(change?[NSKeyValueChangeKey(rawValue: "new")] ?? "")")
        }
    }
    
    
    @objc func moviePlayDidEnd(userinfo: Notification) {
        print("视频结束")
        playerEndBlock()
        if isRoop {
            //跳转到 0
            player?.seek(to: CMTime(value: 0, timescale: 5))
            play()
        }
    }
    
    
}

AVPlayer: 就类似视频的控制者. 可以用来控制视频的播放,暂停,跳转等. 它的创建依赖于AVPlayerItem

player = AVPlayer(playerItem: playerItem)
//播放
player?.play()
//暂停
player?.pause()
//播放状态是否正在播放
    var isPlaying: Bool {
        get {
            return player?.timeControlStatus == .playing
        }
    }

AVPlayerLayer: 用于视频的显示. 可以理解成一个 view. 它的创建依赖于AVPlayer

playerLayer = AVPlayerLayer(player: player)
//当前 view 将视频 layer 添加进去
layer.addSublayer(playerLayer ?? AVPlayerLayer())

简单总结: AVPlayer控制视频状态, AVPlayerLayer用于显示,AVPlayerItem提供视频信息
获取视频的长度 s

/*因为获取视频信息本身是个耗时操作,放在子线程去操作,totalTime 里面的赋值是在主线程的,这里封装过
简单解释一下: duration是 CMTime  类
public var value: CMTimeValue / * !@field value CMTime的值。值/时间表=秒。*/ 我们都知道视频都是一帧一帧凑起来的. value就是视频总共的帧数
public var timescale: CMTimeScale / * !@field timescale CMTime的时间刻度。值/时间表=秒。*/
timescale 表示 每一秒里面有多少帧
所以 value / timescale = 视频的长度(秒)*/
let queue = DispatchQueue(label: "asset")
        queue.async {
            let asset = AVAsset(url: self.videoUrl)
            let totalTime = Int64(asset.duration.value) / Int64(asset.duration.timescale)
            self.controlView.totalTime = totalTime
        }

视频精准跳转.拖动 UISlider,监听 value 改变方法如下:
//为什么timescale要设置成 600 CMTime教程

@objc func sliderValueChanged(slider: UISlider) {
         print("slider == \(slider.value)")
        let time: Float64 = Float64(self.totalTime)  * Float64(slider.value)
        print("当前跳转的时间是\(time)")
        let cmtime: CMTime = CMTime(value: CMTimeValue(time * 600), timescale: 600)
        //需要使用下面的额方法进行定位, 后面两个参数设置容错范围
        self.playerView?.player?.seek(to: cmtime, toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero)
    }

相关文章