视频的编解码-编码篇

转载:http://www.jianshu.com/p/9e078dfabf0a

转载时间:2017年4月6日

在此之前我们通常使用的FFmpeg多媒体库,利用CPU来进行视频的编解码,占用CPU资源,效率低下,俗称软编解码.而苹果在2014年的iOS8中,开放了VideoToolbox.framwork框架,此框架使用GPU或专用的处理器来进行编解码,俗称硬编解码.而此框架在此之前只有MAC

OS系统中可以使用,在iOS作为私有框架.终于苹果在iOS8.0中得到开放引入.

2014年的WWDCDirect Access to Video Encoding and Decoding中,苹果介绍了使用videoToolbox硬编解码.

使用硬编解码有几个优点: * 提高性能; * 增加效率; * 延长电量的使用

(一)对于编解码,AVFoundation框架只有以下几个功能:

1. 直接解压后显示;

2. 直接压缩到一个文件当中;

(二)而对于Video Toolbox,我们可以通过以下功能获取到数据,进行网络流传输等多种保存:

1. 解压为图像的数据结构;

2. 压缩为视频图像的容器数据结构.

一、videoToolbox的基本数据

Video Toolbox视频编解码前后概念说明:

CVPixelBuffer:编码前和解码后的图像数据结构。此内容包含一系列的CVPixelBufferPool内容

CMTime、CMClock和CMTimebase:时间戳相关。时间以64-bit/32-bit的形式出现。

pixelBufferAttributes:字典设置.可能包括Width/height、pixel format type、• Compatibility (e.g., OpenGL ES, Core Animation)

CMBlockBuffer:编码后,结果图像的数据结构。

CMVideoFormatDescription:图像存储方式,编解码器等格式描述。

(CMSampleBuffer:存放编解码前后的视频图像的容器数据结构。

CMTimebase: 关于CMClock的一个控制视图,包含CMClock、时间映射(Time mapping)、速率控制(Rate control)

由二、采集视频数据可知,我们获取到的数据(CMSampleBufferRef)sampleBuffer为未编码的数据;

上图中,编码前后的视频图像都封装在CMSampleBuffer中,编码前以CVPixelBuffer进行存储;编码后以CMBlockBuffer进行存储。除此之外两者都包括CMTime、CMVideoFormatDesc.

二、视频数据流编码并上传到服务器

1.将CVPixelBuffer使用VTCompressionSession进行数据流的硬编码。


(1)初始化VTCompressionSession

VT_EXPORT

OSStatus VTCompressionSessionCreate(     CM_NULLABLE CFAllocatorRef

allocator,    int32_t

width,    int32_t

height,    CMVideoCodecType

codecType,    CM_NULLABLE CFDictionaryRef

encoderSpecification,    CM_NULLABLE CFDictionaryRef

sourceImageBufferAttributes,    CM_NULLABLE CFAllocatorRef

compressedDataAllocator,    CM_NULLABLE

VTCompressionOutputCallback             outputCallback,    void *

CM_NULLABLE                                  outputCallbackRefCon,

CM_RETURNS_RETAINED_PARAMETER CM_NULLABLE VTCompressionSessionRef *

CM_NONNULL compressionSessionOut)

__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_8_0);

VTCompressionSession的初始化参数说明:

allocator:分配器,设置NULL为默认分配

width: 宽

height: 高

codecType: 编码类型,如kCMVideoCodecType_H264

encoderSpecification: 编码规范。设置NULL由videoToolbox自己选择

sourceImageBufferAttributes: 源像素缓冲区属性.设置NULL不让videToolbox创建,而自己创建

compressedDataAllocator: 压缩数据分配器.设置NULL,默认的分配

outputCallback:

当VTCompressionSessionEncodeFrame被调用压缩一次后会被异步调用.注:当你设置NULL的时候,你需要调用VTCompressionSessionEncodeFrameWithOutputHandler方法进行压缩帧处理,支持iOS9.0以上

outputCallbackRefCon: 回调客户定义的参考值.

compressionSessionOut: 压缩会话变量。

(2)配置VTCompressionSession

使用VTSessionSetProperty()调用进行配置compression。 * kVTCompressionPropertyKeyAllowFrameReordering: 允许帧重新排序.默认为true * kVTCompressionPropertyKeyAverageBitRate: 设置需要的平均编码率 * kVTCompressionPropertyKeyH264EntropyMode:H264的熵编码模式。有两种模式:一种基于上下文的二进制算数编码CABAC和可变长编码VLC.在slice层之上(picture和sequence)使用定长或变长的二进制编码,slice层及其以下使用VLC或CABAC.详情请参考* kVTCompressionPropertyKeyRealTime: 视频编码压缩是否是实时压缩。可设置CFBoolean或NULL.默认为NULL * kVTCompressionPropertyKeyProfileLevel: 对于编码流指定配置和标准 .比如kVTProfileLevelH264MainAutoLevel

配置过VTCompressionSession后,可以可选的调用VTCompressionSessionPrepareToEncodeFrames进行准备工作编码帧。

(3)开始硬编码流入的数据

使用VTCompressionSessionEncodeFrame方法进行编码.当编码结束后调用outputCallback回调函数。

VT_EXPORT

OSStatus  VTCompressionSessionEncodeFrame(      CM_NONNULL

VTCompressionSessionRef  session,    CM_NONNULL CVImageBufferRef

imageBuffer,    CMTime

presentationTimeStamp,    CMTime

duration,// may be kCMTimeInvalidCM_NULLABLE CFDictionaryRef

frameProperties,void* CM_NULLABLE                  sourceFrameRefCon,

VTEncodeInfoFlags * CM_NULLABLE     infoFlagsOut )

