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 服务.另外,还有最新的数据库技术.最新 ...
随机推荐
- Centos 安装Django2.1
一.环境准备 1.1 服务器系统 Centos7 1.2.所需工具 python3(Django 2.0 版本是基于python3 版本,故而安装时需安装python3) Setuptools(> ...
- Spring Cloud版本 version命名说明 (Edgware)
Spring Cloud版本 version命名说明 (Edgware) 版权声明:guofangsky 版权所有,转载不究. https://blog.csdn.net/guofangsky/a ...
- 10.webpack学习使用
1.什么是Webpack Webpack 是当下最热门的前端资源模块化管理和打包工具,它可以将许多松散耦合的模块按照依赖和规则打包成符合生产环境部署的前端资源.还可以将按需加载的模块进行代码分离,等到 ...
- Xamarin.Forms移动开发系列4 :XAML基础
摘要 本文介绍Xamarin.Forms创建用户界面的语言:XAML基础部分. 前言 本文介绍Xamarin.Forms定义用户界面的语言:XAML. 本篇篇幅较长,主要讲述XAML语法,以及对其他基 ...
- 洛谷 P1156 垃圾陷阱 题解
题目传送门 dp+排序+01背包 就完事了??? 貌似就是这样的 代码: //dp 排序 01背包 #include<iostream> #include<cstdio> #i ...
- C语言实现五子棋
可以称得上史上最简单的五子棋版本了. 可以使用curses库来改进页面和下棋方式. 并且对于输入的坐标没有进行鉴别,如果输入的坐标超过棋盘大小,就会段错误退出. 我改进了一点,但是还是没有完全避免这个 ...
- activiti在线画流程图
springboot2.2 activiti6.0 activiti-modeler 5.22.0 注明:版本不一样会导致报错 上一篇:springboot整合activiti 效果图 代码分享:ht ...
- 微信企业号消息接口PHP SDK
微信企业号消息接口PHP SDK及Demo <?php /* 方倍工作室 http://www.fangbei.org/ CopyRight 2015 All Rights Reserved * ...
- 微信企业号获取OpenID过程
define('CorpID', "wx82e2c31215d9a5a7"); define('CorpSecret', ""); //当前管理组 设置-> ...
- 修咻咻对追光的人、云打印团队的Beta产品测试报告
修咻咻对追光的人.云打印团队的Beta产品测试报告 作业描述 课程 软件工程1916|W(福州大学) 团队名称 修!咻咻! 作业要求 项目Beta冲刺(团队) 团队目标 切实可行的计算机协会维修预约平 ...