WebRTC手记之WebRtcVideoEngine2模块
转载请注明出处:http://www.cnblogs.com/fangkm/p/4401143.html
终于讲到视频数据的编码发送模块了,不容易。总体来说也看了不少时间WebRTC的源码了,最大的感触就是各个模块在开发的时候非常独立,每个模块都定义了自己的一套接口,最后串起来的时候添加各种适配对象来转接。这给我们这些刚开始源码阅读的人带来非常大的苦恼,不过WebRTC的模块内的结构设计还是很不错的,不然我也没有看下去的动力。
注意命名,WebRtcVideoEngine2带了个2字,不用想,这肯定是个升级版本的VideoEngine,还有个WebRtcVideoEngine类。从目前我的理解来看,WebRtcVideoEngine2比WebRtcVideoEngine改进之处在于将视频流一分为二:发送流(WebRtcVideoSendStream)和接收流(WebRtcVideoReceiveStream),从而结构上更合理,源码更清晰。这个部分等下会细说。在介绍WebRtcVideoEngine2之前,先简单地分析一下WebRTC的Media Engine结构,说实话,我真不会表达Engine是个怎样的概念,但既然这样命名,核心模块肯定是错不了的。结构很简单:

- MediaEngineInterface:抽象Media Engine的逻辑接口,负责创建用于视频传输的VideoMediaChannel、用于音频传输的VoiceMediaChannel、注册音频数据处理接口等。
- CompositeMediaEngine:实现MediaEngineInterface接口,本身也是个模板类,两个模板参数分别是视频Engine和音频Engine。其派生类WebRtcMediaEngine依赖的模板参数是WebRtcVoiceEngine和WebRtcVideoEngine,而用于Chromium的WebRtcMediaEngine2则依赖WebRtcVoiceEngine和WebRtcVideoEngine2。
WebRtcVideoEngine2主要作用在于创建视频channel对象WebRtcVideoChannel2。结构如下:

当调用WebRtcVideoChannel2的AddSendStream方法时,会创建一个WebRtcVideoSendStream对象,同样,调用AddRecvStream成员方法,会创建一个WebRtcVideoReceiveStream对象。
当外部调用WebRtcVideoChannel2的SetCapturer方法时,会转给WebRtcVideoSendStream来响应,WebRtcVideoSendStream内部将InputFrame成员方法挂接VideoCapturer的SignalVideoFrame信号来接收视频采集器传输过来的视频帧数据。
WebRtcVideoChannel2的AddSendStream和SetCapturer的调用时机这里暂时不考虑,这些涉及到网络连接,等每个节点的内容分析完后,再探讨整个流程。
如图所示,WebRtcVideoSendStream和WebRtcVideoReceiveStream也只是个包装类,内部依赖Call接口创建对应的VideoSendStream接口实现类和VideoReceiveStream接口实现类。在internal命名空间内,分别有一个Call类、VideoSendStream类、VideoReceiveStream类来实现这三个接口,Call类创建关键的VideoEngine对象来管理视频数据发送过程中的一系列处理逻辑。从代码结构上看,VideoEngine是一个相对独立的模块,它封装视频数据采集后的处理、编码等逻辑,下面仔细分析一下VideoEngine的结构:

VideoEngine模块里有ViEBase、ViECodec、ViECapture、ViEImageProcess、ViENetwork、ViERender、ViERTP_RTCP、ViEExternalCodec接口,注意,这些都是功能性的接口,它们相应的实现分别对应于上图中的XXXImpl类,VideoEngineImpl类从所有的XXXImpl接口派生,因此外部有了VideoEngine接口,都可以通过强转的方式获取ViEBase、ViECapture等之类的接口(根据VideoEngine强转成相应的接口的逻辑封装在目标接口的GetInterface静态方法中),外界可以通过这些接口来完成视频数据做相应的设置,而这些设置最终都反映到一个名叫ViESharedData的类对象里。该对象由ViEBaseImpl创建并在各接口的实现之间共享,XXXImpl可以通过ViEBaseImpl的shared_data方法来访问,用于共享的数据有三类:ViEInputManager、ViEChannelManager和ViERenderManager。下面分别介绍一下这关键的三个对象。
- ViEInputManager:封装了视频采集/输入逻辑(哈哈,又是一套视频输入逻辑),结构:

