首页 » 网站推广 » movcmsphp技巧_iOS Video Tool box 视频硬编解码

movcmsphp技巧_iOS Video Tool box 视频硬编解码

访客 2024-11-21 0

扫一扫用手机浏览

文章目录 [+]

基于以上比拟,目前大部分业务场景的编解码策略是:手机端采取硬编码天生视频文件发送给做事器,做事器进行软编转码为支持更多的格式或码率的视频,再分发给不雅观看端。
考虑到有些设备不支持硬编解码,常日须要软编解码做兜底。

Annex-B 和 AVCC/HVCC

我们常见的视频编码格式H.264/AVC是由国际标准组织机构(ISO)下属的运动图象专家组(MPEG)和国际电传视讯同盟远程通信标准化组织(ITU-T)开拓的系列编码标准,之后又相继推出了H.265/HEVC和H.266/VVC,iOS目前尚不支持H.266,本文以H.264和H.265格式先容Video Toolbox的功能。

movcmsphp技巧_iOS Video Tool box 视频硬编解码

为了得到高的视频压缩比和网络亲和性(即适用于各种传输网络),将H.264系统架构分为了视频编码层VCL(Video Coding Layer)和网络抽象层(或网络适配层)NAL(Network Abstraction Layer),后续H.265、H.266也沿用了这一分层架构。

movcmsphp技巧_iOS Video Tool box 视频硬编解码
(图片来自网络侵删)
视频编码层(VCL):用于独立于网络进行视频编码,编码完成后输出SODB(String Of Data Bits)数据。
网络抽象层(NAL):该层的浸染是将视频编码数据根据内容的不同划分身分歧类型的NALU,以适配到各种各样的网络和多元环境中。
对VCL输出的SODB数据后添加结尾比特,一个比特 1 和多少个比特 0,用于字节对齐,称为RBAP,然后再在 RBSP 头部加上 NAL Header 来组成一个一个的NAL单元(unit)即NALU。

H.264和H.265的NAL Header构造:

【更多音视频学习资料,点击下方链接免费领取↓↓,先码住不迷路~】

C++程序员必看,捉住音视频开拓的大浪潮!
冲击年薪60万 音视频开拓根本知识和资料包

NAL Header包含当前NALU的类型(nal_unit_type)信息,H.264的NAL Header为一个字节,H.265为两个字节,以是获取类型的方法不同: H.264为:int type = (frame[4] & 0x1F),5代表I帧,7、8分别代表SPS、PPS。
H.265为:int type = int type = (code & 0x7E)>>1,19代表I帧,32、33、34分别代表VPS、SPS、PPS。

由于NALU长度不一,要写到一个文件中须要标识符来分割码流以区分独立的NALU,办理这一问题的两种方案,产生了两种不同的码流格式:

Annex-B:在每个NALU前加上0 0 0 1或者0 0 1,称作start code(起始码),如果原始码流中含有起始码,则起用防竞争字节:如将0 0 0 1处理为0 0 0 3 1。
AVCC/HVCC:在NALU前面加上几个字节,用于表示全体NALU的长度(大端序,读取时调用CFSwapInt32BigToHost()转为小端),在读取的时候先将长度读取出来,再读取全体NALU。

除了NALU前添加的字节表示的含义不同之外,AVCC/HVCC和Annex-B在处理序列参数集SPS(Sequence Parameter Set)、图像参数集PPS(Picture Parameter Set)和视频参数集VPS(Video Parameter Set)(H.265才有)上也不同(不是必须要对参数集的详细内容做详细理解,我们只要知道这些参数集是解码所必需的数据,在解码前须要拿到这些数据即可)。

H.264可以通过CMVideoFormatDescriptionGetH264ParameterSetAtIndex获取,0、1分别对应SPS和PPS。
H.265可以通过CMVideoFormatDescriptionGetHEVCParameterSetAtIndex获取,0、1、2分别对应VPS、SPS和PPS。
后面会有完全代码示例。

Annex-B和AVCC/HVCC对参数集的不同处理办法:

Annex-B:参数集当成普通的NALU处理,每个I帧前都须要添加(VPS/)SPS/PPS。
AVCC/HVCC:参数集分外处理,放在头部被称为extradata的数据中。

