如何同步 AVPlayer 和 MTKView

Pen*_*ise 5 synchronization ios avplayer swift metal

我有一个项目,用户可以在其中拍摄视频,然后向其中添加过滤器或更改亮度和对比度等基本设置。为此,我使用BBMetalImage,它基本上返回MTKView 中的视频(BBMetalView在项目中命名为 a )。

一切正常 - 我可以播放视频,添加过滤器和所需的效果,但没有音频。我就此问过作者,他建议为此使用AVPlayer(或AVAudioPlayer)。所以我做了。但是,视频和音频不同步。可能首先是因为比特率不同,库的作者也提到帧率会因为过滤过程而不同(这消耗的时间是可变的):

渲染视图 FPS 与实际速率并不完全相同。因为视频源输出帧经过过滤器处理,过滤器处理时间是可变的。

首先,我将视频裁剪为所需的纵横比 (4:5)。我将此文件 (480x600) 保存在本地,使用AVVideoProfileLevelH264HighAutoLevelas AVVideoProfileLevelKey. 我的音频配置使用NextLevelSessionExporter,具有以下设置:AVEncoderBitRateKey: 128000AVNumberOfChannelsKey: 2AVSampleRateKey: 44100

然后,BBMetalImage 库获取这个保存的音频文件并提供一个 MTKView (BBMetalView) 来显示视频,允许我实时添加过滤器和效果。设置类型如下所示:

self.metalView = BBMetalView(frame: CGRect(x: 0, y: self.view.center.y - ((UIScreen.main.bounds.width * 1.25) / 2), width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width * 1.25))
self.view.addSubview(self.metalView)
self.videoSource = BBMetalVideoSource(url: outputURL)
self.videoSource.playWithVideoRate = true
self.videoSource.audioConsumer = self.metalAudio
self.videoSource.add(consumer: self.metalView)
self.videoSource.add(consumer: self.videoWriter)
self.audioItem = AVPlayerItem(url: outputURL)                            
self.audioPlayer = AVPlayer(playerItem: self.audioItem)
self.playerLayer = AVPlayerLayer(player: self.audioPlayer)
self.videoPreview.layer.addSublayer(self.playerLayer!)
self.playerLayer?.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
self.playerLayer?.backgroundColor = UIColor.black.cgColor
self.startVideo()
Run Code Online (Sandbox Code Playgroud)

并且startVideo()是这样的:

audioPlayer.seek(to: .zero)
audioPlayer.play()
videoSource.start(progress: { (frameTime) in
    print(frameTime)
}) { [weak self] (finish) in
guard let self = self else { return }
    self.startVideo()
}
Run Code Online (Sandbox Code Playgroud)

由于外部库/库,这可能非常模糊。但是,我的问题很简单:有什么方法可以让MTKView我的AVPlayer? 这对我有很大帮助,我相信Silence-GitHub也会将此功能实现到库中以帮助很多其他用户。欢迎任何有关如何解决此问题的想法!

小智 1

我按如下方式自定义 BBMetalVideoSource 然后它就起作用了:

  1. 在 BBMetalVideoSource 中创建委托以获取我们要同步的音频播放器的当前时间
  2. 在 func 中private func processAsset(progress:, completion:),我将此代码块替换if useVideoRate { //... }为:

    if useVideoRate {
        if let playerTime = delegate.getAudioPlayerCurrentTime() {
            let diff = CMTimeGetSeconds(sampleFrameTime) - playerTime
            if diff > 0.0 {
                sleepTime = diff
                if sleepTime > 1.0 {
                    sleepTime = 0.0
                }
                usleep(UInt32(1000000 * sleepTime))
            } else {
                sleepTime = 0
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

这段代码帮助我们解决了这两个问题:1.预览视频效果时没有音频,2.音频与视频同步。