美文网首页
AVPlayer的使用

AVPlayer的使用

作者: 古月思吉 | 来源:发表于2018-12-26 20:42 被阅读0次
    • 注:
      此文是对 AVPlayer 的简单封装,实现播放器的常用功能。由于播放器的UI自定义性,所以此文没有关于播放器 UI 的代码,而是将 player 功能抽取出来封装的工具类。

    AJPlayerManager.swift 的功能:
    (1)更新播放时间(当前播放时间、总时间)
    (2)更新播放状态
    (3)更新缓存时间(开始时间、缓存时间、总时长)

    /*
     AVPlayer的工具类封装
     */
    
    import UIKit
    import AVKit
    
    @objc enum AJPlayerStatus:Int {
        case playbackBufferEmpty//正在缓存
        case playbackLikelyToKeepUp//缓存好了
        case readyToPlay//准备播放
        case playToEndTime//播放结束
        case failed//播放失败
        case unknown//未知情况
    }
    
    @objc protocol AJPlayerManagerDelegate: NSObjectProtocol {
        
        @objc optional func updatePlayerTime(currentTime:Float64, totalTime:Float64)//更新播放时间(当前播放时间、总时间)
        @objc optional func updatePlayerStatus(status:AJPlayerStatus)//更新播放状态(播放状态)
        @objc optional func updateLoadedTime(startTime:Float64, durationTime:Float64, totalTime:Float64)//更新缓存时间(开始时间、缓存时间、总时长)
        
    }
    
    class AJPlayerManager: NSObject {
        
        //MARK: - 属性
        var player:AVPlayer?//播放器
        var playerItem:AVPlayerItem?//媒体资源管理对象
        var playerLayer:AVPlayerLayer?//显示视频的图层
        weak var delegate:AJPlayerManagerDelegate?
        var timeObserver:Any?//实时监听者
        var isAddPlayerItemObserver = false//属否添加playerItem监听
        
        
        //MARK: - 初始化设置
        func config(delegate:AJPlayerManagerDelegate?, url:URL, toView:UIView) {
            self.delegate = delegate
            self.playerItem = AVPlayerItem.init(url: url)
            self.player = AVPlayer.init(playerItem: self.playerItem)
            self.player?.rate = 1.0
            self.playerLayer = AVPlayerLayer.init(player: self.player)
            self.playerLayer?.videoGravity = .resizeAspectFill
            self.playerLayer?.frame = toView.bounds
            toView.layer.addSublayer(self.playerLayer!)
            //实时的获取播放时长
            self.timeObserver = self.player?.addPeriodicTimeObserver(forInterval: CMTimeMake(1, 50), queue: DispatchQueue.main) { [weak self] (time) in
                guard self?.player?.currentItem != nil else {
                    return
                }
                let currentTime = CMTimeGetSeconds(time).isNaN ? 0 : CMTimeGetSeconds(time)
                let totalTime = CMTimeGetSeconds((self?.player?.currentItem?.duration)!).isNaN ? 0 : CMTimeGetSeconds((self?.player?.currentItem?.duration)!)
                self?.delegate?.updatePlayerTime?(currentTime: currentTime, totalTime: totalTime)
            }
            //playerItem-添加监听
            self.isAddPlayerItemObserver = true
            self.playerItem?.addObserver(self, forKeyPath: "status", options: .new, context: nil)//播放状态
            self.playerItem?.addObserver(self, forKeyPath: "loadedTimeRanges", options: .new, context: nil)//加载的缓存时间
            self.playerItem?.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil)//缓存不够(正在缓存)
            self.playerItem?.addObserver(self, forKeyPath:"playbackLikelyToKeepUp", options: .new,context:nil)//缓存好了
            //添加播放结束的监听
            NotificationCenter.default.addObserver(self, selector: #selector(playToEndTime), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
        }
        
        //销毁方法
        func destory() {
            self.pause()
            self.delegate = nil
            self.playerLayer?.removeFromSuperlayer()
            if let observer = self.timeObserver {
                try? self.player?.removeTimeObserver(observer)
                self.timeObserver = nil
            }
            if isAddPlayerItemObserver {
                try? self.playerItem?.removeObserver(self, forKeyPath: "status", context: nil)
                try? self.playerItem?.removeObserver(self, forKeyPath: "loadedTimeRanges", context: nil)
                try? self.playerItem?.removeObserver(self, forKeyPath: "playbackBufferEmpty", context: nil)
                try? self.playerItem?.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp", context: nil)
                self.isAddPlayerItemObserver = false
            }
            self.playerItem?.cancelPendingSeeks()
            self.playerItem?.asset.cancelLoading()
            self.playerItem = nil
            self.player?.replaceCurrentItem(with: nil)
            self.player = nil
            NotificationCenter.default.removeObserver(self)
        }
        
        
        //MARK: - 其他
        //更新当前时间
        func updateCurrentTime(value:Float) {
            guard let item = self.player?.currentItem else { return }
            let totalTime = CMTimeGetSeconds(item.duration)
            let currentTime = totalTime*Double(value)
            let time = CMTime.init(seconds: currentTime, preferredTimescale: item.asset.duration.timescale)
            self.player?.seek(to: time)
        }
        
        
        //MARK: - 播放 & 暂停
        func play() {//播放
            self.player?.play()
        }
        func pause() {//暂停
            self.player?.pause()
        }
        
        
        //MARK: - 监听方法
        //playerItem的监听
        override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
            guard let item = self.playerItem else { return }
            if keyPath == "status" {//播放状态
                switch item.status{
                case .readyToPlay://准备播放
                    self.delegate?.updatePlayerStatus?(status: .readyToPlay)
                case .failed://播放失败
                    self.delegate?.updatePlayerStatus?(status: .failed)
                case.unknown://未知情况
                    self.delegate?.updatePlayerStatus?(status: .unknown)
                }
            } else if keyPath == "loadedTimeRanges"{//加载的缓存区间
                let loadTimeArray = item.loadedTimeRanges
                //获取最新缓存的区间
                guard let newTimeRange : CMTimeRange = loadTimeArray.first as? CMTimeRange else { return }
                let startSeconds = CMTimeGetSeconds(newTimeRange.start)
                let durationSeconds = CMTimeGetSeconds(newTimeRange.duration)
                //let totalBuffer = startSeconds + durationSeconds//缓冲总长度
                let totalSeconds = CMTimeGetSeconds(item.duration)//总时长
                self.delegate?.updateLoadedTime?(startTime: startSeconds.isNaN ? 0 : startSeconds, durationTime: durationSeconds.isNaN ? 0 : durationSeconds, totalTime: totalSeconds.isNaN ? 0 : totalSeconds)
            } else if keyPath == "playbackBufferEmpty"{//正在缓存
                self.delegate?.updatePlayerStatus?(status: .playbackBufferEmpty)
            } else if keyPath == "playbackLikelyToKeepUp"{//缓存好了
                self.delegate?.updatePlayerStatus?(status: .playbackLikelyToKeepUp)
            }
        }
        //播放结束的监听
        @objc func playToEndTime() {
            self.player?.seek(to: kCMTimeZero)//回退到0
            self.delegate?.updatePlayerStatus?(status: .playToEndTime)
        }
        
    }
    

    参考文章:
    https://www.jianshu.com/p/20e5e6d5f3e5

    相关文章

      网友评论

          本文标题:AVPlayer的使用

          本文链接:https://www.haomeiwen.com/subject/zlmklqtx.html