AVAssetReader无法从copyNextSampleBuffer(),Swift获取像素缓冲区

时间:2018-01-31 23:32:34

标签: swift avfoundation metal avassetreader cvpixelbuffer

我试图在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纹理?

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

1 个答案:

答案 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))