视频编解码
视频编码
- 根据视频的宽高和编码格式创建编码器并设置编码器的一些属性
- 根据收到的CVPixelBufferRef 开始编码
- 在编码后的数据中分离出 sps&pps&nalu,
- 获取 sps&pps (Annex B或者 AVCC)
- Annex B 格式 : 0x00000001(4字节) + sps + 0x00000001(4字节) + pps + 0x00000001(4字节) + NALU 数据 + 0x00000001(4字节) + NALU 数据 + … + 0x00000001(4字节) + NALU 数据
- AVCC 格式 : dict[@“SampleDescriptionExtensionAtoms”][@“avcC”] + NALULen0(4字节) + NALU数据(NALULen0字节) + NALULen1(4字节) + NALU数据(NALULen1字节) + … + NALULenx(4字节) + NALU数据(NALULenx字节)
- 获取nalu
- 获取 sps&pps (Annex B或者 AVCC)
- 然后推流或者保存本地
- 完成后关闭推流器
[关键代码如下] :
// - 创建编码器
-(void)open{
/* 配置编码器, 这里并不是立刻开始编码, 只是配置编码器
1. 分配器 传 NULL
2. 分辨率的 width
3. 分辨率的 height
4. 编码类型 kCMVideoCodecType_H264
5. 编码规范 NULL
6. 源像素缓冲区 NULL
7. 压缩数据分配器 NULL
8. 编码完成的回调
9. self, 将 self 桥接过去
10. VTCompressionSessionRef &_vEnSession
*/
OSStatus status = VTCompressionSessionCreate(NULL, (int32_t)(self.videoConfig.pushStreamWidth), (int32_t)self.videoConfig.pushStreamHeight, kCMVideoCodecType_H264, NULL, NULL, NULL, vtCompressionSessionCallback, (__bridge void * _Nullable)(self), &_vEnSession);
/** 编码器创建成功 配置编码器的参数 */
if (status == noErr) {
// 设置参数
// ProfileLevel,h264的协议等级,不同的清晰度使用不同的ProfileLevel。 kVTProfileLevel_H264_Baseline_AutoLevel 表示舍弃 B 帧
VTSessionSetProperty(_vEnSession, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Main_AutoLevel);
// 设置码率 通常设置为 宽 * 高 * 3 * 4 * 8;
// int bitrate = width * height * 8 * 3 * 4;
// CFNumberRef bitrateRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &bitrateRef);
// VTSessionSetProperty(_vEnSession, kVTCompressionPropertyKey_AverageBitRate, bitrate);
VTSessionSetProperty(_vEnSession, kVTCompressionPropertyKey_AverageBitRate, (__bridge CFTypeRef)@(self.videoConfig.bitrate));
// 设置实时编码
VTSessionSetProperty(_vEnSession, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue);
// 关闭重排Frame,因为有了B帧(双向预测帧,根据前后的图像计算出本帧)后,编码顺序可能跟显示顺序不同。此参数可以关闭B帧。
VTSessionSetProperty(_vEnSession, kVTCompressionPropertyKey_AllowFrameReordering, kCFBooleanFalse);
// 关键帧最大间隔(GOP) 这里是 2s。
VTSessionSetProperty(_vEnSession, kVTCompressionPropertyKey_MaxKeyFrameInterval, (__bridge CFTypeRef)@(self.videoConfig.fps * 2));
// 关于B帧 P帧 和I帧,请参考:http://blog.csdn.net/abcjennifer/article/details/6577934
//编码器参数设置完毕,准备开始,随时来数据,随时编码
status = VTCompressionSessionPrepareToEncodeFrames(_vEnSession);
if (status != noErr) {
[self onErrorWithCode:AWEncoderErrorCodeVTSessionPrepareFailed des:@"硬编码vtsession prepare失败"];
}
}else{
[self onErrorWithCode:AWEncoderErrorCodeVTSessionCreateFailed des:@"硬编码vtsession创建失败"];
}
}
// - 编码数据
-(void)encodeSample(CMSampleBufferRef)sampleBuffer{
if (!_vEnSession) {
return NULL;
}
uint32_t ptsMs = self.manager