我试图在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
}
}
}
这里解决了这个问题(Why does CMSampleBufferGetImageBuffer return NULL),其中建议问题是必须在settings参数中指定视频格式而不是' nil'。所以我尝试更换“nil”#39;使用' videoReaderSettings'上面,格式的各种值: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不是。
我发现这个帖子(Buffer size of CMSampleBufferRef)表明显示0的大小和持续时间可能不是问题,所以可能问题是转换为Metal纹理?
知道我做错了什么吗? 谢谢!
答案 0 :(得分:0)
将videoReaderSetting放入AVAssetReaderTrackOutput。
from matplotlib.colors import cm
def get_rgba(value, limits, cmap):
fac = (value-limits[0]) / (limits[1]-limits[0])
return cmap(fac)
cmap = cm.get_cmap('jet')
limits = [0,10]
value = 5.292
print(get_rgba(value, limits, cmap))