为什么分歧一为一种格式?

我们知道视频分为本地视频文件和网络直播流,对付直播流,AVCC/HVCC 格式只在头部添加了参数集,如果是中途进入不雅观看会获取不到参数集,也就无法初始化解码器进行解码,而 Annex-B 在每个I帧前都添加了参数集,可以从最近的I帧初始化解码器解码不雅观看。
而 AVCC/HVCC 只在头部添加参数集很适宜用于本地文件,解码本地文件只须要获取一次参数集进行解码就能播放,以是不须要像Annex-B一样重复地存储多份参数集。

为什么要理解这两种格式?

由于Video Toolbox编码和解码只支持 AVCC/HVCC 的码流格式,而Android的 MediaCodec 只支持 Annex-B 的码流格式。
因此在流媒体场景下,对付iOS开拓而言,须要在采集编码之后转为Annex-B格式再进行推流,拉流解码时则须要转为AVCC/HVCC格式才能用Video Toolbox进行解码播放。

如果在编码后想直接存储为本地文件,可以利用AVFoundation框架中的AVAssetWriter。

Video Toolbox框架概览

Video Toolbox 最早是在OS X上运行,现在看苹果的官方文档的解释,Video Toolbox 的系统支持为iOS 6.0+,实际上苹果在WWDC2014大会上才开放了Video Toolbox框架,即iOS 8.0往后开拓者才可以利用。

官方文档先容:Video Toolbox是一个底层框架,供应对硬件编码器和解码器的直接访问。
它供应了视频编码和解码做事,以及存储在CoreVideo像素缓冲区的光栅图像格式之间的转换。
这些做事以session(编码、解码和像素转换)的形式供应,并以Core Foundation (CF)类型供应。
不须要直接访问硬件编码器和解码器的运用程序不应该直策应用VideoToolbox。

Video Toolbox框架目前分为了编解码和像素转换两个模块,iOS9.0之后支持了多通道编解码,本文要利用的是Compression的前两个类:VTCompressionSession(编码)和VTDecompressionSession(解码)。

Video Toolbox的输入和输出

在利用Video Toolbox前我们先理解Video Toolbox的输入和输出。
iOS开拓常日利用AVFoundation框架进行视频录制,AVFoundation框架流利的数据类型为CMSampleBuffer,利用AVCapture模块录制视频的回调方法为- (void)captureOutput:(AVCaptureOutput )output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection )connection,编码须要的输入即原始视频帧CVImageBuffer(或CVPixelBuffer)就包裹在CMSampleBuffer中,经由编码后输出的仍为CMSampleBuffer类型,个中的CMBlockBuffer为编码后数据。
相反,解码以CMSampleBuffer类型的CMSampleBuffer作为输入,解码完输出CVImageBuffer(CVPixelBuffer),CMSampleBuffer可以通过AVAssetReader读取文件得到,或者从流媒体中读取NSData利用CMSampleBufferCreateReady手动创建。

编码

VTCompressionSession的文档先容了利用VTCompressionSession进行视频硬编码的事情流程:

1. Create a compression session using VTCompressionSessionCreate.

// 利用VTCompressionSessionCreate创建一个编码会话。

2. Optionally, configure the session with your desired Compression Properties by calling VTSessionSetProperty or VTSessionSetProperties.

// 可选地,通过调用VTSessionSetProperty或VTSessionSetProperties来配置编码器属性。

3. Encode video frames using VTCompressionSessionEncodeFrame and receive the compressed video frames in the session’s VTCompressionOutputCallback.

// 利用VTCompressionSessionEncodeFrame编码视频帧,并在会话的VTCompressionOutputCallback中吸收编码后的视频帧。

4. To force the completion of some or all pending frames, call VTCompressionSessionCompleteFrames.

// 要逼迫完成部分或所有挂起的帧,调用VTCompressionSessionCompleteFrames。

5. When you finish with the compression session, call VTCompressionSessionInvalidate to invalidate it and CFRelease to free its memory.

// 当您完成编码会话时,调用VTCompressionSessionInvalidate来使其无效,并调用CFRelease来开释它的内存。

结合实际的编码和后续处理,逐步解析干系API:

1. 创建编码会话VTCompressionSessionRef

