ios开发视频解码,ios视频编码和解码

iOS-视频硬编/解码(对帧图片编辑)

这里我们分为三步走

创新互联公司是专业的寒亭网站建设公司,寒亭接单;提供成都做网站、成都网站设计,网页设计,网站设计,建网站,PHP网站建设等专业做网站服务;采用PHP框架,可快速的进行寒亭网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,专业的做网站团队,希望更多企业前来合作!

我这边采用的是 iOS利用VideoToolbox实现视频硬解码 中的方式,demo中提供了两种解码方式,一种是第三方库ffmpeg,另外一种是系统的videotoolbox库,效率基本上都差不多,作者封装后都会有一个代理回调,

两种回调都会有一个CMSampleBufferRef的对象,它是像素缓冲,其实就是我们解码后的每一帧的图片。

在第一步中我们已经拿到了每一帧的图片,现在我们在其基础上合成本地图片,合成图片我首先想到的是下面的方法,

这个方法是通过两个image的对象,最终合成一个新的image,效果可以实现,但是在我们这个场景中,我们在对视频解码时,是一个连续异步的操作,代理会不断的回调,此时如果采用这个方式,会多次创建局部对象, 不能及时释放,导致内存爆增 ,引起闪退。所以我采用以下方式

这里我用的是CIImage,它提供了一个系统的api

iOS 视频硬解码

公司的项目里有拉取H.264视频流进行解码播放的功能,本来是采用FFMpeg多媒体库,用CPU做视频的编码和解码,就是大家常说的软编软解。但是软解存在太占用CPU,解码效率低等缺点,所以我们一合计干脆用硬解码代替原来的方案。当然硬件解码使用的当然就是苹果大名鼎鼎的 Video ToolBox 框架,众所周知,苹果在iOS8开始才可以在iOS系统中调用该框架中的 API 。

图2.1展示的是通过 AVSampleBufferDisplaylayer 播放网络上获取的H.264码流。

但并不是说 AVSampleBufferDisplaylayer 能直接播放H.264码流,需要将H.264码流包装成 SampleBuffer 传给给 AVSampleBufferDisplaylayer 解码播放。

再来看一下H.264码流的构成,H.264码流由一系列的 NAL 单元组成。

NAL 单元一般包含:

所以如果要将H.264解码播放就需要将H.264码流包装成CMSampleBuffer。由图1.1可得 CMSampleBuffer = CMTime + CMVideoFormatDesc + CMBlockBuffer 。

解码步骤:

1.从网络获取的码流中获取SPS和PPS生成CMVideoFormatDesc。

(1)H.264 NALU 单元的 Start Code 是"0x 00 00 01" 或"0x 00 01",按照 Start Code 定位NALU。

(2)通过类型信息找到 SPS 和 PPS 并提取,开始码后第一个 byte 的第5位,7代表 SPS ,8代表 PPS 。

(3)使用CMVideoFormatDescriptionCreateFromH264ParameterSets函数来构建CMVideoFormatDescription。

2.提取视频图像数据生成CMBlockBuffer。

(1)按照 Start Code 定位NALU。

(2)CMBlockBuffer数据需要的头部码为4个字节的长度,为:0x 00 80 00,所以需要将H.264的header给替换掉。

(3) CMBlockBufferCreateWithMemoryBlock 接口构造 CMBlockBufferRef 。

3.根据自己的需要设置CMTime

我的项目中的拉取的实时流需要实时播放,不需要设置时间间隔,所以不用设置CMTime。

4.根据上述得到 CMVideoFormatDescriptionRef 、 CMBlockBufferRef 和可选的时间信息,使用 CMSampleBufferCreate 接口得到 CMSampleBuffer 数据这个待解码的原始的数据。

5.用 AVSampleBufferDisplayLayer 处理得到 sampleBuffer 来显示图像。

至此成功用 Video Toolbox 硬件解码H.264码流,并在设备上播放视频。

可是,如果我们要拿到每一帧图像进行处理呢,那该怎么得到?

那么我们还需要用 VTDecompressionSession 解码成 CVPixelBuffer ,通过 UIImageView 或者 OpenGL ES 上显示。

(1)创建 VTDecompressionSession ,需要以下参数:

(2)调用 VTDecompresSessionDecodeFrame 接口进行解码。

(3)VTDecompressionSessionOutputCallback回调函数中可以得到解码后的结果 CVPixelBuffer ,可以将 CVPixelBuffer 转换成UIImage图像显示在ImageView上或者用 OpenGL ES 渲染图像。

