FFmpeg与音视频流媒体工程开发相关

H264编解码(二) —— ios中的H264硬编解码的实现

2017-12-24  本文已影响193人  刀客传奇

版本记录

版本号 时间
V1.0 2017.12.23

前言

对于做过视频的开发者,大家应该对H264编码都不陌生,接下来这几篇就详细的解析一下H264编码方面的相关知识。感兴趣的可以看这几篇文章。
1. H264编码(一) —— 基本概览

视频H264编解码数据结构

下面我们看一下H264编码前后数据结构,如下图所示。

下图为H264解码前后数据结构示意图,这里有几个对象需要说明一下。

从上图中可以看出来:

具体上面几个对象怎么在代码中使用,后续会加上使用方法的Demo。


硬编码和软编码优缺点

利用CPU做视频的编码和解码,称为软编软解。该方法比较通用,但是占用CPU资源,编解码效率不高。

一般系统都会提供GPU或者专用处理器来对视频流进行编解码,也就是硬件编码和解码。苹果在iOS 8.0系统之前,没有开放系统的硬件编码解码功能,不过Mac OS系统一直有,被称为Video ToolBox的框架来处理硬件的编码和解码,终于在iOS 8.0后,苹果将该框架引入iOS系统。

硬编码具有很大的优势,它不像软编码大量占用CPU资源。可以更好的利用GPU以及专门的视频编解码芯片的高性能,可以实现很好的实时性。其实,对于VFoundation也使用硬件对视频进行硬件编解码,但是编码后直接写入文件,解码后就直接显示了。而使用Video Toolbox框架可以得到编码后的帧结构,也可以得到解码后的原始图像,因此具有更大的灵活性做一些视频图像处理。也就是说使用Video Toolbox框架更加灵活,方便进一步进行视频处理。


硬解码

硬解码其实就是从服务端下载视频数据,但是是编码后的,在客户端呈现出来视频数据之前,需要进行解码,然后才可以拿出来图像像素数据进行显示。下面我们先看一下硬编码相关原理及理论,先看一张图。

1. 将H264码流转换为解码前CMSampleBuffer对象

由前面的内容我们知道,解码前的CMSampleBuffer对象,包括CMTime、CMVideoFormatDesc、CMBlockBuffer等,我们解码的任务就是从H264码流里面提取上面三处的信息,合成解码后的CMSampleBuffer对象,提供给硬解码接口进行解码工作。

H264码流由NALU单元组成,NALU单元包含视频图像数据CMBlockBuffer和H264的参数信息则可以组合成FormatDesc,具体参数信息包含SPS(Sequence Parameter Set)PPS(Picture Parameter Set),如下图所示为H264的码流结构。

还可以看下面这个示意图

H264码流结构

下面我们就看一下这个解析过程。

//sps
_spsSize =format.getCsd_0_size()-4;_sps = (uint8_t *)malloc(_spsSize);memcpy(_sps,format.getCsd_0()+4, _spsSize);

//pps
_ppsSize =format.getCsd_1_size()-4;_pps = (uint8_t *)malloc(_ppsSize);memcpy(_pps,format.getCsd_1()+4, _ppsSize);
CMBlockBufferRef blockBuffer=NULL;
CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,                                  
(void*)frame.bytes,                                   
 frame.length,                                  
kCFAllocatorNull,NULL,0, frame.length,0,&blockBuffer);

根据前面产生的CMVideoFormatDescriptionRef、CMBlockBufferRef和可选的时间信息,使用函数CMSampleBufferCreate得到CMSampleBuffer这个待解码的原始数据。

CMSampleBufferRef sampleBuffer =NULL;
CMSampleBufferCreateReady(kCFAllocatorDefault,                          
blockBuffer,                          
_decoderFormatDescription,1,0,NULL,1, sampleSizeArray,                         
 &sampleBuffer);

具体如下所示,为H264解码数据转换图。

H264码流转换CMSampleBuffer示意图

2. 硬解码后的图像显示

下面我们就看一下硬解码后的图像显示,具体的显示方式有两种:

通过系统提供的AVSampleBufferDisplayLayer来解码并显示

AVSampleBufferDisplayLayer是苹果提供的一个专门显示解码后的H264数据的显示层,它是CALayer的子类,因此使用方式和其它CALayer类似。使用方法enqueueSampleBuffer :进行显示该层内置了硬件解码功能,将原始的CMSampleBuffer解码后的图像直接显示在屏幕上面,如下图所示。

AVSampleBufferDisplayLayer显示硬解码后的图像

下面看一下实例代码

CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer,YES);
CFMutableDictionaryRef dict = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(attachments,0);
CFDictionarySetValue(dict, kCMSampleAttachmentKey_DisplayImmediately, kCFBooleanTrue);
if(status == kCMBlockBufferNoErr) {

if([_avslayer isReadyForMoreMediaData]) {dispatch_sync(dispatch_get_main_queue(),^{        

    [_avslayer enqueueSampleBuffer:sampleBuffer];      

    });   
  }    

CFRelease(sampleBuffer);
}

下面看一下这个显示方式的解码流程。

通过VTDecompression接口来,将CMSampleBuffer解码成图像,将图像通过UIImageView或者OpenGL上显示

VTDecompressionSessionRef _deocderSession;
VTDecompressionSessionCreate(kCFAllocatorDefault,                           
 _decoderFormatDescription,NULL, attrs,                            
&callBackRecord,                           
 &_deocderSession);
CIImage *ciImage= [CIImage imageWithCVPixelBuffer:outputPixelBuffer];
UIImage *uiImage= [UIImage imageWithCIImage:ciImage];
// 使用VTDecompressionSessionDecodeFrame接口解码成CVPixelBufferRef数据:

CVPixelBufferRef outputPixelBuffer=NULL;
VTDecompressionSessionDecodeFrame(_deocderSession,
sampleBuffer,
flags
&outputPixelBuffer,
&flagOut);
VTDecompression硬解码过程示意图

下面看一下这种解码方式和显示流程。

下面看一下这两种解码方式的优缺点。


硬编码

硬编码我们也经常见,比如说,我们直播录制视频,就要先通过摄像头采集图像,然后进行硬编码,最后将硬编码后的数据组合成H264码流通过网络传播。

1. 视频采集

这个硬件设备就是摄像头了,通过AVFoundation框架中的AVCaptureSession类来采集图像,并设定好input和output,同时设定deleagte代理和输出队列,在代理delegate方法中,处理采集好的图像。图像输出的格式是未编码的CMSampleBuffer形式。

2. 使用VTCompressionSession进行硬编码

获取采集后的图像,我们需要使用VTCompressionSession进行硬编码。

硬编码处理流程示意图

参考文章

1. iOS8系统H264视频硬件编解码说明
2. iOS-H264 硬解码
3. 一轮圆月作者关于硬编解码的GitHub Demo

后记

未完,待续~~~

上一篇下一篇

猜你喜欢

热点阅读