/ 参数解析

allocator: session的内存分配器,传NULL表示默认的分配器。

width,height: 指定编码器的像素的宽高,与捕捉到的视频分辨率保持同等,如果视频编码器不能支持供应的宽度和高度,它可能会改变它们。

codecType: 编码类型,如H.264:kCMVideoCodecType_H264、H.265:kCMVideoCodecType_HEVC,最好先调用VTIsHardwareDecodeSupported(codecType) 判断是否支持该编码类型。

encoderSpecification: 指定必须利用特定的编码器,传NULL的话Video Toolbox会自己选择一个编码器。

sourceImageBufferAttributes: 原始视频数据须要的属性,紧张用于创建CVPixelBufferPool,如果不须要Video Toolbox创建,可以传NULL,但是利用自己创建的CVPixelBufferPool会增加须要拷贝图像数据的几率。

compressedDataAllocator: 编码数据的内存分配器,传NULL表示利用默认的分配器.

outputCallback: 吸收编码数据的回调,这个回调可以选择利用同步或异步办法吸收。
如果用同步则与VTCompressionSessionEncodeFrame函数线程保持同等,如果用异步会新建一条线程吸收。
该参数也可传NULL不过当且仅当我们利用VTCompressionSessionEncodeFrameWithOutputHandler函数作编码时。

outputCallbackRefCon: 可以传入用户自定义数据,紧张用于回调函数与主类之间的交互。

compressionSessionOut: 传入要创建的session的内存地址,把稳,session不能为NULL。

/

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) API_AVAILABLE(macosx(10.8), ios(8.0), tvos(10.2));

个中,视频解码输出的回调参数outputCallback类型为typedef void (VTCompressionOutputCallback)(void outputCallbackRefCon, void sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer),"sampleBuffer"即为编码后的数据。

2. 配置编码器属性

/

session: 会话

propertyKey: 属性名称

propertyValue: 属性值,设置为NULL将规复默认值。

/

VT_EXPORT OSStatus

VTSessionSetProperty(

CM_NONNULL VTSessionRef session,

CM_NONNULL CFStringRef propertyKey,

CM_NULLABLE CFTypeRef propertyValue ) API_AVAILABLE(macosx(10.8), ios(8.0), tvos(10.2));

WWDC2022之后即iOS16.0之后新加了3个key,加上之前支持的53个,统共支持56个不同的key值配置编码参数。
下表列举了常用的一些key值,大略感想熏染一下可以配置的内容:

【更多音视频学习资料,点击下方链接免费领取↓↓,先码住不迷路~】

音视频开拓根本知识和资料包

3. 准备编码

// session: 编码会话

VT_EXPORT OSStatus

VTCompressionSessionPrepareToEncodeFrames( CM_NONNULL VTCompressionSessionRef session ) API_AVAILABLE(macosx(10.9), ios(8.0), tvos(10.2));

4. 开始编码

/

session: 编码会话

imageBuffer: 一个CVImageBuffer,包含一个要压缩的视频帧,引用计数不能为0。

presentationTimeStamp: PTS,此帧的显示韶光戳,会添加到CMSampleBuffer中。
通报给session的每个PTS必须大于前一帧的。

duration: 此帧的显示持续韶光,会添加到CMSampleBuffer中。
如果没有持续韶光信息,则通报kCMTimeInvalid。

frameProperties: 指定此帧编码的属性的键/值对。
把稳,一些会话属性也可能在帧之间改变,这样的变革会对随后编码的帧产生影响。

sourceFrameRefCon: 帧的参考值,它将被通报给输出回调函数。

infoFlagsOut: 指向VTEncodeFlags指针,用于吸收有关编码操作的信息。
如果编码正在进行,可以设置kVTEncodeInfo_Asynchronous,如果帧被删除;可以设置kVTEncodeInfo_FrameDropped;如果不想吸收次信息可以传NULL。

/

VT_EXPORT OSStatus

VTCompressionSessionEncodeFrame(

CM_NONNULL VTCompressionSessionRef session,

CM_NONNULL CVImageBufferRef imageBuffer,

CMTime presentationTimeStamp,

CMTime duration, // may be kCMTimeInvalid

CM_NULLABLE CFDictionaryRef frameProperties,

void CM_NULLABLE sourceFrameRefcon,

VTEncodeInfoFlags CM_NULLABLE infoFlagsOut ) API_AVAILABLE(macosx(10.8), ios(8.0), tvos(10.2));