ios开发,现在需处理一个视频剪辑的功能,对多媒体视频流处理,截取部分视频流,解码播放。。

首先告诉你摆渡知道没这方面的大神高手。懂的人也不会来这里混。。。你看到的回答大多数都是搬砖搬过来的,铁耙也是,懂的人不会去那里混的。你应该去多点论坛,什么CSDN,Cocoachina这一类。

然后,视频处理解码需要用到第三方库,用苹果默认的是不支持的。然后截取部分视频这个要用到一个代理方法。至于什么我忘记了,

iOS音视频编码

iOS音视频开发涉及的方面比较广泛,一般分为采集、音视频的封装、编码、推流、拉流、解封装、解码、播放等阶段。其中音视频的编码和解码阶段是非常重要的步骤。其中编码分为硬编码和软编码,对应解码分为硬解码和软解码。

硬编码是指使用非CPU进行编码,如显卡GPU、专用的DSP、FPGA、ASIC芯片等。

硬编码一般性能高,对CPU没有压力,但是对其他硬件要求较高(如GPU等)。

软编码是指使用CPU进行编码,软编码通常使用:ffmpeg+x264。

软编码实现直接、简单,参数调整方便,升级易,但CPU负载重,性能较硬编码低。

常见的直播服务普遍采用了RTMP作为流媒体协议,FLV作为封装格式,H.264作为视频编码格式,AAC作为音频编码格式。

iOS中编码方式:

在iOS8之前,苹果并没有开放硬编码的接口,所以只能采用ffmpeg+x624进行软编码

在iOS8之后,苹果开放了接口,并且封装了VideoToolBoxAudioToolbox两个框架,分别用于对视频音频进行硬编码

视频的编解码-编码篇

四、视频的编解码-编码篇

时间 2016-08-05 10:22:59 Twenty's 时间念

原文

主题 iOS开发 MacOS

在此之前我们通常使用的FFmpeg多媒体库,利用CPU来进行视频的编解码,占用CPU资源,效率低下,俗称软编解码.而苹果在2014年的iOS8中,开放了VideoToolbox.framwork框架,此框架使用GPU或专用的处理器来进行编解码,俗称硬编解码.而此框架在此之前只有MAC OS系统中可以使用,在iOS作为私有框架.终于苹果在iOS8.0中得到开放引入.

2014年的WWDC Direct 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:存放编解码前后的视频图像的容器数据结构。

CMClock

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

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

图1.1

上图中,编码前后的视频图像都封装在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。 * kVTCompressionPropertyKey AllowFrameReordering: 允许帧重新排序.默认为true * kVTCompressionPropertyKey AverageBitRate: 设置需要的平均编码率 * kVTCompressionPropertyKey H264EntropyMode:H264的 熵编码 模式。有两种模式:一种基于上下文的二进制算数编码CABAC和可变长编码VLC.在slice层之上(picture和sequence)使用定长或变长的二进制编码,slice层及其以下使用VLC或CABAC. 详情请参考 * kVTCompressionPropertyKey RealTime: 视频编码压缩是否是实时压缩。可设置CFBoolean或NULL.默认为NULL * kVTCompressionPropertyKey ProfileLevel: 对于编码流指定配置和标准 .比如kVTProfileLevel H264 Main AutoLevel

配置过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协议做推流准备。

iOS 音视频编解码基本概念(一)

内容元素:

当采集视频信号时,如果采用逐行扫描,则每次扫描得到的信号就是一副图像,也就是一帧。

当采集视频信号时,如果采用隔行扫描(奇、偶数行),则扫描下来的一帧图像就被分为了两个部分,这每一部分就称为「场」,根据次序分为:「顶场」和「底场」。

「帧」和「场」的概念又带来了不同的编码方式:帧编码、场编码逐行扫描适合于运动图像,所以对于运动图像采用帧编码更好;隔行扫描适合于非运动图像,所以对于非运动图像采用场编码更好

网络提取层单元(NALU, Network Abstraction Layer Unit),

NALU 是用来将编码的数据进行打包的,一个分片(Slice)可以编码到一个 NALU 单元。不过一个 NALU 单元中除了容纳分片(Slice)编码的码流外,还可以容纳其他数据,比如序列参数集 SPS。对于客户端其主要任务则是接收数据包,从数据包中解析出 NALU 单元,然后进行解码播放。

宏块(Macroblock),分片是由宏块组成。


新闻标题:ios开发视频解码,ios视频编码和解码
本文路径:http://ybzwz.com/article/hopcoj.html