本周我有一段悲惨的时间试图从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
}