来源:http://blog.csdn.net/wangruihit/article/details/46550853

VideoToolbox是iOS平台在iOS8之后开放的一个Framework,提供了在iOS平台利用硬件实现H264编解码的能力。

这套接口的合成主要我一个人参与,花费了四五天的时间,中间主要参考了WWDC 2014  513关于hardware codec的视频教程

OpenWebrtc的vtenc/vtdec模块

chromium的一部分代码

https://src.chromium.org/svn/trunk/src/content/common/gpu/media/vt_video_decode_accelerator.cc

https://chromium.googlesource.com/chromium/src/media/+/cea1808de66191f7f1eb48b5579e602c0c781146/cast/sender/h264_vt_encoder.cc

还有stackoverflow的一些帖子,如

http://stackoverflow.com/questions/29525000/how-to-use-videotoolbox-to-decompress-h-264-video-stream

http://stackoverflow.com/questions/24884827/possible-locations-for-sequence-picture-parameter-sets-for-h-264-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时所做的设置。设置代码如下:

  1. CFMutableDictionaryRef source_attrs = CFDictionaryCreateMutable (NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  2. CFNumberRef number;
  3. number = CFNumberCreate (NULL, kCFNumberSInt16Type, &codec_settings->width);
  4. CFDictionarySetValue (source_attrs, kCVPixelBufferWidthKey, number);
  5. CFRelease (number);
  6. number = CFNumberCreate (NULL, kCFNumberSInt16Type, &codec_settings->height);
  7. CFDictionarySetValue (source_attrs, kCVPixelBufferHeightKey, number);
  8. CFRelease (number);
  9. OSType pixelFormat = kCVPixelFormatType_420YpCbCr8Planar;
  10. number = CFNumberCreate (NULL, kCFNumberSInt32Type, &pixelFormat);
  11. CFDictionarySetValue (source_attrs, kCVPixelBufferPixelFormatTypeKey, number);
  12. CFRelease (number);
  13. CFDictionarySetValue(source_attrs, kCVPixelBufferOpenGLESCompatibilityKey, kCFBooleanTrue);
  14. OSStatus ret = VTCompressionSessionCreate(NULL, codec_settings->width, codec_settings->height, kCMVideoCodecType_H264, NULL, source_attrs, NULL, EncodedFrameCallback, this, &encoder_session_);
  15. if (ret != 0) {
  16. WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
  17. "vt_encoder::InitEncode() fails to create encoder ret_val %d",
  18. ret);
  19. return WEBRTC_VIDEO_CODEC_ERROR;
  20. }
  21. 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

  1. /*!
  2. @function   CMVideoFormatDescriptionCreateFromH264ParameterSets
  3. @abstract   Creates a format description for a video media stream described by H.264 parameter set NAL units.
  4. @discussion This function parses the dimensions provided by the parameter sets and creates a format description suitable for a raw H.264 stream.
  5. The parameter sets' data can come from raw NAL units and must have any emulation prevention bytes needed.
  6. 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.
  7. */
  8. CM_EXPORT
  9. OSStatus CMVideoFormatDescriptionCreateFromH264ParameterSets(
  10. CFAllocatorRef allocator,                      /*! @param allocator
  11. CFAllocator to be used when creating the CMFormatDescription. Pass NULL to use the default allocator. */
  12. size_t parameterSetCount,                      /*! @param parameterSetCount
  13. The number of parameter sets to include in the format description. This parameter must be at least 2. */
  14. const uint8_t * constconst * parameterSetPointers,  /*! @param parameterSetPointers
  15. Points to a C array containing parameterSetCount pointers to parameter sets. */
  16. const size_tsize_t * parameterSetSizes,              /*! @param parameterSetSizes
  17. Points to a C array containing the size, in bytes, of each of the parameter sets. */
  18. int NALUnitHeaderLength,                       /*! @param NALUnitHeaderLength
  19. Size, in bytes, of the NALUnitLength field in an AVC video sample or AVC parameter set sample. Pass 1, 2 or 4. */
  20. CMFormatDescriptionRef *formatDescriptionOut ) /*! @param formatDescriptionOut
  21. Returned newly-created video CMFormatDescription */
  22. __OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0);

4,判断VT编码出来的数据是否是keyframe

