iOS - 自定义视频播放器 -- (1)
2020-08-25 本文已影响0人
烧烤有点辣
背景需求
如何将视频添加上自定义的渲染效果,并显示?
大致流程
1、解码视频
2、获取视频帧
3、渲染视频帧
4、显示渲染后的视频帧
5、编码视频帧,生成新的视频
通过AVPlayer进行实时获取视频帧
核心对象:AVPlayer,AVPlayerItemVideoOutput
AVPlayer:驱动播放用例的中心阶层,是用于管理媒体资产的回放和定时的控制器对象
这里AVPlayer,我制作简单的播放,暂停,seek。并且添加上AVPlayerItemVideoOutput做一个视频帧输出的工作。
创建一个播放器
init(videoPath url:URL) {
super.init()
videoURL = url
let urlAsset = AVURLAsset(url: videoURL, options: [AVURLAssetPreferPreciseDurationAndTimingKey:true])
playerItem = AVPlayerItem(asset: urlAsset)
playerItem?.add(playerOutput)
player = AVPlayer(playerItem: playerItem)
player?.isMuted = false
playerItem?.addObserver(self, forKeyPath: "status", options: .new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "status" {
if let item = object as? AVPlayerItem {
switch item.status {
case .readyToPlay:
durationTime = CMTimeGetSeconds(item.duration)*1000
sendPlayerStauDelegate(videoStatu: .Prepared)
default:
sendPlayerStauDelegate(videoStatu: .Error)
}
}
}
}
func play() {
if !playing {
player?.play()
playing = true
}
}
func pause() {
if playing {
player?.pause()
playing = false
}
}
func seekTo(time:CMTime) {
player?.seek(to: time, toleranceBefore: .zero, toleranceAfter: .zero)
}
AVPlayerItemVideoOutput获取视频帧
var playerOutput:AVPlayerItemVideoOutput = {
// 根据需求获取相对于视频帧数据 通常选择RGBA数据,比较容易处理.系统默认是Y-UV数据
let out = AVPlayerItemVideoOutput(pixelBufferAttributes: [kCVPixelBufferPixelFormatTypeKey as String:kCVPixelFormatType_32BGRA])
return out
}()
var onPixelBuffer:CVPixelBuffer? {
get {
//获取当前视频帧
let time = self.playerOutput.itemTime(forHostTime: CACurrentMediaTime())
if self.playerOutput.hasNewPixelBuffer(forItemTime: time) {
return self.playerOutput.copyPixelBuffer(forItemTime: time, itemTimeForDisplay: nil)
} else {
return nil
}
}
}
主要的核心工具是AVPlayerItemVideoOutput,这对象相当于一个视频解码工具,对它进行属性设置,可以获取视频中某一时刻的想要数据的CVPixelBuffer视频帧。
func copyPixelBuffer(forItemTime itemTime: CMTime, itemTimeForDisplay outItemTimeForDisplay: UnsafeMutablePointer<CMTime>?) -> CVPixelBuffer?
通过获取到的CVPixelBuffer,进行OPenGL自定义渲染显示。
外部需要开启一个定时器,来实时的进行画面的刷新。定时器时间可以根据视频的FPS来控制。
至此如何获取视频帧就可以了。
如何获取视频帧,这里都比较简单,都是通过系统层去实现功能。
主要注意的是:
1、AVPlayerItemVideoOutput的获取的数据格式定义,根据需求设置RGBA还是YUV420的数据。
2、AVPlayer使用seek时候,使用精度比较高的方法,提高在seek时候的画面流畅度
func seek(to time: CMTime, toleranceBefore: CMTime, toleranceAfter: CMTime)
3、获取的CVPixelBuffer在Swift语言,不需要手动释放。在OC上需要调用CVPixelBufferRelease()
手动释放