5. 处理编码后的数据

在回调outputCallback中处理编码后的数据,即CMSamplaBuffer。
要将编码后的CMSamplaBuffer写入mp4、MOV等容器文件,可以利用AVFoundation框架AVAssetWriter。

我们着重讲解流媒体场景下如何将AVCC/HVCC转为Annex-B格式:

从关键帧中获取extradata,获取参数集

// 判断是否是关键帧

bool isKeyFrame = !CFDictionaryContainsKey((CFDictionaryRef)CFArrayGetValueAtIndex(CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true), 0), (const void )kCMSampleAttachmentKey_NotSync);

CMSampleBufferGetSampleAttachmentsArray用于获取样本的附加信息数据,苹果文档对kCMSampleAttachmentKey_NotSync的阐明为:一个同步样本,也被称为关键帧或IDR(瞬时解码刷新),可以在不须要任何之前的样本被解码的情形下被解码。
同步样本之后的样本也不须要在同步样本之前的样本被解码。
以是样本的附加信息字典不包含该key即为I帧。

// 获取编码类型

CMVideoCodecType codecType = CMVideoFormatDescriptionGetCodecType(CMSampleBufferGetFormatDescription(sampleBuffer));

CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer);

if (codecType == kCMVideoCodecType_H264) {

// H.264/AVC 取出formatDescription中index 0、1对应的SPS、PPS

size_t sparameterSetSize, sparameterSetCount, pparameterSetSize, pparameterSetCount;

const uint8_t sparameterSet, pparameterSet;

OSStatus statusCode_sps = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &sparameterSet, &sparameterSetSize, &sparameterSetCount, 0);

if (statusCode_sps == noErr) { // SPS

NSData sps = [NSData dataWithBytes:sparameterSet length:sparameterSetSize];

}

OSStatus statusCode_pps = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pparameterSet, &pparameterSetSize, &pparameterSetCount, 0);

if (statusCode_pps == noErr) { // PPS

NSData pps = [NSData dataWithBytes:pparameterSet length:pparameterSetSize];

}

} else if (codecType == kCMVideoCodecType_HEVC) {

// H.265/HEVC 取出formatDescription中index 0、1、2对应的VPS、SPS、PPS

size_t vparameterSetSize, vparameterSetCount, sparameterSetSize, sparameterSetCount, pparameterSetSize, pparameterSetCount;

const uint8_t vparameterSet, sparameterSet, pparameterSet;

if (@available(iOS 11.0, )) { // H.265/HEVC 哀求iOS11以上

OSStatus statusCode_vps = CMVideoFormatDescriptionGetHEVCParameterSetAtIndex(format, 0, &vparameterSet, &vparameterSetSize, &vparameterSetCount, 0);

if (statusCode_vps == noErr) { // VPS

NSData vps = [NSData dataWithBytes:vparameterSet length:vparameterSetSize];

}

OSStatus statusCode_sps = CMVideoFormatDescriptionGetHEVCParameterSetAtIndex(format, 1, &sparameterSet, &sparameterSetSize, &sparameterSetCount, 0);

if (statusCode_sps == noErr) { // SPS

NSData sps = [NSData dataWithBytes:sparameterSet length:sparameterSetSize];

}

OSStatus statusCode_pps = CMVideoFormatDescriptionGetHEVCParameterSetAtIndex(format, 2, &pparameterSet, &pparameterSetSize, &pparameterSetCount, 0);

if (statusCode_pps == noErr) { // PPS

NSData pps = [NSData dataWithBytes:pparameterSet length:pparameterSetSize];

}

}

2.将参数集分别组装成单个NALU

// 添加start code(后续都以H.264为例)

NSMutableData annexBData = [NSMutableData new];

uint8_t startcode[] = {0x00, 0x00, 0x00, 0x01};

[annexBData appendBytes:nalPartition length:4];

[annexBData appendData:sps];

[annexBData appendBytes:nalPartition length:4];

[annexBData appendData:pps];

3.将AVCC/HVCC格式中表示长度的4字节更换为start code

// 获取编码数据。
这里的数据是 AVCC/HVCC 格式的。

CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);

