2

我正在尝试使用 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 可能不是问题,所以问题可能在于转换为金属纹理?

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

4

1 回答 1

0

将 videoReaderSetting 放在 AVAssetReaderTrackOutput 中。

let videoReaderSettings : [String : Int] = [kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_32BGRA)]

let output = AVAssetReaderTrackOutput(track:track, outputSettings: videoReaderSettings)
于 2020-07-03T04:51:35.433 回答