这个代码取自OpenWebrtc from Ericsson

  1. static bool
  2. vtenc_buffer_is_keyframe (CMSampleBufferRef sbuf)
  3. {
  4. bool result = FALSE;
  5. CFArrayRef attachments_for_sample;
  6. attachments_for_sample = CMSampleBufferGetSampleAttachmentsArray (sbuf, 0);
  7. if (attachments_for_sample != NULL) {
  8. CFDictionaryRef attachments;
  9. CFBooleanRef depends_on_others;
  10. attachments = (CFDictionaryRef)CFArrayGetValueAtIndex (attachments_for_sample, 0);
  11. depends_on_others = (CFBooleanRef)CFDictionaryGetValue (attachments,
  12. kCMSampleAttachmentKey_DependsOnOthers);
  13. result = (depends_on_others == kCFBooleanFalse);
  14. }
  15. return result;
  16. }

4,SPS和PPS变化后判断VT是否还能正确解码

通过下面的接口判断是否需要需要更新VT

  1. /*!
  2. <span style="white-space:pre">    </span>@function VTDecompressionSessionCanAcceptFormatDescription
  3. <span style="white-space:pre">    </span>@abstract Indicates whether the session can decode frames with the given format description.
  4. <span style="white-space:pre">    </span>@discussion
  5. <span style="white-space:pre">        </span>Some video decoders are able to accommodate minor changes in format without needing to be
  6. <span style="white-space:pre">        </span>completely reset in a new session.  This function can be used to test whether a format change
  7. <span style="white-space:pre">        </span>is sufficiently minor.
  8. */
  9. VT_EXPORT Boolean
  10. VTDecompressionSessionCanAcceptFormatDescription(
  11. <span style="white-space:pre">    </span>VTDecompressionSessionRef<span style="white-space:pre">      </span>session,
  12. <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.

  1. VT_EXPORT OSStatus
  2. VTCompressionSessionEncodeFrame(
  3. VTCompressionSessionRef     session,
  4. CVImageBufferRef            imageBuffer,
  5. CMTime                      presentationTimeStamp,
  6. CMTime                      duration, // may be kCMTimeInvalid
  7. CFDictionaryRef             frameProperties, // may be NULL
  8. voidvoid *                      sourceFrameRefCon,
  9. VTEncodeInfoFlags           *infoFlagsOut /* may be NULL */ ) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_8_0);

6,编码选项

  1. kVTCompressionPropertyKey_AllowTemporalCompression
  1. kVTCompressionPropertyKey_AllowFrameReordering

TemporalCompression控制是否产生P帧。

FrameReordering控制是否产生B帧。

7,使用自带的PixelBufferPool提高性能。

创建VTSession之后会自动创建一个PixelBufferPool,用做循环缓冲区,降低频繁申请释放内存区域造成的额外开销。

  1. VT_EXPORT CVPixelBufferPoolRef
  2. VTCompressionSessionGetPixelBufferPool(
  3. VTCompressionSessionRef     session ) __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_8_0);
  1. CV_EXPORT CVReturn CVPixelBufferPoolCreatePixelBuffer(CFAllocatorRef allocator,
  2. CVPixelBufferPoolRef pixelBufferPool,
  3. CVPixelBufferRef *pixelBufferOut) __OSX_AVAILABLE_STARTING(__MAC_10_4,__IPHONE_4_0);

中间还有很多很多的细节,任何一处错误都是导致千奇百怪的crash/编码或解码失败等

多看看我提供的那几个链接,会很有帮助。

经过测试,iOS8 硬件编解码效果确实很好,比OpenH264出来的视频质量更清晰,并且能轻松达到30帧,码率控制的精确性也更高。