__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_8_0);

presentationTimeStamp: 获取到的这个sample buffer数据的展示时间戳。每一个传给这个session的时间戳都要大于前一个展示时间戳.

duration: 对于获取到sample buffer数据,这个帧的展示时间.如果没有时间信息,可设置kCMTimeInvalid.

frameProperties: 包含这个帧的属性.帧的改变会影响后边的编码帧.

sourceFrameRefCon: 回调函数会引用你设置的这个帧的参考值.

infoFlagsOut:

指向一个VTEncodeInfoFlags来接受一个编码操作.如果使用异步运行,kVTEncodeInfo_Asynchronous被设置;同步运行,kVTEncodeInfo_FrameDropped被设置;设置NULL为不想接受这个信息.

(4)执行VTCompressionOutputCallback回调函数

typedefvoid(*VTCompressionOutputCallback)(void*

CM_NULLABLE outputCallbackRefCon,void* CM_NULLABLE sourceFrameRefCon,

OSStatus status,         VTEncodeInfoFlags infoFlags,

CM_NULLABLE CMSampleBufferRef sampleBuffer );

outputCallbackRefCon: 回调函数的参考值

sourceFrameRefCon: VTCompressionSessionEncodeFrame函数中设置的帧的参考值

status: 压缩的成功为noErr,如失败有错误码

infoFlags: 包含编码操作的信息标识

sampleBuffer: 如果压缩成功或者帧不丢失,则包含这个已压缩的数据CMSampleBuffer,否则为NULL

(5)将压缩成功的sampleBuffer数据进行处理为基本流NSData上传到服务器

MPEG-4是一套用于音频、视频信息的压缩编码标准.

图1.1可知,已压缩 $$CMSampleBuffer = CMTime(可选) + CMBlockBuffer + CMVideoFormatDesc$$。

5.1 先判断压缩的数据是否正确

//不存在则代表压缩不成功或帧丢失if(!sampleBuffer)return;if(status

!=

noErr)return;//返回sampleBuffer中包括可变字典的不可变数组,如果有错误则为NULLCFArrayRefarray=

CMSampleBufferGetSampleAttachmentsArray(sampleBuffer,true);if(!array)return;

CFDictionaryRef dic =

CFArrayGetValueAtIndex(array,0);if(!dic)return;//issue

3:kCMSampleAttachmentKey_NotSync:没有这个键意味着同步, yes: 异步. no:同步BOOL keyframe

= !CFDictionaryContainsKey(dic,

kCMSampleAttachmentKey_NotSync);//此代表为同步

而对于issue 3从字面意思理解即为以上的说明,但是网上看到很多都是做为查询是否是视频关键帧,而查询文档看到有此关键帧key值kCMSampleBufferAttachmentKey_ForceKeyFrame存在,因此对此值如若有了解情况者敬请告知详情.

5.2 获取CMVideoFormatDesc数据由三、解码篇可知CMVideoFormatDesc 包括编码所用的profile,level,图像的宽和高,deblock滤波器等.具体包含第一个NALU的SPS(Sequence Parameter Set)和第二个NALU的PPS(Picture Parameter Set).

//if

(keyframe && !encoder -> sps) {    //获取sample buffer 中的

CMVideoFormatDesc    CMFormatDescriptionRef format =

CMSampleBufferGetFormatDescription(sampleBuffer);

//获取H264参数集合中的SPS和PPS    const uint8_t * sparameterSet;size_t

sparameterSetSize,sparameterSetCount ;   OSStatus statusCode =

CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0,

&sparameterSet, &sparameterSetSize,

&sparameterSetCount,0);if (statusCode == noErr) {        size_t

pparameterSetSize, pparameterSetCount;        const uint8_t

*pparameterSet;OSStatus statusCode =

CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1,

&pparameterSet, &pparameterSetSize,

&pparameterSetCount,0);if (statusCode == noErr) {

encoder->sps = [NSData

dataWithBytes:sparameterSetlength:sparameterSetSize];encoder->pps =

[NSData dataWithBytes:pparameterSetlength:pparameterSetSize];}    }}

5.3 获取CMBlockBuffer并转换成数据

CMBlockBufferRef

blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);    size_t

lengthAtOffset,totalLength;char*dataPointer;//接收到的数据展示OSStatus

blockBufferStatus = CMBlockBufferGetDataPointer(blockBuffer,0,

&lengthAtOffset, &totalLength,

&dataPointer);if(blockBufferStatus != kCMBlockBufferNoErr)    {

size_t bufferOffset =0;staticconstintAVCCHeaderLength

=4;while(bufferOffset < totalLength -  AVCCHeaderLength) {// Read the

NAL unit lengthuint32_t NALUnitLength =0;/**

*  void *memcpy(void *dest, const void *src, size_t n);

*  从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中

*/memcpy(&NALUnitLength,

dataPointer + bufferOffset, AVCCHeaderLength);//字节从高位反转到低位NALUnitLength

= CFSwapInt32BigToHost(NALUnitLength);            RTAVVideoFrame *

frame = [RTAVVideoFramenew];            frame.sps = encoder -> sps;

frame.pps = encoder -> pps;            frame.data = [NSData

dataWithBytes:(dataPointer+bufferOffset+AVCCHeaderLength)

length:NALUnitLength];            bufferOffset += NALUnitLength +

AVCCHeaderLength;        }    }

此得到的H264数据应用于后面的RTMP协议做推流准备。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容