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 服务.另外,还有最新的数据库技术.最新 ...
随机推荐
- Java虚拟机所管理的内存,包含的运行时数据区域?
运行时数据区域 线程私有(随用户线程的启动和结束而建立和销毁)或所有线程共享(随虚拟机进程的启动而存在) 抛出的异常 备注 程序计数器(Program Counter Register) 线程私有 唯 ...
- 实现 Trie (前缀树)
实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作. 示例: Trie trie = new Trie(); trie.insert(" ...
- TortoiseSVN客户端(七)
TortoiseSVN 是一个 Windows 下的版本控制系统 Apache™ Subversion®的客户端工具. 一.安装 官网下载地址:https://tortoisesvn.net/down ...
- Excel-统计函数
1.Count系列函数 COUNT 数字个数----下面结果为 4 counta 非空的字数 ----下面为6 COUNTBLANK ------非空个数 ---- 下面为9 如何将字符串形式的数字 ...
- Spring Cloud微服务安全实战_4-1_微服务网关安全_概述&微服务安全面临的挑战
第四章 网关安全 这一章从简单的API的场景过渡到复杂的微服务的场景 4.1 概述 微服务安全面临的挑战:介绍中小企业的一个微服务架构,相比第三章的单体应用的简单的API所面临的哪些挑战 OAu ...
- 虚拟机-Ubuntu
1.安装 下载iso镜像文件,在VMware中创建时添加即可 2.安装tools,可以复制文件 参考:https://www.cnblogs.com/justaman/p/10545239.html ...
- 分析并解决Linux发行版的自带OpenJdk和自己安装的OracleJdk新旧版本冲突问题
解决办法: 从Oraclejdk 目录里可执行文件链接都复制到自己的LINK目录,然后IDE使用LINK变量下的命令 本文没有具体解决方法,只有探索思路........................ ...
- Via板载声卡底噪严重、播放卡顿及耳机与扬声器音源切换问题【解决方法】
HD VDeck[VIA威盛HD audio系列音频驱动] 关闭音效增强之后,一切正常............ 默默骂一句VIA沙雕 另外附上:开启耳机和扬声器独立音源的设置 注册表 Computer ...
- copy running-config startup-config 与 copy startup-config running-config
1.copy running-config startup-config 与 copy startup-config running-config 两者有什么不同???ANS:running-conf ...
- Qt Quick 常用元素:RadioButton(单选框),CheckBox(复选框) 与 GroupBox(分组框)
先介绍一下 ExclusiveGroup. ExclusiveGroup (互斥分组)本身是不可见元素,用于将若干个可选择元素组合在一起, 供用户选择其中的一个选项.你可以在 ExclusiveGro ...