7

我已经构建了一些代码来逐帧处理 OSX 上的视频文件。以下是代码的摘录,它构建好、打开文件、找到视频轨道(仅轨道)并开始毫无问题地读取 CMSampleBuffers。但是,当我尝试提取像素缓冲区帧时,我获得的每个 CMSampleBufferRef 都返回 NULL。iOS 文档中没有说明为什么我可以期望 NULL 返回值或我可以期望如何解决该问题。无论捕获源或编解码器如何,我测试过的所有视频都会发生这种情况。

非常感谢任何帮助。

NSString *assetInPath = @"/Users/Dave/Movies/movie.mp4";
NSURL *assetInUrl = [NSURL fileURLWithPath:assetInPath];
AVAsset *assetIn = [AVAsset assetWithURL:assetInUrl];

NSError *error;
AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:assetIn error:&error];
AVAssetTrack *track = [assetIn.tracks objectAtIndex:0];
AVAssetReaderOutput *assetReaderOutput = [[AVAssetReaderTrackOutput alloc]
                                              initWithTrack:track
                                              outputSettings:nil];
[assetReader addOutput:assetReaderOutput];

// Start reading
[assetReader startReading];

CMSampleBufferRef sampleBuffer;
do {
       sampleBuffer = [assetReaderOutput copyNextSampleBuffer];

       /**
        ** At this point, sampleBuffer is non-null, has all appropriate attributes to indicate that
        ** it's a video frame, 320x240 or whatever and looks perfectly fine. But the next
        ** line always returns NULL without logging any obvious error message
        **/

       CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

       if( pixelBuffer != NULL ) {
           size_t width = CVPixelBufferGetWidth(pixelBuffer);
           size_t height = CVPixelBufferGetHeight(pixelBuffer);
           CVPixelBufferLockBaseAddress(pixelBuffer, 0);
           ...
           other processing removed here for clarity
        }
} while( ... );

需要明确的是,我已经删除了所有错误检查代码,但该代码中没有指出任何问题。即 AVAssetReader 正在读取,CMSampleBufferRef 看起来不错等。

4

3 回答 3

17

outputSettings创建AVAssetReaderTrackOutput. _ 我在指定“nil”以便在调用时接收视频轨道的原始像素格式时遇到了您的问题copyNextSampleBuffer。在我的应用程序中,为了性能起见,我想确保在调用时没有发生转换copyNextSampleBuffer,如果这对您来说不是一个大问题,请在输出设置中指定像素格式。

以下是 Apple 基于硬件能力推荐的像素格式:

kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange kCVPixelFormatType_420YpCbCr8BiPlanarFullRange

于 2014-09-15T16:32:57.130 回答
5

因为您没有提供任何 outputSettings,所以您被迫使用框架中包含的原始数据。

您必须使用从样本缓冲区获取块缓冲区CMSampleBufferGetDataBuffer(sampleBuffer),之后您需要使用获取块缓冲区的实际位置

size_t blockBufferLength; char *blockBufferPointer; CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &blockBufferLength, &blockBufferPointer);

*blockBufferPointer使用所需编解码器的帧头信息查看和解码字节。

于 2015-12-07T22:11:24.400 回答
2

FWIW:这是官方文档对 CMSampleBufferGetImageBuffer 的返回值所说的:

“结果是媒体数据的 CVImageBuffer。如果 CMSampleBuffer 不包含 CVImageBuffer,或者如果 CMSampleBuffer 包含 CMBlockBuffer,或者存在其他错误,则结果将为 NULL。”

另请注意,调用者不拥有从 CMSampleBufferGetImageBuffer 返回的 dataBuffer,如果调用者需要维护对它的引用,则必须明确保留它。

希望这些信息有所帮助。

于 2013-06-01T14:32:31.297 回答