ViEInputManager为每个通道分配一个ViECapturer对象来做为视频源,ViECapturer可以传入也可以自己创建一个VideoCaptureModule视频采集模块,并通过VideoCaptureDataCallback接口从其接收数据,也可以直接通过ViEExternalCapture接口接收从外部直接传入的视频帧数据(调用ViEExternalCapture接口的IncomingFrame方法)。VideoSendStream就是通过ViEInputManager创建一个ViEExternalCapture对象来传入外界传来的视频帧数据(通过WebRtcVideoSendStream的InputFrame传来)。这里要注意,ViEInputManager为创建的ViECapturer对象分配一个capture_id,外界可以通过这个capture_id来操作其对应的ViECapturer。视频源传入逻辑已经明了,接下来分析一下视频是怎么传出去的。无论通过哪种视频数据接收方法,ViECapturer都不会立即将数据传递出去,因为它内部需要对这些视频数据做相关的处理。数据处理必然耗时,如果采用同步的方式,必将阻塞视频传入的流程。因此,在创建ViECapturer的时候,会启动一采集线程,该线程调用ViECaptureProcess处理函数,在该处理函数里,先调用VideoProcessingModule对视频数据进行处理(灯光加亮、去闪烁),如果在ViEImageProcessImpl里注册了ViEEffectFilter处理对象,这里也会调用该对象来处理视频帧数据,最后通过DeliverFrame方法分发到注册进来的所有ViEFrameCallback接口。
- ViEChannelManager:封装了视频编码和传输逻辑,这块结构比较复杂,总体如下:

ViEChannelManager维护了ViEEncoder和ViEChannel对象,ViEEncoder实现了ViEFrameCallback接口从ViECapturer对象中接收视频帧数据,ViEEncoder对接收到的视频帧数据进行编码,然后将编码后的数据传给ViEChannel(通过两者之间共享的PayloadRouter对象),ViEChannel将编码后的视频数据通过RTP/RTCP协议发送出去。下面分别分析一下ViEEncoder和ViEChannel。
1) ViEEncoder类:封装了视频编码流程。

视频编码由VideoCodingModule模块统一管理,视频帧传入接口是通过VideoCodingModule的的AddVideoFrame方法,编码后的视频传出接口是借助VCMPacketizationCallback接口来回调。具体选取哪种视频编码的逻辑位于VCMCodecDataBase类,当前支持VP8编码、VP9编码和视频格式到I420格式的转换。
2)ViEChannel类:封装了编码后的视频数据发送逻辑和视频数据接收解码逻辑。

视频数据发送逻辑是通过PayloadRouter对象委托给RtpRtcp模块做RTP协议的封装,具体的网络发送操作还是回托给ViESender做数据的网络发送操作。ViESender的逻辑相对简单,限于篇幅,图中无法做详细的标注。ViESender的发送操作依赖外部设置给它的Transport接口(通过VideoEngine模块的ViENetwork接口来完成设置)。
当WebRtcVideoChannel2接收到网路数据包后(通过OnPacketReceived或OnRtcpReceived方法响应),会在VideoReceiveStream对象中通过VideoEngine模块暴露出去的ViENetwork接口来响应数据包处理,最终触发到ViEChannel的ReceivedRTPPacket或ReceivedRTCPPacket方法。ViEChannel中将接收并解码网络视频数据的任务分配给ViEReceiver对象。ViEReceiver先调用RTP/RTCP模块做协议的解析(图中限于篇幅未标注出来),解析完成后调用VideoCodingModule模块进行数据的解码操作(参见ViEReceiver的OnReceivedPayloadData方法),VideoCodingModule模块内部维护了一个与VideoSender对应的VideoReceiver来完成解码逻辑,这块与VideoSender的编码逻辑完全对称,这里不再表述。
- ViERenderManager:这个类封装了视频渲染逻辑,结构如下:

当ViEChannel接收到网络数据解包并解码后,就会开启触发渲染流程(参见FrameToRender方法),ViEChannel会调用向其注册的ViEFrameCallback接口来派发视频帧数据。ViERenderManager维护了一个ViERenderer对象来实现ViEFrameCallback接口,它将数据进一步派发,最终通过ExternalRenderer接口派发给WebRtcVideoChannel2的VideoReceiveStream对象。VideoReceiveStream通过VideoSource设置进来的VideoRenderer接口将数据派发给VideoTrack,用户可以挂接VideoRendererInterface接口来接收视频帧数据。真够绕的,而且那么多命名的相似性(比如VideoRender/VideoRenderer),感觉各模块开发期间,都实现了自己的一套接口规范,最后强行串在一起了。
WebRTC手记之WebRtcVideoEngine2模块的更多相关文章
- (六)WebRTC手记之WebRtcVideoEngine2模块
转自:http://www.cnblogs.com/fangkm/p/4401143.html 终于讲到视频数据的编码发送模块了,不容易.总体来说也看了不少时间WebRTC的源码了,最大的感触就是各个 ...
- 单独编译使用WebRTC的音频处理模块
块,每块个点,(12*64=768采样)即AEC-PC仅能处理48ms的单声道16kHz延迟的数据,而 - 加载编译好的NS模块动态库 接下来只需要按照 此文 的描述在 android 的JAVA代码 ...
- webrtc的音频处理模块apm( audio processing)下载与编译出libwebrtc_audio_preprocessing.so
webrtc代码在android和chromium项目中都有.但是android中的那个带有Android.mk,稍微修改下就能用ndk-build编译出libwebrtc_audio_preproc ...
- 【单独编译使用WebRTC的音频处理模块 - android】
更新 [2015年2月15日] Bill 这段时间没有再关注 WebRTC 以及音频处理的相关信息,且我个人早已不再推荐单独编译 WebRTC 中的各个模块出来使用.实际上本文的参考价值已经很小了,甚 ...
- webRTC中语音降噪模块ANS细节详解(二)
上篇(webRTC中语音降噪模块ANS细节详解(一))讲了维纳滤波的基本原理.本篇先给出webRTC中ANS的基本处理过程,然后讲其中两步(即时域转频域和频域转时域)中的一些处理细节. ANS的基本处 ...
- webRTC中语音降噪模块ANS细节详解(三)
上篇(webRTC中语音降噪模块ANS细节详解(二))讲了ANS的处理流程和语音在时域和频域的相互转换.本篇开始讲语音降噪的核心部分,首先讲噪声的初始估计以及基于估计出来的噪声算先验信噪比和后验信噪比 ...
- webRTC中语音降噪模块ANS细节详解(四)
上篇(webRTC中语音降噪模块ANS细节详解(三))讲了噪声的初始估计方法以及怎么算先验SNR和后验SNR. 本篇开始讲基于带噪语音和特征的语音和噪声的概率计算方法和噪声估计更新以及基于维纳滤波的降 ...
- (五)WebRTC手记Channel概念
转自:http://www.cnblogs.com/fangkm/p/4401075.html 前面两篇博文完整地介绍了WebRTC音视频的采集模块,接下来应该开始介绍关键的音视频编码模块.不过在介绍 ...
- WebRTC手记Channel概念
转载请注明出处:http://www.cnblogs.com/fangkm/p/4401075.html 前面两篇博文完整地介绍了WebRTC音视频的采集模块,接下来应该开始介绍关键的音视频编码模块. ...
随机推荐
- squid日志配置与轮询
squid日志分类及参数 SQUID默认的log文件非常多,其中最重要的LOG日志有三个,分别为access.log.store.log.cache.log.三个日志的记录的内容如下: access. ...
- 【SpringMVC】SpringMVC系列15之SpringMVC最佳实践
15.SpringMVC最佳实践 15.1.遵循Restful API最佳实践 参考:http://segmentfault.com/a/1190000002949234 15.2.统一返回字段 15 ...
- poj 2709
http://poj.org/problem?id=2709 题意:就是那个老师需要n瓶颜色的墨水,和1瓶颜色的灰色的墨水,但是灰色的墨水没得卖,只能由三种颜色相同的墨水混合而成,但是3瓶50ML的墨 ...
- 39.递归颠倒栈[ReverseStack]
[题目] 用递归颠倒一个栈.例如输入栈{1, 2, 3, 4, 5},1在栈顶.颠倒之后的栈为{5, 4, 3, 2, 1},5处在栈顶. [分析] 乍一看到这道题目,第一反应是把栈里的所有元素逐一p ...
- discuz 学习
一.Discuz首页“今日”“昨日”“欢迎新会员”等文字删除添加 搜索templeta/default/forum/discuz.htm (使用非默认模版的请修改当前使用模版的discuz.htm ...
- Maven类包冲突终极解决方案
本文转自:http://ian.wang/106.htm 举例A依赖于B及C,而B又依赖于X.Y,而C依赖于X.M,则A除引B及C的依赖包下,还会引入X,Y,M的依赖包(一般情况下了,Maven可通过 ...
- 分布式架构从零开始========》【基于Java自身技术实现消息方式的系统间通信】
基于Java自身包实现消息方式的系统间通信的方式有:TCP/IP+BIO,TCP/IP+NIO,UDP/IP+BIO,UDP/IP+NIO.下面就这4种类型一一做个详细的介绍: 一.TCP/IP+BI ...
- Android之Intent深入
Android中的意图包含多种用法,本文主要包括以下内容 显式意图 隐匿意图 要求结果回传的意图 显式意图 :必须指定要激活的组件的完整包名和类名 (应用程序之间耦合在一起) 一般激活自己应用的组件的 ...
- Linux下循环、选择、顺序操作
1.Shell脚本数组操作小结 http://www.jb51.net/article/52382.htm Linux Shell在编程方面比Windows批处理强大很多,无论是在循环.运算. bas ...
- hrbustoj 1161:Leyni(树状数组练习)
LeyniTime Limit: 3000 MS Memory Limit: 65536 KTotal Submit: 260(59 users) Total Accepted: 80(55 user ...