iOS - 自定义视频播放器 -- (1)

背景需求

如何将视频添加上自定义的渲染效果,并显示?

大致流程

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?) -> 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()手动释放


Git Code:AVPlayer-Render

你可能感兴趣的:(iOS - 自定义视频播放器 -- (1))