AVAssetReader 从 copyNextSampleBuffer(), Swift 获取像素缓冲区时遇到问题



我正在尝试在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"。 所以我尝试将"nil"替换为上面的"videoReaderSettings",并用各种格式值:kCVPixelFormatType_32BGRA,kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange等。

结果是帧"时间"值仍然正确,但"大小"和"持续时间"值为 0。 但是,CMSampleBufferGetImageBuffer 确实返回了一些东西,而在它之前它是 0。 但是垃圾出现在屏幕上。

这是将像素缓冲区转换为金属纹理的函数。

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
}

当我能够将像素缓冲区传递给此函数时,它确实会报告图像的正确大小。 但正如我所说,屏幕上出现的是垃圾 - 它实际上是由最近的Safari浏览器页面块组成的。 我不确定问题是出在第一个函数还是第二个函数上。 来自CMSampleBufferGetImageBuffer的非零返回值令人鼓舞,但大小和持续时间的0则不然。

我发现了这个线程(CMSampleBufferRef 的缓冲区大小),这表明显示 0 的大小和持续时间可能不是问题,所以也许问题出在转换为 Metal 纹理上?

知道我做错了什么吗?
谢谢!

将videoReaderSet放在AVAssetReaderTrackOutput。

let videoReaderSettings : [String : Int] = [kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_32BGRA)]
let output = AVAssetReaderTrackOutput(track:track, outputSettings: videoReaderSettings)

最新更新