AVPlayer是一个功能强大的iOS视频播放器(也是最坑的),在这仅仅介绍AVPlayer的基本使用,但是不太推荐使用,最好还是自己解码并显示,或者使用其他播放器。
AVPlayer
SystemPlayerView
系统建议的播放器View
写法,也是最基础的播放器。
1 | open class SystemPlayerView: UIView { |
PlayerViewController
创建一个ViewController
来放置Player,所有的事件都由Controller来处理(进度条拖动,暂停,开始。。)
Observer
定义全局KVOContext:private var MediaPlayerViewControllerKVOContext = 0
比较有意义的就是下面几个属性的监听。
- 播放器的
rate
,为0的时候表示暂停,不为0的时候表示 可能在播放
这里的播放并不是我们常说的播放状态,它更像
AudioToolBox
里AudioQueueStart
之后的状态,表示当前队列服务在运行,如果当前在缓冲的状态,视频画面是不动的,但是依旧是播放的状态。所以仅仅依靠这个判断画面是否在动其实并不是很准确。
1 | player.addObserver(self, forKeyPath: #keyPath(AVPlayer.rate), options: [.new], context: &MediaPlayerViewControllerKVOContext) |
- 缓冲区是否空了,true表示当前缓冲区已经空了,无法继续播放视频;false表示当前缓冲区有数据。
但是并不表示这个值为false的时候视频可以播放,只要缓冲区有数据这个值就会变成false,所以这个值最常用的就是监听到这个值为true的时候显示缓冲Loading。
1 | playerItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.isPlaybackBufferEmpty), options: [.new], context: &MediaPlayerViewControllerKVOContext) |
- 缓冲区的数据是否可以支持视频开始播放,true表示当前视频可以播放了,false表示缓冲区的Buffer还不能完全支持播放器进行播放。
当监听到这个值为true的时候消失Loading。
1 | playerItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.isPlaybackLikelyToKeepUp), options: [.new], context: &MediaPlayerViewControllerKVOContext) |
- 是否缓冲完成
当值为true的时候表示当前的视频已经缓冲完成,之后不再进行缓冲操作,之后的I/O操作将被暂停。
1 | playerItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.isPlaybackBufferFull), options: [.new], context: &MediaPlayerViewControllerKVOContext) |
- 播放总时间,获取到总的播放时长。
一般监听到这个值一个有效值的时候,播放、暂停按钮可以点击;进度条变成可以操作;显示总时间等操作。
1 | playerItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.duration), options: [.new], context: &MediaPlayerViewControllerKVOContext) |
- 播放状态,获取当前的播放状态。
总共有三种状态,如果监听了
initial
首先是unknown
,在这里直接监听new
,所以直接返回failed
或者readyToPlay
,当监听到readyToPlay
表示当前视频已经准备好播放了,当监听到failed
表示当前视频无法播放。⚠️ 在这里才可以进行seek
操作。
1 | playerItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: [.new], context: &MediaPlayerViewControllerKVOContext) |
- 缓冲进度。
在这里我们可以拿到播放器的缓冲进度(但这个缓冲进度可能并不是连续的,而且在每次seek之后都会从seek点开始缓冲)。
1 | playerItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.loadedTimeRanges), options: [.new], context: &MediaPlayerViewControllerKVOContext) |
- 具体的监听处理:
判断当前视频是否可以播放的一些关键点:
1 | private static let assetKeysRequiredToPlay = [ |
具体的监听:
1 | override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { |
全屏实现:
设备可旋转:
当设备可以旋转是最好处理的,直接调用下面代码就可以实现全屏模式。
判断是否是全屏:1
var isFullScreen: Bool { return UIApplication.shared.statusBarOrientation.isLandscape }
监听旋转事件:1
NotificationCenter.default.addObserver(self, selector: #selector(), name: .UIApplicationDidChangeStatusBarOrientation, object: nil)
直接旋转:1
2
3
4
5
6
7
8
9if isFullScreen {
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
UIApplication.shared.setStatusBarHidden(false, with: .fade)
UIApplication.shared.statusBarOrientation = .portrait
} else {
UIDevice.current.setValue(UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation")
UIApplication.shared.setStatusBarHidden(false, with: .fade)
UIApplication.shared.statusBarOrientation = .landscapeRight
}
设备无法旋转:
如果在Xcode
中配置仅Portrait
模式,那么调用上面的方法是没有用的。
所以针对这个就需要使用仿射变换
了(有点坑)。
1 | /// Full screen view |
Code
1 | import UIKit |
代码中嵌入了很多UI的元素,由于自己后续不太会用系统的播放器,所以并不多详细讲解,具体可以看下面的视频。