size_t length, totalLength;

char dataPointer;

OSStatus statusCodeRet = CMBlockBufferGetDataPointer(dataBuffer, 0, &length, &totalLength, &dataPointer);

if (statusCodeRet == noErr) {

size_t bufferOffset = 0;

static const int NALULengthHeaderLength = 4;

// 拷贝编码数据。

while (bufferOffset < totalLength - NALULengthHeaderLength) {

// 通过 length 字段获取当前这个 NALU 的长度。

uint32_t NALUnitLength = 0;

memcpy(&NALUnitLength, dataPointer + bufferOffset, NALULengthHeaderLength);

NALUnitLength = CFSwapInt32BigToHost(NALUnitLength);

// 添加start code

[annexBData appendData:[NSData dataWithBytes:startcode length:4]];

[annexBData appendData:[NSData dataWithBytes:(dataPointer + bufferOffset + NALULengthHeaderLength) length:NALUnitLength]];

bufferOffset += NALULengthHeaderLength + NALUnitLength;

}

}

之后就可以进行上传推流或者直接将H.264/H.265写入文件了。

6. 结束编码

// 逼迫完成部分或所有挂起的帧

VTCompressionSessionCompleteFrames(_compressionSession, kCMTimeInvalid);

// 销毁编码器

VTCompressionSessionInvalidate(_compressionSession);

// 开释内存

CFRelease(_compressionSession);

按是否须要转化格式来分,iOS硬编码流程图如下:

解码

iOS视频硬解码是个从CMSampleBuffer中获取视频帧CVImageBuffer和用于播放所必须的显示韶光戳presentationTimeStamp和显示持续韶光presentationDuration的过程,Video Toolbox用于解码的类是VTDecompressionSession,文档也先容了利用VTDecompressionSession进行视频硬解码的事情流程:

1. Create a decompression session by calling VTDecompressionSessionCreate.

// 调用VTDecompressionSessionCreate创建一个解码会话

2. Optionally, configure the session with your desired Decompression Properties by calling VTSessionSetProperty or VTSessionSetProperties.

// 可选地,通过调用VTSessionSetProperty或VTSessionSetProperties来配置解码器属性。

3. Decode video frames using VTDecompressionSessionDecodeFrame.

// 解码视频帧利用VTDecompressionSessionDecodeFrame,并在解码会话的VTDecompressionOutputCallbackRecord回调中处理解码后的视频帧、显示韶光戳、显示持续韶光等信息。

4. When you finish with the decompression session, call VTDecompressionSessionInvalidate to tear it down, and call CFRelease to free its memory.

// 完成解码会话时,调用VTDecompressionSessionInvalidate来删除它,并调用CFRelease来开释它的内存。

结合实际的解码和后续处理,逐步解析干系API:

1. 创建解码会话VTDecompressionSessionRef

/参数解析

allocator: session的内存分配器,传NULL表示默认的分配器。

videoFormatDescription: 源视频帧的描述信息。

videoDecoderSpecification: 指定必须利用的特定视频解码器。
传NULL则Video Toolbox会自动选择一个解码器。

destinationImageBufferAttributes: 目标图像的属性哀求。
传NULL则不作哀求。

outputCallback: 解码的回调函数。
只能在调用VTDecompressionSessionDecodeFrameWithOutputHandler解码帧时通报NULL。

decompressionSessionOut: 指向一个变量以吸收新的解码会话。

/

VT_EXPORT OSStatus

VTDecompressionSessionCreate(

CM_NULLABLE CFAllocatorRef allocator,

CM_NONNULL CMVideoFormatDescriptionRef videoFormatDescription,

CM_NULLABLE CFDictionaryRef videoDecoderSpecification,

CM_NULLABLE CFDictionaryRef destinationImageBufferAttributes,

const VTDecompressionOutputCallbackRecord CM_NULLABLE outputCallback,

CM_RETURNS_RETAINED_PARAMETER CM_NULLABLE VTDecompressionSessionRef CM_NONNULL decompressionSessionOut) API_AVAILABLE(macosx(10.8), ios(8.0), tvos(10.2));

