了解h264的桢结构
起始标志:4个字节 0000 0001
起始标志之后的第一个字节的低5位表示nalu_type = buf[4]&0x1f
5为I桢、7为sps、8为pps、1为p桢
构成顺序为sps+pps+I+p+p+…+p
1 | 00000001674d001f95a814016e8400000fa00001d4c0100000000168ee3c800000000106e501a8800000000165b800000f9bf0bffeced6afe1b11a32d639e05db83b1341baefb9ded91702fbd91a7c6c4700bfbcf1ae33ebce56e84c01f0d6b06c77669fc13c4903f755227 |
框架
framework VideoToolbox.h
数据结构
- CMSampleBuffer:存放编解码前后的视频图像的容器数据结构。
- CMBlockBuffer:编码后,结果图像的数据结构。
- CVPixelBuffer:编码前和解码后的图像数据结构。
- VTDecompressionRef:session
- CMVideoFormatDescriptionRef:视频信息描述
关系如下图
方法
解码前期需要以下三个函数
CMVideoFormatDescriptionCreateFromH264ParameterSets设置描述信息VTDecompressionSessionCreate创建解码 sessionCMBlockBufferCreateWithMemoryBlock(uint_8 *) -> CMBlockBufferCMSampleBufferCreateReadyCMBlockBuffer -> CMSampleBufferVTDecompressionSessionDecodeFrame解码 CMSampleBuffer -> CVPixelBufferVTDecompressionSessionInvalidate销毁解码 session
解码流程
- 设置
CMVideoFormatDescriptionRef _description视频信息描述信息:
通过CMVideoFormatDescriptionCreateFromH264ParameterSets方法,带入sps、pps、&_description,可以提取出sps、pps中携带的视频属性信息。
- 创建 decode session
1
2
3
4
5OSStatus status = VTDecompressionSessionCreate(kCFAllocatorDefault,
decoderFormatDescription,
NULL, attrs,
&callBackRecord,
&deocderSession);
decoderFormatDescription即为description 1中已经设置OK,
这个Session的作用是把callBackRecord函数、description描述信息连接起来,callBackRecord 是用来指定回调函数的,解码器支持异步模式,解码后会调用这里的回调函数
- 创建CMSampleBuffer
先用CMBlockBufferCreateWithMemoryBlock从H.264数据创建一个CMBlockBufferRef实例。
1
2
3
4
5
6
7
8
9CMBlockBufferCreateWithMemoryBlock(NULL,
(void *)frame,
frameSize,
kCFAllocatorNull,
NULL,
0,
frameSize,
FALSE,
&blockBuffer);然后用 CMSampleBufferCreateReady创建CMSampleBufferRef实例。当然这个CMSampleBuffer按照最上面的关系,应该是进行了一次封装,将blockbuffer封装进samplebuffer。以备之后加码使用。
1
2
3
4
5status = CMSampleBufferCreateReady(kCFAllocatorDefault,
blockBuffer,
_decoderFormatDescription ,
1, 0, NULL, 1, sampleSizeArray,
&sampleBuffer);
这样可以得到一个CMSampleBufferRef的实例。
- 解码
1
2
3
4
5
6
7
8
9VTDecodeFrameFlags flags = 0;
//kVTDecodeFrame_EnableTemporalProcessing | kVTDecodeFrame_EnableAsynchronousDecompression;
VTDecodeInfoFlags flagOut = 0;
CVPixelBufferRef outputPixelBuffer = NULL;
OSStatus decodeStatus = VTDecompressionSessionDecodeFrame(deocderSession,
sampleBuffer,
flags,
&outputPixelBuffer,
&flagOut);
解码成功之后,outputPixelBuffer里就是一帧 NV12格式的YUV图像了。
- 显示
配合Apple提供的Layer类,”AAPLEAGLLayer.h”,在controller中声明一个该类的实例_glLayer,在解码的回调中将解码出的CVPixelBuffer赋值给 _glLayer.pixelBuffer属性即可显示出来。
口答流程:
- 主要用到了四个数据结构和六个方法,列在纸上,说到哪里指到哪里;四个数据结构都是干什么用的,以及它们之间的关系。
- 画一个整体的流程图
-

- 对于拿到的NSData数据我们要如何处理,首先要知道h264的数据格式,sps、pps、i、p的排列以及作用
- sps,pps设置CMVideoFormatDescriptionRef 视频信息描述,通过一个H264ParameterSets函数
- 刚刚提到四个函数,用了一个,剩下的三个怎么用?首先我们要调用VTDecompressionSessionCreate创建session,这就用到了刚刚的description,同时这个过程也把解码后的callBack声明好了
- 接下来要用VTDecompressionSessionDecodeFrame进行解码了,但是传入的参数应该时CMSampleBuffer类型,所以在这之前我们需要把(uint8_t *)类型的参数进行转化。
- 这就要用到
CMBlockBufferCreateWithMemoryBlock和CMSampleBufferCreateReady这个两个函数了,作用我的理解就是数据结构的转化uint_8* -> CMBlockBuffer 、CMBlockBuffer -> CMSampleBuffer - 终于到了解码过程,从解码函数的参数我们可以看到,把CMSampleBuffer、CVPixelBuffer和session联系起来了,而session又与description、callBack关联,输出的CVPixelBuffer直接添加到callback中,进行显示就可以了。


