WebRTC视频数据流程分析( 三 )


 
VideoTrack是WebRTC中比较重要的一个概念,音频、视频等媒体从概念上来说其实就是一个Track,我们通常会添加或从远端接收一个Track 。另外,IOS的流程与上图中流程有一些区别,其视频预览不是从VideoBroadcaster接收每一帧的数据然后进行渲染,而是其系统存在接口可以将采集和预览两个系统类关联并自动实现渲染 。但其实我们也可以像RemoteRenderer类一样,获取到一帧个数据后再进行渲染,用RemoteRenderer类添加到采集端的VideoBroadcaster中进行渲染 。
在非iOS的平台上,本地预览以及远端视频的渲染其实都是通过一个类来实现的 。

WebRTC视频数据流程分析

文章插图
完整视频数据流程(调用栈)
图中详细的列出了视频数据的整体采集、处理、传输相关步骤 。简单来看,就是从上到下到最底部网络层,再由下到上最终到渲染的整体流程 。所有平台的视频数据流程基本上都是大同小异的,区别只在于采集、编解码和渲染的实现不同,其余的流程基本是一致的 。
采集:
WebRTC视频数据流程分析

文章插图
 
首先RTCCameraVideoCapture会从系统数据回调,接收到实际的视频数据,交给VideoSource通过_nativeVideoSource将数据传递到C++这一层,最后提交AdaptedVideoTrackSource进行一些如旋转、裁剪之类的操作 。
编码:
WebRTC视频数据流程分析

文章插图
 
视频数据经过AdaptedVideoTrackSource层之后,就可以通过broadcaster_进行分发 。在安卓或者linux中可能会有多个分支,一个预览一个编码,这里我们以编码为主干进行分析 。Sink实际上就是数据的消费者,通过VideoStreamEncoder来实现编码,但其只是概念上的编码,最终实际编码还是调用系统相关的类,因此最终会回到ObjectiveC层,通过一些调用到达RTCVideoEncoderH.264,再调用VideotoolbBox接口,实现H.264的硬件编码 。编码完成之后会实现系统的回调,再将编码后的数据交回给C++层,即VideoStreamEncoder的OnEncodedImage回调函数中,表示一帧视频数据已经完成编码 。
 
发送:
WebRTC视频数据流程分析

文章插图
 
VideoSendStream表示要发送的视频流,通过rtp_vVideo_sSender_进行RTP打包处理,再接下来就是需要进行的RTP封包和网络传输 。假设通过网络传输数据已经到达RtpVideoStreamReceiver,我们可以看到左右两边的sender和receiver在类以及函数的命名上会有一些对称的地方 。RtpVideoStreamReceiver接收到RTP,并且已经完成解包以及其它的网络乱序、错误重传等处理,获得一帧完整可解码的帧,然后就会调用解码回调,送到VideoReceiveStream中进行解码操作,在这里会调用vVideo_rReceiver_的Decode函数 。
解码:
WebRTC视频数据流程分析

文章插图
 
vVideo_rReceiver_的Decode函数其实也是概念上的解码,叫VCMGenericDecoder,最终也会调到平台相关的ObjectiveC实现的视频硬解,即RTCVideoDecoderH.264,也是调用VideoToolBox进行解码,解码后通过DecodedFrameCallback交还给C++这一层 。
WebRTC视频数据流程分析

文章插图
 
解码后的数据最终还是到达了VideoStreamdDecoder,交给了incoming_vVideo_sStream_ 。这里会存在一个视频帧的队列,解码和编码不太一样,编码是采集到一帧视频帧,编码完成后立刻发送,但解码完成后却不会立刻进行渲染,而是需要一定的缓冲,以避免由于抖动而导致卡顿 。所以视频数据解码完成后会首先放入队列中,等待渲染模块控制节奏,需要时再获取数据 。
WebRTC视频数据流程分析

文章插图
 
渲染:
 
获取到视频数据后,会通过Broadcaster将数据交给sink,sink在iOS上具体是通过RTCMTLVideoView对数据进行渲染,MTL是调用iOS的Metal接口进行视频渲染 。
 
其实图中只是视频流程中调用栈的总结,书中有一章节的内容总结了视频数据流程的更多示例代码的分析以及讲解 。
实战:客户端视频录制 
WebRTC视频数据流程分析

文章插图
 
首先要明确需求:1. 推流和收流都需要,即发送的数据需要录制成文件并且接收到的内容也要录制成文件;2. 其次是不希望做额外的编码,因为通常接收或者发送的视频都是已经处理(编码)好的,额外的编码会造成资源浪费 。3.在不需要额外编码的情况下,我们只需要调用FFmpeg把编码后的数据存储到文件内即可 。4.我们应该从哪里拿数据?


推荐阅读