AVPlayer的组成:
AVPlayer:控制播放过程,相当于ViewController
AVPlayerItem:提供播放的资源,相当于Model
AVPlayerLayer: 显示播放的播放器,相当于View
使用AVPlayerItem创建AVPlayer,使用创建好的AVPlayer创建AVPlayerLayer
// view容器
playerView = UIView()
playerView.backgroundColor = UIColor.white
view.addSubview(playerView)
playerView.snp.makeConstraints { (make) in
make.top.equalTo(navView.snp.bottom)
make.left.right.equalTo(view)
make.bottom.equalTo(bottomView.snp.top)
}
// 使用URL创建AVPlayerItem
let item = AVPlayerItem(url: URL(string: currentUrl)!)
playerItem = item
// 使用创建好的AVPlayerItem创建AVPlayer
player = AVPlayer(playerItem: playerItem)
// 使用创建好的AVPlayer创建AVPlayerLayer
playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVLayerVideoGravityResizeAspect
playerLayer.backgroundColor = UIColor.white.cgColor
playerView.setNeedsDisplay()
playerView.layoutIfNeeded()
playerLayer.frame = CGRect(x: 0, y: 0, width: CGFloat(playerView.width), height: CGFloat(playerView.height))
playerView.layer.addSublayer(playerLayer)
AVPlayer可以播放网络资源,进行切换时使用方法:
playerItem = nil
playerItem = AVPlayerItem(url: URL(string: video.videoUrl)!)
player.replaceCurrentItem(with: playerItem)
播放:player.play()
暂停:player.pause()
AVPlayer没有playing方法,故通过rate来判断是否正在播放
if playerItem.status != AVPlayerItemStatus.failed {
if player.rate == 0 { //暂停
button.isSelected = true
player.play()
} else if player.rate == 1{ // 播放
button.isSelected = false
player.pause()
}
}
通过KVO来获取播放过程的信息
// 播放状态
playerItem.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.new, context: nil)
// 缓冲状态
playerItem.addObserver(self, forKeyPath: "loadedTimeRanges", options: NSKeyValueObservingOptions.new, context: nil)
// 播放完成的通知
NotificationCenter.default.addObserver(self, selector: #selector(playEndAction), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: playerItem)
// 播放完成通知事件
@objc fileprivate func playEndAction(){
player.seek(to: kCMTimeZero) { end in
self.playButton.isSelected = false
self.propressSlider.value = 0.0
self.currentTime.text = "00:00"
}
}
// KVO监听方法
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
// 拿到PLayerItem
let item = object as! AVPlayerItem
if (keyPath == "status") {
if (item.status == AVPlayerItemStatus.readyToPlay){
//开始播放
player.play()
playButton.isSelected = true
//隐藏加载状态
SVProgressHUD.dismiss()
// 当前视频资源的总时间
let totalSecond = Int(item.duration.value) / Int(item.duration.timescale) // 转换为秒
propressSlider.maximumValue = Float(totalSecond)
let m = totalSecond / 60
let s = totalSecond - m * 60
totalTime.text = String(format: "%02d:%02d", m, s)
// 更新时间进度,此为AVPlayer的观察者事件,每1秒更新一次
timeObserver = player.addPeriodicTimeObserver(forInterval: CMTimeMake(1, 1), queue: nil, using: { (time) in
let currentSecond = Int(item.currentTime().value) / Int(item.currentTime().timescale)
let m = currentSecond / 60
let s = currentSecond - m * 60
self.currentTime.text = String(format: "%02d:%02d", m, s)
self.propressSlider.setValue(Float(currentSecond), animated: true)
})
}else if (item.status == AVPlayerItemStatus.failed){
SVProgressHUD.dismiss()
SVProgressHUD.showError(withStatus: "加载失败")
}
}else if (keyPath == "loadedTimeRanges"){
// 缓冲时间进度
let timtInter = availableDuration()
// 总时间
let duration = playerItem.duration
let totalDuration = CMTimeGetSeconds(duration)
let value = timtInter / totalDuration
bufferProgress.setProgress(Float(value), animated: true)
}
}
}
// 缓冲时间获取
func availableDuration() -> TimeInterval{
// 获取缓冲区域
let loadTimeRanges = player.currentItem?.loadedTimeRanges
let timeRange = loadTimeRanges?.first?.timeRangeValue
// 开始时间区域
let startSeconds = CMTimeGetSeconds((timeRange?.start)!)
// 时间区域值
let durationSeconds = CMTimeGetSeconds((timeRange?.duration)!)
// 缓冲总进度
let result = startSeconds + durationSeconds
return result
}
此方法中总共使用了4个observer。
在页面返回或切换下一首上一首时都需要移除,否则观察的不是当前的AVPlayerItem
fileprivate func removePlayerPlayerItemObserver(){
playerItem.removeObserver(self, forKeyPath: "status")
playerItem.removeObserver(self, forKeyPath: "loadedTimeRanges")
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: playerItem)
// 播放时间监听移除
if timeObserver != nil {
player.removeTimeObserver(timeObserver)
}
if player.rate == 1{
player.pause()
}
playButton.isSelected = false
}
通过slider来跳转视频播放进度:
// 视频跳转事件
@objc fileprivate func videoSliderChangeValueClick(){
if (player.status == AVPlayerStatus.readyToPlay) {
playerItem.seek(to: CMTime(value: CMTimeValue(propressSlider.value), timescale: 1))
}
}
通过slider进行视频音量控制
// 控制音量
@objc fileprivate func volumeSliderChangeValueClick(){
player.volume = volumeProgressSlider.value
}
// 增加音量 button事件
@objc fileprivate func addVolumeClick(){
if player.volume >= 1.0 {return}
player.volume += 0.1
volumeProgressSlider.setValue(player.volume, animated: true)
}
// 减小音量 button事件
@objc fileprivate func decreaseVolumeClick(){
if player.volume <= 0.0 {return}
player.volume -= 0.1
volumeProgressSlider.setValue(player.volume, animated: true)
}