我正在尝试使用 macOSX 上的 AVFoundation 和 AVAssetReader 从 Quicktime 电影文件中读取图像帧。我想通过 Metal 中的纹理贴图显示帧。有很多在线使用 AVAssetReader 的示例,但我无法让它为我想要的工作。
我可以从电影中读取基本帧数据——打印输出中的时间值、大小和持续时间看起来正确。但是,当我尝试获取 pixelBuffer 时,CMSampleBufferGetImageBuffer 返回 NULL。
let track = asset.tracks(withMediaType: AVMediaType.video)[0]
let videoReaderSettings : [String : Int] = [kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_32BGRA)]
let output = AVAssetReaderTrackOutput(track:track, outputSettings:nil) // using videoReaderSettings causes it to no longer report frame data
guard let reader = try? AVAssetReader(asset: asset) else {exit(1)}
output.alwaysCopiesSampleData = true
reader.add(output)
reader.startReading()
while(reader.status == .reading){
if let sampleBuffer = output.copyNextSampleBuffer(), CMSampleBufferIsValid(sampleBuffer) {
let frameTime = CMSampleBufferGetOutputPresentationTimeStamp(sampleBuffer)
if (frameTime.isValid){
print("frame: \(frameNumber), time: \(String(format:"%.3f", frameTime.seconds)), size: \(CMSampleBufferGetTotalSampleSize(sampleBuffer)), duration: \( CMSampleBufferGetOutputDuration(sampleBuffer).value)")
if let pixelBuffer : CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
getTextureFromCVBuffer(pixelBuffer)
// break
}
frameNumber += 1
}
}
}
此处已解决此问题(为什么 CMSampleBufferGetImageBuffer 返回 NULL),建议问题是必须在设置参数中指定视频格式而不是“nil”。所以我尝试用上面的“videoReaderSettings”替换“nil”,格式有各种值:kCVPixelFormatType_32BGRA、kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 等。
结果是帧“时间”值仍然正确,但“大小”和“持续时间”值为 0。但是,CMSampleBufferGetImageBuffer 确实返回了一些内容,之前为 0。但屏幕上出现了垃圾。
这是将 pixelBuffer 转换为 Metal 纹理的函数。
func getTextureFromCVBuffer(_ pixelBuffer:CVPixelBuffer) {
// Get width and height for the pixel buffer
let width = CVPixelBufferGetWidth(pixelBuffer)
let height = CVPixelBufferGetHeight(pixelBuffer)
// Converts the pixel buffer in a Metal texture.
var cvTextureOut: CVMetalTexture?
if CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, self.textureCache!, pixelBuffer, nil, .bgra8Unorm, width, height, 0, &cvTextureOut) != kCVReturnSuccess {
print ("CVMetalTexture create failed!")
}
guard let cvTexture = cvTextureOut, let inputTexture = CVMetalTextureGetTexture(cvTexture) else {
print("Failed to create metal texture")
return
}
texture = inputTexture
}
当我能够将 pixelBuffer 传递给此函数时,它会报告图像的正确大小。但正如我所说,出现在屏幕上的是垃圾——实际上它是由最近的 Safari 浏览器页面组成的。我不确定问题出在第一个函数还是第二个函数中。CMSampleBufferGetImageBuffer 的非零返回值令人鼓舞,但大小和持续时间的 0 则不是。
我发现这个线程(CMSampleBufferRef 的缓冲区大小)表明大小和持续时间显示 0 可能不是问题,所以问题可能在于转换为金属纹理?
知道我做错了什么吗?
谢谢!