webrtc (6) 在Webrtc中集成VideoToolbox的更多相关文章

  1. WebRTC 学习之 WebRTC 简介

    本文使用的WebRTC相关API都是基于Intel® Collaboration Suite for WebRTC的. 相关文档链接:https://software.intel.com/sites/ ...

  2. solr服务中集成IKAnalyzer中文分词器、集成dataimportHandler插件

    昨天已经在Tomcat容器中成功的部署了solr全文检索引擎系统的服务:今天来分享一下solr服务在海量数据的网站中是如何实现数据的检索. 在solr服务中集成IKAnalyzer中文分词器的步骤: ...

  3. [译]MVC网站教程(四):MVC4网站中集成jqGrid表格插件(系列完结)

    目录 1.   介绍 2.   软件环境 3.   在运行示例代码之前(源代码 + 示例登陆帐号) 4.         jqGrid和AJAX 5.         GridSettings 6.  ...

  4. 如何在 ASP.NET MVC 中集成 AngularJS(3)

    今天来为大家介绍如何在 ASP.NET MVC 中集成 AngularJS 的最后一部分内容. 调试路由表 - HTML 缓存清除 就在我以为示例应用程序完成之后,我意识到,我必须提供两个版本的路由表 ...

  5. 如何在 ASP.NET MVC 中集成 AngularJS(2)

    在如何在 ASP.NET MVC 中集成 AngularJS(1)中,我们介绍了 ASP.NET MVC 捆绑和压缩.应用程序版本自动刷新和工程构建等内容. 下面介绍如何在 ASP.NET MVC 中 ...

  6. 如何在ios中集成微信登录功能

    在ios中集成微信的登录功能有两种方法 1 用微信原生的api来做,这样做的好处就是轻量级,程序负重小,在Build Settings 中这样设置 然后设置 友盟的设置同上,但是要注意,加入你需要的所 ...

  7. 在Application中集成Microsoft Translator服务之开发前准备

    第一步:准备一个微软账号 要使用Microsoft Translator API需要在Microsoft Azure Marketplace(https://datamarket.azure.com/ ...

  8. 在Abp中集成Swagger UI功能

    在Abp中集成Swagger UI功能 1.安装Swashbuckle.Core包 通过NuGet将Swashbuckle.Core包安装到WebApi项目(或Web项目)中. 2.为WebApi方法 ...

  9. 如何在 ASP.NET MVC 中集成 AngularJS(1)

    介绍 当涉及到计算机软件的开发时,我想运用所有的最新技术.例如,前端使用最新的 JavaScript 技术,服务器端使用最新的基于 REST 的 Web API 服务.另外,还有最新的数据库技术.最新 ...

随机推荐

  1. Java虚拟机所管理的内存,包含的运行时数据区域?

    运行时数据区域 线程私有(随用户线程的启动和结束而建立和销毁)或所有线程共享(随虚拟机进程的启动而存在) 抛出的异常 备注 程序计数器(Program Counter Register) 线程私有 唯 ...

  2. 实现 Trie (前缀树)

    实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作. 示例: Trie trie = new Trie(); trie.insert(" ...

  3. TortoiseSVN客户端(七)

    TortoiseSVN 是一个 Windows 下的版本控制系统 Apache™ Subversion®的客户端工具. 一.安装 官网下载地址:https://tortoisesvn.net/down ...

  4. Excel-统计函数

    1.Count系列函数 COUNT 数字个数----下面结果为 4 counta 非空的字数 ----下面为6 COUNTBLANK ------非空个数  ---- 下面为9 如何将字符串形式的数字 ...

  5. Spring Cloud微服务安全实战_4-1_微服务网关安全_概述&微服务安全面临的挑战

      第四章  网关安全 这一章从简单的API的场景过渡到复杂的微服务的场景 4.1 概述 微服务安全面临的挑战:介绍中小企业的一个微服务架构,相比第三章的单体应用的简单的API所面临的哪些挑战 OAu ...

  6. 虚拟机-Ubuntu

    1.安装 下载iso镜像文件,在VMware中创建时添加即可 2.安装tools,可以复制文件 参考:https://www.cnblogs.com/justaman/p/10545239.html ...

  7. 分析并解决Linux发行版的自带OpenJdk和自己安装的OracleJdk新旧版本冲突问题

    解决办法: 从Oraclejdk 目录里可执行文件链接都复制到自己的LINK目录,然后IDE使用LINK变量下的命令 本文没有具体解决方法,只有探索思路........................ ...

  8. Via板载声卡底噪严重、播放卡顿及耳机与扬声器音源切换问题【解决方法】

    HD VDeck[VIA威盛HD audio系列音频驱动] 关闭音效增强之后,一切正常............ 默默骂一句VIA沙雕 另外附上:开启耳机和扬声器独立音源的设置 注册表 Computer ...

  9. copy running-config startup-config 与 copy startup-config running-config

    1.copy running-config startup-config 与 copy startup-config running-config 两者有什么不同???ANS:running-conf ...

  10. Qt Quick 常用元素:RadioButton(单选框),CheckBox(复选框) 与 GroupBox(分组框)

    先介绍一下 ExclusiveGroup. ExclusiveGroup (互斥分组)本身是不可见元素,用于将若干个可选择元素组合在一起, 供用户选择其中的一个选项.你可以在 ExclusiveGro ...