本例需求:使用H264, H265实现视频数据的编码并录制开始200帧存为文件.
原理:比如做直播功能,需要将客户端的视频数据传给服务器,如果分辨率过大如2K,4K则传输压力太大,所以需要对视频数据进行编码,传给服务器后再解码以实现大数据量的视频数据的传输,而利用硬件编码则可以极大限度减小CPU压力,
H264进行编码,iOS 11 之后,iPhone 7以上的设备可以支持新的编码器H265编码器,使得同等质量视频占用的存储空间更小。所以本例中可以使用两种方式实现视频数据的编码
最终效果如下 : h264
H264 编码
h265 :
H265编码
GitHub地址(附代码) : H264,H265Encode
简书地址 : H264,H265Encode
博客地址 : H264,H265Encode
掘金地址 : H264,H265Encode
实现方式:
-
H264 : H264是当前主流编码标准,以高压缩高质量和支持多种网络的流媒体传输著称
-
H265 :H264编码器的下一代,它的主要优点提供的压缩比高,相同质量的视频是H264的两倍。
1、.本文需要基本知识点
注意:可以先通过H264,H265编码器介绍, H.264 Data Structure了解预备知识。
1.1 软编与硬编概念
-
软编码:使用CPU进行编码。
-
硬编码:不使用CPU进行编码,使用显卡GPU,专用的DSP、FPGA、ASIC芯片等硬件进行编码。
-
比较
-
软编码:实现直接、简单,参数调整方便,升级易,但CPU负载重,性能较硬编码低,低码率下质量通常比硬编码要好一点。
-
性能高,低码率下通常质量低于软编码器,但部分产品在GPU硬件平台移植了优秀的软编码算法(如X264)的,质量基本等同于软编码。
-
苹果在iOS 8.0系统之前,没有开放系统的硬件编码解码功能,不过Mac OS系统一直有,被称为Video ToolBox的框架来处理硬件的编码和解码,终于在iOS 8.0后,苹果将该框架引入iOS系统
-
-
1.2 H265优点
-
压缩比高,在相同图片质量情况下,比JPEG高两倍
-
能增加如图片的深度信息,透明通道等辅助图片。
-
支持存放多张图片,类似相册和集合。(实现多重曝光的效果)
-
支持多张图片实现GIF和livePhoto的动画效果。
-
无类似JPEG的最大像素限制
-
支持透明像素
-
分块加载机制
-
支持缩略图
2、代码解析
2.1 实现流程
-
初始化相机参数,设置相机代理,这里就固定只有竖屏模式。
-
初始化编码器参数,并启动编码器
-
在编码成功的回调中从开始录制200帧(文件大小可自行修改)的视频,存到沙盒中,可以通过连接数据线到电脑从itunes中将文件(test0.asf)提取出来
2.2 编码器实现流程
-
创建编码器需要的session (h264, h265 或同时创建)
-
设置session属性,如实时编码,码率,fps, 编码的分辨率的宽高,相邻I帧的最大间隔等等
-
注意H265目前不支持码率的限制
-
-
当相机回调AVCaptureVideoDataOutputSampleBufferDelegate采集到一帧数据的时候则使用H264/H265编码器对每一帧数据进行编码。
-
若编码成功会触发回调,回调函数首先检测是否有I帧出现,如果有I帧出现则将sps,pps信息写入否则遍历NALU码流并将startCode替换成{0x00, 0x00, 0x00, 0x01}
2.3 主要方法解析
-
初始化编码器 首先选择使用哪种方式实现,在本例中可以设置[XDXHardwareEncoder getInstance].enableH264 = YES 或者 [XDXHardwareEncoder getInstance].enableH265 = YES,也可以同时设置,如果同时设置需要将其中一个回调函数中的writeFile的方法屏蔽掉,并且只有较新的iPhone(> iPhone8 稳定)才支持同时打开两个session。
判断当前设备是否支持H265编码,必须满足两个条件,一是iPhone 7 以上设备,二是版本大于iOS 11
if (@available(iOS 11.0, *)) {
BOOL hardwareDecodeSupported = VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC);
if (hardwareDecodeSupported) {
_deviceSupportH265 = YES;
NSLog(@"XDXHardwareEncoder : Support H265 Encode/Decode!");
}
}else {
_deviceSupportH265 = NO;
NSLog(@"XDXHardwareEncoder : Not support H265 Encode/Decode!");
}
系统已经提供VTIsHardwareDecodeSupported判断当前设备是否支持H265编码
初始化编码器操作
- (void)prepareForEncode {
if(self.width == 0 || self.height == 0) {
NSLog(@"XDXHardwareEncoder : VTSession need with and height for init,with = %d,height = %d",self.width, self.height);
return;
}
if(g_isSupportRealTimeEncoder) NSLog(@"XDXHardwareEncoder : Device processor is 64 bit");
else NSLog(@"XDXHardwareEncoder : Device processor is not 64 bit");
NSLog(@"XDXHardwareEncoder : Current h264 open state : %d, h265 open state : %d",self.enableH264, self.enableH265);
OSStatus h264Status,h265Status;
BOOL isRestart = NO;
if (self.enableH264) {
if (h264CompressionSession != NULL) {
NSLog(@"XDXHardwareEncoder : H264 session not NULL");
return;
}
[m_h264_lock lock];
NSLog(@"XDXHardwareEncoder : Prepare H264 hardware encoder");
//[self.delegate willEncoderStart];
self.h264ErrCount = 0;
h264Status = VTCompressionSessionCreate(NULL, self.width, self.height, kCMVideoCodecType_H264, NULL, NULL, NULL, vtCallBack,(__bridge void *)self, &h264CompressionSession);
if (h264Status != noErr) {
self.h265ErrCount++;
NSLog(@"XDXHardwareEncoder : H264 VTCompressionSessionCreate Failed, status = %d",h264Status);