直播二:iOS中硬编码(VideoToolBox)
硬编码相对于软编码来说,使用非CPU进行编码,如显卡GPU、专用的DSP、FPGA、ASIC芯片等,性能高,对CPU没有压力,但是对其他硬件要求较高(如GPU等)。
在iOS8之后,苹果开放了接口,并且封装了VideoToolBox&AudioToolbox两个框架,分别用于对视频&音频进行硬编码,音频编码放在后面做总结,这次主要总结VideoToolBox。
Demo的Github地址:https://github.com/wzpziyi1/HardCoding-For-iOS
1、相关基础数据结构:
CVPixelBuffer:编码前和解码后的图像数据结构。
CMTime、CMClock和CMTimebase:时间戳相关。时间以64-bit/32-bit的形式出现。
CMBlockBuffer:编码后,结果图像的数据结构。
CMVideoFormatDescription:图像存储方式,编解码器等格式描述。
CMSampleBuffer:存放编解码前后的视频图像的容器数据结构。
// 编码完成回调
void finishCompressH264Callback(void *outputCallbackRefCon, void *sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer)
{
if (status != noErr) return; //根据传入的参数获取对象
ZYVideoEncoder *encoder = (__bridge ZYVideoEncoder *)(outputCallbackRefCon); //判断是否是关键帧
bool isKeyFrame = !CFDictionaryContainsKey( (CFArrayGetValueAtIndex(CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true), )), kCMSampleAttachmentKey_NotSync); //如果是关键帧,获取sps & pps数据
if (isKeyFrame)
{
CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer); //获取sps信息
size_t sparameterSetSize, sparameterSetCount;
const uint8_t *sparameterSet;
CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, , &sparameterSet, &sparameterSetSize, &sparameterSetCount, ); // 获取PPS信息
size_t pparameterSetSize, pparameterSetCount;
const uint8_t *pparameterSet;
CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, , &pparameterSet, &pparameterSetSize, &pparameterSetCount, ); // 装sps/pps转成NSData,以方便写入文件
NSData *sps = [NSData dataWithBytes:sparameterSet length:sparameterSetSize];
NSData *pps = [NSData dataWithBytes:pparameterSet length:pparameterSetSize]; // 写入文件
[encoder gotSpsPps:sps pps:pps];
} //获取数据块
CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
size_t length, totalLength;
char *dataPointer;
OSStatus statusCodeRet = CMBlockBufferGetDataPointer(dataBuffer, , &length, &totalLength, &dataPointer); if (statusCodeRet == noErr)
{
size_t bufferOffset = ;
// 返回的nalu数据前四个字节不是0001的startcode,而是大端模式的帧长度length
static const int AVCCHeaderLength = ; //循环获取nalu数据
while (bufferOffset < totalLength - AVCCHeaderLength)
{
uint32_t NALUnitLength = ; //读取NAL单元长度
memcpy(&NALUnitLength, dataPointer + bufferOffset, AVCCHeaderLength); // 从大端转系统端
NALUnitLength = CFSwapInt32BigToHost(NALUnitLength); NSData* data = [[NSData alloc] initWithBytes:(dataPointer + bufferOffset + AVCCHeaderLength) length:NALUnitLength];
[encoder gotEncodedData:data isKeyFrame:isKeyFrame]; // 移动到写一个块,转成NALU单元
bufferOffset += AVCCHeaderLength + NALUnitLength;
} }
}
所需要的信息都可以从CMSampleBufferRef中得到。
2、NAL(网络提取层)代码讲解
直播一中提到了NALU概念上的封装,下面是代码部分:
代码B:
- (void)gotSpsPps:(NSData*)sps pps:(NSData*)pps
{
// 拼接NALU的header
const char bytes[] = "\x00\x00\x00\x01";
size_t length = (sizeof bytes) - 1;
NSData *ByteHeader = [NSData dataWithBytes:bytes length:length]; // 将NALU的头&NALU的体写入文件
[self.fileHandle writeData:ByteHeader];
[self.fileHandle writeData:sps];
[self.fileHandle writeData:ByteHeader];
[self.fileHandle writeData:pps]; } - (void)gotEncodedData:(NSData*)data isKeyFrame:(BOOL)isKeyFrame
{
NSLog(@"gotEncodedData %d", (int)[data length]);
if (self.fileHandle != NULL)
{
const char bytes[] = "\x00\x00\x00\x01";
size_t length = (sizeof bytes) - 1; //string literals have implicit trailing '\0'
NSData *ByteHeader = [NSData dataWithBytes:bytes length:length];
[self.fileHandle writeData:ByteHeader];
[self.fileHandle writeData:data];
}
}
结合这张图片:
一个GOP序列,最前面是sps和pps,它们单独被封装成两个NALU单元,一个NALU单元包含header和具体数据,NALU单元header序列固定为00 00 00 01。那么得到一帧画面时,需要判断该帧是不是I帧,如果是,那么取出sps和pps,再是相关帧的提取写入。(具体参考代码A)。
3、VTCompressionSession进行硬编码
a、给出width、height
b、使用VTCompressionSessionCreate创建compressionSession,并设置使用H264进行编码,相关type是kCMVideoCodecType_H264
c、b中还需要设置回调函数finishCompressH264Callback,需要在回调函数里面取出编码后的GOP、sps、pps等数据。
d、设置属性为实时编码,直播必然是实时输出。
e、设置期望帧数,每秒多少帧,一般都是30帧以上,以免画面卡顿
f、设置码率(码率: 编码效率, 码率越高,则画面越清晰, 如果码率较低会引起马赛克 --> 码率高有利于还原原始画面,但是也不利于传输)
g、设置关键帧间隔(也就是GOP间隔)
h、设置结束,准备编码
代码:
- (void)setupVideoSession
{
//用于记录当前是第几帧数据
self.frameID = ; //录制视频的宽高
int width = [UIScreen mainScreen].bounds.size.width;
int height = [UIScreen mainScreen].bounds.size.height; // 创建CompressionSession对象,该对象用于对画面进行编码
// kCMVideoCodecType_H264 : 表示使用h.264进行编码
// finishCompressH264Callback : 当一次编码结束会在该函数进行回调,可以在该函数中将数据,写入文件中
//传入的self,就是finishCompressH264Callback回调函数里面的outputCallbackRefCon,通过bridge就可以取出此self
VTCompressionSessionCreate(NULL, width, height, kCMVideoCodecType_H264, NULL, NULL, NULL, finishCompressH264Callback, (__bridge void * _Nullable)(self), &_compressionSession); //设置实时编码,直播必然是实时输出
VTSessionSetProperty(_compressionSession, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue); //设置期望帧数,每秒多少帧,一般都是30帧以上,以免画面卡顿
int fps = ;
CFNumberRef fpsRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &fps);
VTSessionSetProperty(_compressionSession, kVTCompressionPropertyKey_ExpectedFrameRate, fpsRef); //设置码率(码率: 编码效率, 码率越高,则画面越清晰, 如果码率较低会引起马赛克 --> 码率高有利于还原原始画面,但是也不利于传输)
int bitRate = * ;
CFNumberRef rateRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bitRate);
VTSessionSetProperty(_compressionSession, kVTCompressionPropertyKey_AverageBitRate, rateRef);
NSArray *limit = @[@(bitRate * 1.5/), @()];
VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_DataRateLimits, (__bridge CFArrayRef)limit); //设置关键帧间隔(也就是GOP间隔)
//这里设置与上面的fps一致,意味着每间隔30帧开始一个新的GOF序列,也就是每隔间隔1s生成新的GOF序列
//因为上面设置的是,一秒30帧
int frameInterval = ;
CFNumberRef intervalRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &frameInterval);
VTSessionSetProperty(_compressionSession, kVTCompressionPropertyKey_MaxKeyFrameInterval, intervalRef); //设置结束,准备编码
VTCompressionSessionPrepareToEncodeFrames(_compressionSession);
}
码率:
初始化后通过VTSessionSetProperty
设置对象属性
编码方式:H.264编码
帧率:每秒钟多少帧画面
码率:单位时间内保存的数据量
关键帧(GOPsize)间隔:多少帧为一个GOP
参数参考:
直播二:iOS中硬编码(VideoToolBox)的更多相关文章
- iOS视频硬编码技术
iOS视频硬编码技术 一.iOS视频采集硬编码 基本原理 硬编码 & 软编码 硬编码:通过系统自带的Camera录制视频,实际上调用的是底层的高清编码硬件模块,即显卡,不使用CPU,速度快 软 ...
- IOS中的编码规范
1.指导原则 [原则1-]首先是为人编写程序,其次才是计算机. 说明:这是软件开发的基本要点,软件的生命周期贯穿产品的开发.测试.生产.用户使用.版本升级和后期维护等长期过程,只有易读.易维护的软件代 ...
- ios中base64编码
参考文章:其中文章的:http://blog.csdn.net/ztp800201/article/details/9470065 下载包 其中 包括GTMBase包下载地址 http://pan.b ...
- maven构建项目时硬编码中文乱码问题解决
场景:1. 项目采用maven作为构建工具.2. 前端页面为jsp,由前端团队独立完成,添加编码配置:<%@ page contentType="text/html;charset=u ...
- 01:***VideoToolbox硬编码H.264
最近接触了一些视频流H264的编解码知识,之前项目使用的是FFMpeg多媒体库,利用CPU做视频的编码和解码,俗称为软编软解.该方法比较通用,但是占用CPU资源,编解码效率不高.一般系统都会提供GPU ...
- 使用VideoToolbox硬编码H.264<转>
文/落影loyinglin(简书作者)原文链接:http://www.jianshu.com/p/37784e363b8a著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. ======= ...
- iOS中集成ijkplayer视频直播框架
ijkplayer 是一款做视频直播的框架, 基于ffmpeg, 支持 Android 和 iOS, 网上也有很多集成说明, 但是个人觉得还是不够详细, 在这里详细的讲一下在 iOS 中如何集成ijk ...
- 使用configuration配置结束在quartz.net中使用硬编码Job,Trigger任务提高灵活性
经常在项目中遇到定时任务的时候,通常第一个想到的是Timer定时器,但是这玩意功能太弱鸡,实际上通常采用的是专业化的第三方调度框架,比如说 Quartz,它具有功能强大和应用的灵活性,我想使用过的人都 ...
- 直播软件开发关于Android、iOS中的视频采集步骤
很多人对直播软件开发还是抱有想法的,但是在这个资本冷静的市场下,直播平台该怎么玩,在直播软件开发过程中哪些功能是必须具备的,这都是值得关注的话题.今天我们给大家分享一份详细的直播软件开发关于Andro ...
随机推荐
- 对Numpy广播操作的理解
1.首先检查两个矩阵维数是否相同,若不同,对维数少的补一.注意这里的维数不是指n行d列中的n和d的值,对于这种情况维数就是2.若一个两维的矩阵(n,d)和一个一维的数组(m,)相乘,补一操作就是将那个 ...
- Picture
Picture Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submi ...
- 哈尔滨理工大学第六届程序设计团队 I-Team
/* 以前做过一个插队的题,这个类似从后往前操作 */ #include <iostream> #include <stdio.h> #include <algorith ...
- Tomcat初应用
Tomcat初应用 这里我们自己建立一个html的web资源,然后在tomcat里进行配置,使我们可以通过服务器在浏览器里打开. 第一步:新建html文件,在里面随便输入几个字符串如:新建txt文件- ...
- 一个“”字引发的痛苦经历
(一篇老文章,还有点价值,特意整理了一下.由于涉及客户项目,已经进行了脱敏处理) 1 写在前面的话 虽然这个问题是有解决方案的,但我不建议大家提供给客户,理由见此. 2 问题描述 2010.10.12 ...
- 学习C++ 模板类
#include<iostream>#include<typeinfo>#include<cstring> using namespace std; class A ...
- Unity中的Mono & Linux上编译Mono的流程
前段时间编译了一下Unity的Mono,看了很多相关的文章,也遇到很多新坑.所以来总结一下,加深自己对Mono的理解 为什么Unity可以跨平台运行呢 通常Unity的脚本有C#.JS.Boo.不过现 ...
- Node做中转服务器,转发接口
查询各种资料,和整理网上一哥们不完整的接口.做成,可以使用的转发服务! 由于项目在做前后端分离,牵扯跨域和夸协议问题,临时抱佛脚,选择用nodejs做中转,我想应该好多人都用它.但是做普通的表单转发没 ...
- 阿里云CentOS搭建系统
1.在阿里云网站上购买申请服务器. 2.通过Xshell连接服务器,并用root账户登入. 3.配置java开发环境:(也可以使用阿里云一键部署,自动配置并部署服务器) 一.安装jdk 1.查看Lin ...
- 抛弃JQ,回归原生js……
之前我写过一篇文章叫做<jq不会被淘汰>--而事实上它真的不会被淘汰,因为即使在mvvm框架盛行的今天,原生js的api越来越友好的今天,jq依然在用户量上是霸主-- 但是今天我们要讨论的 ...