webrtc (6) 在Webrtc中集成VideoToolbox
来源:http://blog.csdn.net/wangruihit/article/details/46550853
VideoToolbox是iOS平台在iOS8之后开放的一个Framework,提供了在iOS平台利用硬件实现H264编解码的能力。
这套接口的合成主要我一个人参与,花费了四五天的时间,中间主要参考了WWDC 2014 513关于hardware codec的视频教程,
chromium的一部分代码
https://src.chromium.org/svn/trunk/src/content/common/gpu/media/vt_video_decode_accelerator.cc,
还有stackoverflow的一些帖子,如
http://stackoverflow.com/questions/29525000/how-to-use-videotoolbox-to-decompress-h-264-video-stream
另外还有apple forum的帖子如:
https://devforums.apple.com/message/1063536#1063536
中间需要注意的是,
1,YUV数据格式
Webrtc传递给Encoder的是数据是I420,对应VT里的kCVPixelFormatType_420YpCbCr8Planar,如果VT使用
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange格式(即NV12),那么需要将I420转换为NV12再进行编码。转换可使用libyuv库。
I420格式有3个Planar,分别存放YUV数据,并且数据连续存放。类似:YYYYYYYYYY.......UUUUUU......VVVVVV......
NV12格式只有2个Planar,分别存放YUV数据,首先是连续的Y数据,然后是UV数据。类似YYYYYYYYY......UVUVUV......
选择使用I420格式编码还是NV12进行编码,取决于在初始化VT时所做的设置。设置代码如下:
- CFMutableDictionaryRef source_attrs = CFDictionaryCreateMutable (NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- CFNumberRef number;
- number = CFNumberCreate (NULL, kCFNumberSInt16Type, &codec_settings->width);
- CFDictionarySetValue (source_attrs, kCVPixelBufferWidthKey, number);
- CFRelease (number);
- number = CFNumberCreate (NULL, kCFNumberSInt16Type, &codec_settings->height);
- CFDictionarySetValue (source_attrs, kCVPixelBufferHeightKey, number);
- CFRelease (number);
- OSType pixelFormat = kCVPixelFormatType_420YpCbCr8Planar;
- number = CFNumberCreate (NULL, kCFNumberSInt32Type, &pixelFormat);
- CFDictionarySetValue (source_attrs, kCVPixelBufferPixelFormatTypeKey, number);
- CFRelease (number);
- CFDictionarySetValue(source_attrs, kCVPixelBufferOpenGLESCompatibilityKey, kCFBooleanTrue);
- OSStatus ret = VTCompressionSessionCreate(NULL, codec_settings->width, codec_settings->height, kCMVideoCodecType_H264, NULL, source_attrs, NULL, EncodedFrameCallback, this, &encoder_session_);
- if (ret != 0) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
- "vt_encoder::InitEncode() fails to create encoder ret_val %d",
- ret);
- return WEBRTC_VIDEO_CODEC_ERROR;
- }
- CFRelease(source_attrs);
2,VT编码出来的数据是AVCC格式,需要转换为Annex-B格式,才能回调给Webrtc。主要区别在于数据开头是长度字段还是startCode,具体见stackoverflow的帖子。
同理,编码时,需要将webrtc的Annex-B格式转换为AVCC格式。
Annex-B:StartCode + Nalu1 + StartCode + Nalu2 + ...
AVCC :Nalu1 length + Nalu1 + Nalu2 length + Nalu2 + ...
注意⚠:AVCC格式中的length字段需要是big endian顺序。length字段的长度可定制,一般为1/2/4byte,需要通过接口配置给解码器。
3,创建VideoFormatDescription
解码时需要创建VTDecompressionSession,需要一个VideoFormatDescription参数。
创建VideoFormatDescription需要首先从码流中获取到SPS和PPS,然后使用如下接口创建VideoFormatDescription
- /*!
- @function CMVideoFormatDescriptionCreateFromH264ParameterSets
- @abstract Creates a format description for a video media stream described by H.264 parameter set NAL units.
- @discussion This function parses the dimensions provided by the parameter sets and creates a format description suitable for a raw H.264 stream.
- The parameter sets' data can come from raw NAL units and must have any emulation prevention bytes needed.
- The supported NAL unit types to be included in the format description are 7 (sequence parameter set), 8 (picture parameter set) and 13 (sequence parameter set extension). At least one sequence parameter set and one picture parameter set must be provided.
- */
- CM_EXPORT
- OSStatus CMVideoFormatDescriptionCreateFromH264ParameterSets(
- CFAllocatorRef allocator, /*! @param allocator
- CFAllocator to be used when creating the CMFormatDescription. Pass NULL to use the default allocator. */
- size_t parameterSetCount, /*! @param parameterSetCount
- The number of parameter sets to include in the format description. This parameter must be at least 2. */
- const uint8_t * constconst * parameterSetPointers, /*! @param parameterSetPointers
- Points to a C array containing parameterSetCount pointers to parameter sets. */
- const size_tsize_t * parameterSetSizes, /*! @param parameterSetSizes
- Points to a C array containing the size, in bytes, of each of the parameter sets. */
- int NALUnitHeaderLength, /*! @param NALUnitHeaderLength
- Size, in bytes, of the NALUnitLength field in an AVC video sample or AVC parameter set sample. Pass 1, 2 or 4. */
- CMFormatDescriptionRef *formatDescriptionOut ) /*! @param formatDescriptionOut
- Returned newly-created video CMFormatDescription */
- __OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0);
4,判断VT编码出来的数据是否是keyframe
这个代码取自OpenWebrtc from Ericsson
- static bool
- vtenc_buffer_is_keyframe (CMSampleBufferRef sbuf)
- {
- bool result = FALSE;
- CFArrayRef attachments_for_sample;
- attachments_for_sample = CMSampleBufferGetSampleAttachmentsArray (sbuf, 0);
- if (attachments_for_sample != NULL) {
- CFDictionaryRef attachments;
- CFBooleanRef depends_on_others;
- attachments = (CFDictionaryRef)CFArrayGetValueAtIndex (attachments_for_sample, 0);
- depends_on_others = (CFBooleanRef)CFDictionaryGetValue (attachments,
- kCMSampleAttachmentKey_DependsOnOthers);
- result = (depends_on_others == kCFBooleanFalse);
- }
- return result;
- }
4,SPS和PPS变化后判断VT是否还能正确解码
通过下面的接口判断是否需要需要更新VT
- /*!
- <span style="white-space:pre"> </span>@function VTDecompressionSessionCanAcceptFormatDescription
- <span style="white-space:pre"> </span>@abstract Indicates whether the session can decode frames with the given format description.
- <span style="white-space:pre"> </span>@discussion
- <span style="white-space:pre"> </span>Some video decoders are able to accommodate minor changes in format without needing to be
- <span style="white-space:pre"> </span>completely reset in a new session. This function can be used to test whether a format change
- <span style="white-space:pre"> </span>is sufficiently minor.
- */
- VT_EXPORT Boolean
- VTDecompressionSessionCanAcceptFormatDescription(
- <span style="white-space:pre"> </span>VTDecompressionSessionRef<span style="white-space:pre"> </span>session,
- <span style="white-space:pre"> </span>CMFormatDescriptionRef<span style="white-space:pre"> </span>newFormatDesc ) __OSX_AVAILABLE_STARTING(__MAC_10_8,__IPHONE_8_0);
5,PTS
PTS会影响VT编码质量,一般情况下,duration参数表示每帧数据的时长,用样点数表示,一般视频采样频率为90KHz,帧率为30fps,则duration就是sampleRate / frameRate = 90K/30 = 3000.
而pts表示当前帧的显示时间,也用样点数表示,即 n_samples * sampleRate / frameRate.
- VT_EXPORT OSStatus
- VTCompressionSessionEncodeFrame(
- VTCompressionSessionRef session,
- CVImageBufferRef imageBuffer,
- CMTime presentationTimeStamp,
- CMTime duration, // may be kCMTimeInvalid
- CFDictionaryRef frameProperties, // may be NULL
- voidvoid * sourceFrameRefCon,
- VTEncodeInfoFlags *infoFlagsOut /* may be NULL */ ) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_8_0);
6,编码选项
- kVTCompressionPropertyKey_AllowTemporalCompression
- kVTCompressionPropertyKey_AllowFrameReordering
TemporalCompression控制是否产生P帧。
FrameReordering控制是否产生B帧。
7,使用自带的PixelBufferPool提高性能。
创建VTSession之后会自动创建一个PixelBufferPool,用做循环缓冲区,降低频繁申请释放内存区域造成的额外开销。
- VT_EXPORT CVPixelBufferPoolRef
- VTCompressionSessionGetPixelBufferPool(
- VTCompressionSessionRef session ) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_8_0);
- CV_EXPORT CVReturn CVPixelBufferPoolCreatePixelBuffer(CFAllocatorRef allocator,
- CVPixelBufferPoolRef pixelBufferPool,
- CVPixelBufferRef *pixelBufferOut) __OSX_AVAILABLE_STARTING(__MAC_10_4,__IPHONE_4_0);
中间还有很多很多的细节,任何一处错误都是导致千奇百怪的crash/编码或解码失败等
多看看我提供的那几个链接,会很有帮助。
经过测试,iOS8 硬件编解码效果确实很好,比OpenH264出来的视频质量更清晰,并且能轻松达到30帧,码率控制的精确性也更高。
webrtc (6) 在Webrtc中集成VideoToolbox的更多相关文章
- WebRTC 学习之 WebRTC 简介
本文使用的WebRTC相关API都是基于Intel® Collaboration Suite for WebRTC的. 相关文档链接:https://software.intel.com/sites/ ...
- solr服务中集成IKAnalyzer中文分词器、集成dataimportHandler插件
昨天已经在Tomcat容器中成功的部署了solr全文检索引擎系统的服务:今天来分享一下solr服务在海量数据的网站中是如何实现数据的检索. 在solr服务中集成IKAnalyzer中文分词器的步骤: ...
- [译]MVC网站教程(四):MVC4网站中集成jqGrid表格插件(系列完结)
目录 1. 介绍 2. 软件环境 3. 在运行示例代码之前(源代码 + 示例登陆帐号) 4. jqGrid和AJAX 5. GridSettings 6. ...
- 如何在 ASP.NET MVC 中集成 AngularJS(3)
今天来为大家介绍如何在 ASP.NET MVC 中集成 AngularJS 的最后一部分内容. 调试路由表 - HTML 缓存清除 就在我以为示例应用程序完成之后,我意识到,我必须提供两个版本的路由表 ...
- 如何在 ASP.NET MVC 中集成 AngularJS(2)
在如何在 ASP.NET MVC 中集成 AngularJS(1)中,我们介绍了 ASP.NET MVC 捆绑和压缩.应用程序版本自动刷新和工程构建等内容. 下面介绍如何在 ASP.NET MVC 中 ...
- 如何在ios中集成微信登录功能
在ios中集成微信的登录功能有两种方法 1 用微信原生的api来做,这样做的好处就是轻量级,程序负重小,在Build Settings 中这样设置 然后设置 友盟的设置同上,但是要注意,加入你需要的所 ...
- 在Application中集成Microsoft Translator服务之开发前准备
第一步:准备一个微软账号 要使用Microsoft Translator API需要在Microsoft Azure Marketplace(https://datamarket.azure.com/ ...
- 在Abp中集成Swagger UI功能
在Abp中集成Swagger UI功能 1.安装Swashbuckle.Core包 通过NuGet将Swashbuckle.Core包安装到WebApi项目(或Web项目)中. 2.为WebApi方法 ...
- 如何在 ASP.NET MVC 中集成 AngularJS(1)
介绍 当涉及到计算机软件的开发时,我想运用所有的最新技术.例如,前端使用最新的 JavaScript 技术,服务器端使用最新的基于 REST 的 Web API 服务.另外,还有最新的数据库技术.最新 ...
随机推荐
- Rational Rose 画九种常用架构图
UML建模详解(3)—使用Rational Rose 画九种常用架构图 UML是一种建模语言,是系统建模的标准.我们之所以建模是因为大规模的系统设计时相当复杂的,当系统比较复杂时就会涉及到以下这几 ...
- SQL基础知识整理
建议:关键字和函数名全部大写:数据库名称.表名称.字段名称全部小写:分号结尾.但是大小写语句不区分,本人讨厌大写(英语菜,不能一眼看出内容),所以全部小写.[]看情况可有可无,{}花括号的内容必须要有 ...
- .Net反射-两种方式获取Enum中的值
public enum EJobType { 客服 = , 业务员 = , 财务 = , 经理 = } Type jobType = typeof(EJobType); 方式1: Array enum ...
- odoo 新建模块命令
python odoo-bin scaffold academy myaddons 自动初始化所有的配置信息: python odoo-bin --addons=addons,"/home/ ...
- 徒手实现lower_bound和upper_bound
STL中lower_bound和upper_bound的使用方法:STL 二分查找 lower_bound: ; ; //初始化 l ,为第一个合法地址 ; //初始化 r , 地址的结束地址 int ...
- Spring FrameWork体系结构及模块间依赖关系
详见:https://www.cnblogs.com/ywlaker/p/6136625.html 几个图: (Spring3) (Spring4) 由于Spring体系结构庞大且复杂,为了简化开发者 ...
- linux shell脚本中的延时
linux shell脚本中的延时 还是使用 sleep 或usleep函数. 详细如下: .sleep : 默认为秒. sleep 1s 表示延迟一秒 sleep 1m 表示延迟一分钟 ...
- STM32开发/烧录/调试环境搭建 基于:Win10+STM32Cube+openocd+cmsis-dap(dap-link)
dap-link是个不错的STM32下载/调试工具,然而STM32Cube并没有直接支持它,只能通过openocd的方式间接支持. 网络上虽然已经有了其他人的教程,不过基本都是基于linux或者msy ...
- asp.net core 2.1 容器中使用 System.Drawing.Common 的问题
- 用友U8合同执行单保存报错,提示“语法错误或违反访问规则”
问题描述 用户参照合同录入第一阶段的执行单,保存时提示“语法错误或违反访问规则”,其他合同均正常,只有这张合同出现这个问题. 解决过程 语法错误或违反访问规则是SQL Server常见的报错,但其他合 ...