将H.264 I帧放到AVSampleBufferDisplayLayer但不显示视频图像

时间:2014-09-28 01:48:47

标签: iphone video ios8 h.264

在对WWDC2014,Session513进行详细审查后,我尝试在IOS8.0上编写我的应用程序,以解码并显示一个实时H.264流。首先,我成功构建了H264参数集。当我得到一个带有4位起始码的I帧时,就像" 0x00 0x00 0x00 0x01 0x65 ...",我将它放入CMblockBuffer。然后我使用预览CMBlockBuffer构建一个CMSampleBuffer。之后,我将CMSampleBuffer放入AVSampleBufferDisplayLayer。一切都很好(我检查了返回的值),但AVSampleBufferDisplayLayer没有显示任何视频图像。由于这些API对每个人来说都是新手,我无法找到任何可以解决此问题的机构。

我将按如下方式给出密码:如果您能帮助弄清楚为什么无法显示视频图像,我真的很感激。非常感谢。

(1)初始化AVSampleBufferDisplayLayer。 dsplayer是我的主视图控制器的objc实例。

    @property(nonatomic,strong)AVSampleBufferDisplayLayer *dspLayer;

if(!_dspLayer)
{
    _dspLayer = [[AVSampleBufferDisplayLayer alloc]init];
    [_dspLayer setFrame:CGRectMake(90,551,557,389)];
    _dspLayer.videoGravity = AVLayerVideoGravityResizeAspect;
   _dspLayer.backgroundColor = [UIColor grayColor].CGColor;
    CMTimebaseRef tmBase = nil;
    CMTimebaseCreateWithMasterClock(NULL,CMClockGetHostTimeClock(),&tmBase);
    _dspLayer.controlTimebase = tmBase;
    CMTimebaseSetTime(_dspLayer.controlTimebase, kCMTimeZero);
    CMTimebaseSetRate(_dspLayer.controlTimebase, 1.0);

     [self.view.layer addSublayer:_dspLayer];

}

(2)在另一个帖子中,我得到一个H.264 I帧。         //构造h.264参数设置确定

    CMVideoFormatDescriptionRef formatDesc;
    OSStatus formatCreateResult =
    CMVideoFormatDescriptionCreateFromH264ParameterSets(NULL, ppsNum+1, props, sizes, 4, &formatDesc);
    NSLog([NSString stringWithFormat:@"construct h264 param set:%ld",formatCreateResult]);

//构造cmBlockbuffer。 // databuf指向H.264数据。以" 0x00 0x00 0x00 0x01 0x65 ........"开头

    CMBlockBufferRef blockBufferOut = nil;
    CMBlockBufferCreateEmpty (0,0,kCMBlockBufferAlwaysCopyDataFlag, &blockBufferOut);
    CMBlockBufferAppendMemoryBlock(blockBufferOut,
                                    dataBuf,
                                    dataLen,
                                    NULL,
                                    NULL,
                                    0,
                                    dataLen,
                                    kCMBlockBufferAlwaysCopyDataFlag);

//构造cmsamplebuffer ok

    size_t sampleSizeArray[1] = {0};
    sampleSizeArray[0] = CMBlockBufferGetDataLength(blockBufferOut);
    CMSampleTiminginfo tmInfos[1] = {
        {CMTimeMake(5,1), CMTimeMake(5,1), CMTimeMake(5,1)}
    };
    CMSampleBufferRef sampBuf = nil;
    formatCreateResult = CMSampleBufferCreate(kCFAllocatorDefault,
                         blockBufferOut,
                         YES,
                         NULL,
                         NULL,
                         formatDesc,
                         1,
                         1,
                         tmInfos,
                         1,
                         sampleSizeArray,
                         &sampBuf);

//放到AVSampleBufferdisplayLayer,只有一帧。但我在视图中看不到任何视频帧

    if([self.dspLayer isReadyForMoreMediaData])
    {
    [self.dspLayer enqueueSampleBuffer:sampBuf];
    }
    [self.dspLayer setNeedsDisplay];

2 个答案:

答案 0 :(得分:5)

您的NAL单元起始码0x00 0x00 0x01或0x00 0x00 0x00 0x01需要用长度标头替换。

在WWDC会议中明确指出,附件B起始码需要由符合AVCC标准的lengh标头替换。您现在基本上可以从附件B流格式中复制到MP4文件格式(当然简化说明)。

创建参数集时,您的呼叫为“4”,因此您需要为VCL NAL单元添加前缀为4字节的前缀。这就是为什么你用AVCC格式指定它的长度标题可以更短。

无论你放在CMSampleBuffer中什么都没关系,没有完整性检查是否可以解码内容,只是你遇到了与定时信息和参数集结合的任意数据所需的参数。

基本上,您输入的数据表示VCL NAL单位长度为1个字节。解码器没有得到完整的NAL单元并且出错了。

同时确保在使用时创建参数集,使PPS / SPS没有添加长度,并且附件B起始码也被剥离。

另外,我建议不要使用AVSampleBufferDisplayLayer,而是通过VTDecompressionSession,这样你就可以做像色彩校正或像素着色器中需要的其他东西。

答案 1 :(得分:0)

最初使用DecompressionSessionDecode Frame可能是一个想法,因为这将为您提供有关解码成功的一些反馈。如果解码存在问题,AVSampleBufferDisplay图层不会告诉您它只是没有显示任何内容。如果需要的话,我可以给你一些代码来帮助解决这个问题,让我知道你是如何继续的,因为我正在尝试同样的事情:)