个中destinationImageBufferAttributes可以设置图像显示的分辨率(kCVPixelBufferWidthKey/kCVPixelBufferHeightKey)、像素格式(kCVPixelBufferPixelFormatTypeKey)、是兼容OpenGL(kCVPixelBufferOpenGLCompatibilityKey)等等,例如:

NSDictionary destinationImageBufferAttributes = @{

(id)kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange],

(id)kCVPixelBufferWidthKey: [NSNumber numberWithInteger:_config.width],

(id)kCVPixelBufferHeightKey: [NSNumber numberWithInteger:_config.height],

(id)kCVPixelBufferOpenGLCompatibilityKey: [NSNumber numberWithBool:true]

};

而videoFormatDescription须要分情形处理: 如果是通过AVAssetReader或者其他办法可以直接获取到CMSampleBuffer,我们可以直接调用CMSampleBufferGetFormatDescription(sampleBuffer)获取。
如果是从流媒体读取等无法直接获取CMSampleBuffer的情形,我们须要从Annex-B格式转为AVCC/HVCC,并且手动创建videoFormatDescription和CMSampleBuffer。

格式转换过程与编码恰好相反:将start code 转为4字节的NALU长度;判断数据类型,如果是参数集则存储用于初始化解码器,如果是帧数据则进行解码。

// 从流媒体读取NSData

NSData h264Data = ...;

uint8_t nalu = (uint8_t )h264Data.bytes;

// 获取NALU类型,以H.264为例

int type = (naluData[4] & 0x1F);

// 将start code 转为4字节的NALU长度

uint32_t naluSize = frameSize - 4;

uint8_t pNaluSize = (uint8_t )(&naluSize);

naluData[0] = (pNaluSize + 3);

naluData[1] = (pNaluSize + 2);

naluData[2] = (pNaluSize + 1);

naluData[3] = (pNaluSize);

// 处理不同类型数据

switch (type) {

case 0x05:

// I帧,去解码(解码前先确保解码器会话存在,否则就创建)

break;

case 0x06:

// SEI信息,不处理,H.265也有一些不用处理的信息,详情可以去理解一下H.265的type表

break;

case 0x07:

// sps

_spsSize = naluSize;

_sps = malloc(_spsSize);

// 从下标4(也便是第五个元素)开始复制数据

memcpy(_sps, &naluData[4], _spsSize);

break;

case 0x08:

// pps

_ppsSize = naluSize;

_pps = malloc(_ppsSize);

// 从下标4(也便是第五个元素)开始复制数据

memcpy(_pps, &naluData[4], _ppsSize);

break;

default:

// 其他帧(1-5),去解码(解码前先确保解码器会话存在,否则就创建)

break;

}

利用参数集创建CMVideoFormatDescriptionRef:

CMVideoFormatDescriptionRef videoDesc;

const uint8_t const parameterSetPointers[2] = {_sps, _pps};

const size_t parameterSetSizes[2] = {_spsSize, _ppsSize};

int naluHeaderLen = 4; // 大端模式起始位长度

OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault, 2, parameterSetPointers, parameterSetSizes, naluHeaderLen, &videoDesc);

【更多音视频学习资料,点击下方链接免费领取↓↓,先码住不迷路~】

C++程序员必看,捉住音视频开拓的大浪潮!
冲击年薪60万

2. 配置解码器属性

/

session: 会话

propertyKey: 属性名称

propertyValue: 属性值,设置为NULL将规复默认值。

/

VT_EXPORT OSStatus

VTSessionSetProperty(

CM_NONNULL VTSessionRef session,

CM_NONNULL CFStringRef propertyKey,

CM_NULLABLE CFTypeRef propertyValue ) API_AVAILABLE(macosx(10.8), ios(8.0), tvos(10.2));

苹果官方文档列举了可配置的key目前有24个,不再赘述,可查阅Decompression Properties。

3. 开始解码

/

session: 解码器会话

sampleBuffer: 包含一个或多个视频帧的CMSampleBuffer工具

