2014-09-28 86 views
5

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

我给出的关键代码如下,我真的很感激它,如果你能帮助找出为什么无法显示视频图像。非常感谢。

(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数据。开始于 “0×00 0×00 0×00 0×01 0x65 ........”

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

//构建cmsamplebuffer确定

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]; 
+0

Scythe42答案可能会解决你的问题。我也遇到了一些问题,使其工作。但最后我做到了。你应该[看看](http://stackoverflow.com/questions/25980070/how-to-use-avsamplebufferdisplaylayer-in-ios-8-for-rtp-h264-streams-with-gstream)。 – Zappel 2014-10-29 19:09:07

+0

也一样。有一个vail&ready CMSampleBuffer,但它不会显示在屏幕上...... :( – zaxy78 2017-11-01 14:15:19

回答

4

你NAL单元起始码0×00 0×00 0×01或0×00 0×00 0×00 0×01需要由长度头部被替换。

您在WWDC会议中已明确指出,附件B的开始代码需要由AVCC符合标题替换。您基本上正在从这里的附件B流格式转换MP4文件格式(当然的简化说明)。

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

无论你放进CMSampleBuffer里面什么都没问题,如果内容可以被解码,就没有理智检查,只要你满足了任意数据结合定时信息和参数集的必要参数。

基本上你输入的数据表示VCL NAL单元长度为1个字节。解码器没有得到完整的NAL单元,并在出现错误时解除。

还要确保在使用创建PPS/SPS没有添加长度副本的参数集并且附件B开始代码也被剥离时。

另外我建议不要使用AVSampleBufferDisplayLayer,但要经过一个VTDecompressionSession,因此您可以执行诸如颜色校正或像素着色器内所需的其他内容。

+2

注意NAL长度代码需要以big-endian字节顺序也很重要。 – Greg 2015-03-03 20:25:22

0

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