解码H264视频时出现Error -8971错误,使用VideoToolkit API无法创建VTDecompressionSession。

11

我正在尝试使用硬件支持的视频工具包解码器编写视频解码器。但是,如果我像下面发布的示例那样尝试初始化解码会话,调用VTDecompressionSessionCreate时会出现错误-8971。有人可以告诉我在这里做错了什么吗?

谢谢和问候,

Oliver

OSStatus status;

int tmpWidth = sps.EncodedWidth();
int tmpHeight = sps.EncodedHeight();
NSLog(@"Got new Width and Height from SPS - %dx%d", tmpWidth, tmpHeight);

const VTDecompressionOutputCallbackRecord callback = { ReceivedDecompressedFrame, self };
status = CMVideoFormatDescriptionCreate(NULL,
                                       kCMVideoCodecType_H264,
                                       tmpWidth,
                                       tmpHeight,
                                       NULL,
                                       &decoderFormatDescription);

if (status == noErr)
{
    // Set the pixel attributes for the destination buffer
    CFMutableDictionaryRef destinationPixelBufferAttributes = CFDictionaryCreateMutable(
                                                                 NULL, // CFAllocatorRef allocator
                                                                 0,    // CFIndex capacity
                                                                 &kCFTypeDictionaryKeyCallBacks, 
                                                                 &kCFTypeDictionaryValueCallBacks);

    SInt32 destinationPixelType = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
    CFDictionarySetValue(destinationPixelBufferAttributes,kCVPixelBufferPixelFormatTypeKey, CFNumberCreate(NULL, kCFNumberSInt32Type, &destinationPixelType));
    CFDictionarySetValue(destinationPixelBufferAttributes,kCVPixelBufferWidthKey, CFNumberCreate(NULL, kCFNumberSInt32Type, &tmpWidth));
    CFDictionarySetValue(destinationPixelBufferAttributes, kCVPixelBufferHeightKey, CFNumberCreate(NULL, kCFNumberSInt32Type, &tmpHeight));
    CFDictionarySetValue(destinationPixelBufferAttributes, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue);

    // Set the Decoder Parameters
    CFMutableDictionaryRef decoderParameters = CFDictionaryCreateMutable(
                                                        NULL, // CFAllocatorRef allocator
                                                        0,    // CFIndex capacity
                                                        &kCFTypeDictionaryKeyCallBacks,
                                                        &kCFTypeDictionaryValueCallBacks);

    CFDictionarySetValue(decoderParameters,kVTDecompressionPropertyKey_RealTime, kCFBooleanTrue);

    // Create the decompression session
    // Throws Error -8971 (codecExtensionNotFoundErr)
    status = VTDecompressionSessionCreate(NULL, decoderFormatDescription, decoderParameters, destinationPixelBufferAttributes, &callback, &decoderDecompressionSession);

    // release the dictionaries
    CFRelease(destinationPixelBufferAttributes);
    CFRelease(decoderParameters);

    // Check the Status
    if(status != noErr)
    {
        NSLog(@"Error %d while creating Video Decompression Session.", (int)status);
        continue;
    }
}
else
{
    NSLog(@"Error %d while creating Video Format Descripttion.", (int)status);
    continue;
}

2
现在我正在通过CMVideoFormatDescriptionCreateFromH264ParameterSets直接向解码器提供我的SPS和PPS,而不是使用CMVideoFormatDescriptionCreate。这导致VTDecompressionSessionCreate不再出错,但VTDecompressionSessionDecodeFrame现在会抛出错误-12911(kVTVideoDecoderMalfunctionErr),回调会得到错误-12909(kVTVideoDecoderBadDataErr)。这是一个错误吗?应该报告还是我做错了什么? - lowtraxx
在正确解析NAL长度后,我的解码器现在可以工作了,但是生成的CVImageBufferRef没有平面,而'CVPixelBufferGetPixelFormatType'返回'kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange'。如果我执行'CVPixelBufferGetBaseAddressOfPlane(imageBuffer,0)',它会返回NULL。 - lowtraxx
我忘记在调用CVPixelBufferGetBaseAddressOfPlane时添加CVPixelBufferLockBaseAddress/CVPixelBufferUnlockBaseAddress,现在它可以无错误解码。现在唯一剩下的问题是渲染器不会渲染数据,但这不是本问题的范围,所以我将自己回答它。 - lowtraxx
2
lowtraxx,我遇到了kVTVideoDecoderBadDataErr错误。你说的“在正确解析NAL长度后进行更正”是什么意思? - rjkaplan
嘿@rjkaplan,我也遇到了同样的错误。你找到答案了吗?由于某种原因,这只发生在Mac OSX中。 - Oz Shabat
3个回答

8

我也遇到了 kVTVideoDecoderBadDataErr。在我的情况下,我改变了头部 0x00000001 的大小,而这个头部的大小包括了NAL数据包的4个字节,这就是问题所在。我将大小更改为不包括这4个字节 (frame_size = sizeof(NAL) - 4)。这个大小应该使用大端编码。


6
您需要根据您的SPS和PPS创建CMFormatDescriptionRef,例如:
CMFormatDescriptionRef decoderFormatDescription;
const uint8_t* const parameterSetPointers[2] = { (const uint8_t*)[currentSps bytes], (const uint8_t*)[currentPps bytes] };
const size_t parameterSetSizes[2] = { [currentSps length], [currentPps length] };
status = CMVideoFormatDescriptionCreateFromH264ParameterSets(NULL,
                                                             2,
                                                             parameterSetPointers,
                                                             parameterSetSizes,
                                                             4,
                                                             &decoderFormatDescription);

另外,如果您的视频数据以Annex-B格式获取,则需要删除起始代码并将其替换为4字节大小信息,以便解码器将其识别为avcc格式(这就是传递给CMVideoFormatDescriptionCreateFromH264ParameterSets的第5个参数的作用)。


2
请问您能否解释一下如何获得currentSps?我很难找到获取parameterSetPointers和parameterSetSizes参数的方法。 - Joride
1
如果您的设备在H264 RTP流中未发送SPS和PPS怎么办?我一直在尝试使用具有H264和正确分辨率的CMVideoFormatDescriptionCreate,但是我通过解码器回调获取到了-8971。 - DrMickeyLauer

0

@Joride

请参考http://www.szatmary.org/blog/25

该文章解释了NALU中每个缓冲区的头(第一个)字节描述了缓冲区的类型。您需要屏蔽这些位并将它们与提供的表进行比较。请注意有关位字段的注释。您需要使用0x1f掩码字节以获取类型值。


1
该文章的新URL为https://dev59.com/qGAf5IYBdhLWcg3wOQi2。 - tmm1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接