decodeFlags: kVTDecodeFrame EnableAsynchronousDecompression位表示视频解码器是否可以异步解压缩帧。
kVTDecodeFrame EnableTemporalProcessing位指示解码器是否可以延迟对输出回调的调用,以便以韶光(显示)顺序进行处理。
如果这两个标志都被打消,解压完成,输出回调函数将在VTDecompressionSessionDecodeFrame返回之前被调用。
如果设置了个中一个标志,VTDecompressionSessionDecodeFrame可能会在调用输出回调函数之前返回。

sourceFrameRefCon: 解码标识。
如果sampleBuffer包含多个帧,输出回调函数将利用这个sourceFrameRefCon值多次调用。

infoFlagsOut: 指向VTEncodeInfoFlags指针,用于吸收有关解码操作的信息。
如果解码正在进行,可以设置kVTDecodeInfo_Asynchronous,如果帧被删除;可以设置kVTDecodeInfo_FrameDropped;如果不想吸收次信息可以传NUL。

/

VTDecompressionSessionDecodeFrame(

CM_NONNULL VTDecompressionSessionRef session,

CM_NONNULL CMSampleBufferRef sampleBuffer,

VTDecodeFrameFlags decodeFlags, // bit 0 is enableAsynchronousDecompression

void CM_NULLABLE sourceFrameRefCon,

VTDecodeInfoFlags CM_NULLABLE infoFlagsOut) API_AVAILABLE(macosx(10.8), ios(8.0), tvos(10.2));

个中的sampleBuffer如果须要从NSData中读取,我们须要利用视频帧的数据创建CMBlockBuffer,再结合参数集信息创建CMSampleBuffer:

CVPixelBufferRef outputPixelBuffer = NULL;

CMBlockBufferRef blockBuffer = NULL;

CMBlockBufferFlags flag0 = 0;

// 创建blockBuffer

OSStatus status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, frame, frameSize, kCFAllocatorNull, NULL, 0, frameSize, flag0, &blockBuffer);

CMSampleBufferRef sampleBuffer = NULL;

const size_t sampleSizeArray[] = {frameSize};

// 创建sampleBuffer

status = CMSampleBufferCreateReady(kCFAllocatorDefault, blockBuffer, _videoDesc, 1, 0, NULL, 1, sampleSizeArray, &sampleBuffer);

4. 处理解码后的数据

在解码的回调videoDecoderCallBack中我们可以获取图像和显示韶光戳、显示持续韶光,结合OpenGL、Core Imge进行显示,不再深入先容。

5. 结束解码

// 销毁解码器

VTDecompressionSessionInvalidate(self.decodeSession);

// 开释内存

CFRelease(self.decodeSession);

iOS不同场景的硬解码流程图如下

把稳事变前后台切换会导致编解码出错,须要重新创建会话。
有些视频流虽然是AVCC格式,但NALU size的大小是3个字节,须要转为4字节格式。
切换分辨率时须要拿到新的参数集,重启解码器。
编码后的视频帧之间存在参考关系,为避免遗漏末了几帧,须要在解码完末了一帧数据后调用VTDecompressionSessionWaitForAsynchronousFrames,该接口会等待所有未输出的视频帧输出结束后再返回。

标签:

相关文章

语言栏,现代科技与人类语言的完美融合

随着科技的飞速发展,人类的生活越来越便捷。其中,电脑语言栏作为现代科技与人类语言的完美融合,为我们带来了极大的便利。本文将从语言栏...

网站推广 2024-12-29 阅读0 评论0

语言框架,沟通的艺术与方法

在人际交往中,沟通是不可或缺的一环。而语言框架作为一种沟通的艺术与技巧,对于提高沟通效果、促进人际关系具有重要作用。本文将从语言框...

网站推广 2024-12-29 阅读0 评论0

语言暴力,心灵毒瘤的蔓延与反思

语言是人与人沟通的桥梁,在现实生活中,却有一部分人将语言当作伤害他人的利器,用恶毒的语言对他人进行欺负。这种现象,我们称之为“语言...

网站推广 2024-12-29 阅读0 评论0

语言教程软件,助力英语学习的新时代利器

随着科技的飞速发展,人工智能逐渐成为教育领域的有力助手。在英语学习方面,语言教程软件应运而生,为广大学子提供了便捷、高效的学习途径...

网站推广 2024-12-29 阅读0 评论0