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 だったものを返します。
これは、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 ブラウザー ページのチャンクで構成されています。問題が最初の関数にあるのか、2 番目の関数にあるのかわかりません。CMSampleBufferGetImageBuffer からのゼロ以外の戻り値は励みになりますが、サイズと期間の 0 はそうではありません。
このスレッド ( CMSampleBufferRef のバッファー サイズ) を見つけました。これは、サイズと期間に 0 を表示しても問題ない可能性があることを示唆しているため、問題はメタル テクスチャへの変換にあるのでしょうか?
私が間違っていることは何ですか?
ありがとう!