本周我有一段悲惨的时间试图从Quicktime电影中获取视频帧并将其显示在Metal纹理中 . 它比我预想的要困难得多 . 我只想显示第0帧,然后使用箭头键在视频帧中前后移动 . 在使用AVPlayererItemVideoOutput.hasNewPixelBuffer(forItemTime:time)函数失败后,我现在尝试使用seek()函数 . 这几乎可行 . 视频暂停后,我会在我想要的时间内调用搜索功能,并说它成功了 .

但是,每隔几帧,hasNewPixelBuffer()函数返回'false',而copyPixelBuffer()函数只返回与当前显示的帧相同的数据 . 起初,认为它可能与视频(h264)具有帧重新排序的事实有关 . 但是当我以正确的顺序使用所有帧的视频时,我得到相同的结果 . 我已经尝试将搜索容差设置为0,然后尝试各种值,如半帧或更少,以防万一我的计算时间有一些浮点舍入问题 . 似乎没什么用 .

有人知道怎么做吗?它应该是如此简单,但我已经尝试了我能想到的一切 . 我也尝试了AVAssetImageGenerator,它做了同样的事情 . AVAssetReader将按顺序报告所有帧,但如果您只是想随机访问帧,那么这并没有多大帮助 .

本周之后我讨厌AVFoundation - 为什么这么简单的视频任务必须如此困难......

任何建议赞赏 . 这是我的代码的基础知识 .

func gotoFrame(_ frame:Int) {
        frameNanoseconds = Int64( (Double(frame)/Double(BigRender.framerate)) * Double(1_000_000_000))
        let cmTime = CMTimeMake(frameNanoseconds ,1_000_000_000)

        playerItem.seek(to: cmTime, toleranceBefore:tolerance, toleranceAfter:tolerance) {
                (result: Bool) in
                if result {
                    //print("seek got back: \(result)")
                    self.needFrame = true
                    self.videoTime = cmTime
                }
                else {
                    print("bad seek!")
                }
        }
}

当搜索回调完成时,我的渲染器将尝试抓取视频帧 . 寻求总是会成功 . 它 grab 了似乎失败的新视频帧......每隔几帧,'hasNewPixelBuffer'返回false . 我每按一次键只想尝试一次新帧,所以这不是无法跟上播放速度的问题 . 它也是可重复的 - 它跳过的帧总是和我来回走动的帧一样 .

我感谢任何人的建议 .

if playerItemVideoOutput.hasNewPixelBuffer(forItemTime: time), let pixelBuffer = playerItemVideoOutput.copyPixelBuffer(forItemTime: time, itemTimeForDisplay: nil) {

    // Get width and height for the pixel buffer
    let width = CVPixelBufferGetWidth(pixelBuffer)
    let height = CVPixelBufferGetHeight(pixelBuffer)

    // Converts the pixel buffer in a Metal texture.
    if CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, self.textureCache!, pixelBuffer, nil, .bgra8Unorm, width, height, 0, &cvTextureOut) != kCVReturnSuccess {
        print ("texture create failed!")
    }

    guard let cvTexture = cvTextureOut, let inputTexture = CVMetalTextureGetTexture(cvTexture) else {
        print("Failed to create metal texture")
        return
    }

    texture = inputTexture    

 }