AVPlayer的使用

  • 注:
    此文是对 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的使用)