一、基本原理
WebRTC的线程功能由Thread
类提供。Thread
继承于消息队列MessageQueue
,这样WebRTC中的每个线程都有了自己的消息循环,外部可以向该线程的消息循环Post消息Message
,然后该线程轮询从消息循环Get
到消息后处理消息。
UML如下:
在介绍WebRTC的线程模型之前,先介绍webrtc线程模型中用到的几个简单、常用的模块或函数。webrtc是一个代码宝库,且它本身跨平台(windows,linux,ios,android),不管是哪个平台上面开发,都可以从中学习到很多有用的东西。
platform_thread.cc
void SetCurrentThreadName(const char* name) {
#if defined(WEBRTC_WIN)
struct {
DWORD dwType;
LPCSTR szName;
DWORD dwThreadID;
DWORD dwFlags;
} threadname_info = {0x1000, name, static_cast<DWORD>(-1), 0};
__try {
::RaiseException(0x406D1388, 0, sizeof(threadname_info) / sizeof(DWORD),
reinterpret_cast<ULONG_PTR*>(&threadname_info));
} __except (EXCEPTION_EXECUTE_HANDLER) {
}
#elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID)
prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name));
#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
pthread_setname_np(name);
#endif
}
WebRTC没有提供这方面的接口,我们可以通过修改sdp信息来实现音视频流的传输带宽控制,sdp格式参考之前的文章:《WebRTC – SDP格式解析》
实现原理比较简单,就是分别在sdp的audio和video媒体部分加入b=AS:信息,可以参考下面的代码实现:
webrtc视频处理流水线的第一个环节就是获取视频数据,视频源可以有多种来源,以摄像头采集为例,每个平台往往又提供不同的接口,本文打算以android平台camera采集为例,分析一下webrtc视频采集和分发流程。
如下所示,webrtc针对视频采集对外主要提供的是VideoCapturer接口,实现类有ScreenCapturerAndroid、FileVideoCapturer和CameraCapturer,分别表示屏幕、文件、摄像头三种不同的视频来源,因为android系统先后提供了camera1.0和camera2.0接口,因此CameraCapturer又用Camera1Capturer和Camera2Capturer两个子类分别表示。
createEndpoint
当我们打开index.js 后,看到的第一个重要的 API就是createEndpoint函数了。我们就以这个 API为开头,一步一步的分析一下medooze的运行机制。
createEndpoint
函数定义在 medooze-media-server
库中的 lib 目录下的 MediaServer.js 中。代码如下:
/**
* Create a new endpoint object
* @memberof MediaServer
* @param {String} ip - External IP address of server, to be used when announcing the local ICE candidate
* @returns {Endpoint} The new created endpoing
*/
MediaServer.createEndpoint = function(ip)
{
//Cretate new rtp endpoint
return new Endpoint(ip);
};
它的作用是公布 ICE 本地候选者,当做一个终端。
顺着这个函数我们继续往下看,在该函数中会创建一个 Endpoint 对象。
新的一年又开始了,我打算先把年前看的webrtc的流程先梳理下。省的后面忙于其他事情,时间一长又忘记了。还是尽量按webrtc开篇中的顺利来梳理吧。在之前的两篇博客中基本完成了webrtc视频采集的分析,因此本篇开始就分析一下webrtc视频编码相关的内容,准备分三篇来介绍。
废话少说,如下图所示,主要是从初始化流程和编码流程来分析webrtc视频编码模块,step1~step31是初始化流程,主要是创建相关的对象,step32~step49是编码流程,本篇文章打算先分析一下初始化流程。
在webrtc源码分析之视频编码之一分析了视频编码初始化流程,接下来分析一下视频编码流程,
如下图所示,视频编码流程是从VideoBroadcaster回调VideoStreamEncoder的OnFrame开始的。
http://blog.sina.com.cn/s/blog_40d608bb01010n73.html
http://www.cnblogs.com/fangkm/p/4370492.html
http://mojiapp.cn/a/yuanmashili/2015/0708/542.html
http://max.book118.com/html/2015/1228/32140782.shtm
https://www.slideshare.net/libfetion/webrtc
https://chromium.googlesource.com/external/webrtc/+/master
https://source.codeaurora.org/quic/lc/external/webrtc/tree/webrtc?h=chromium.org/master
1、配置webrtc抗丢包方法为FEC Only。
启动配置的时候,可以选NACK Only 或FEC Only 或NACK、FEC混合模式。
为了测试webrtc的X264 FEC抗丢包性能,在启动配置的时候,选择FEC Only模式。
配置方法请参考:VCMLossProtectionLogic::SetMethod
2、使用Network Emulator Client配置网络丢包模型为随机丢包,随机丢包概率为5%
1、建立X264编码视频连接,查看视频清晰度及流畅性。
2、建立VP8编码视频连接,查看视频清晰度及流畅性。
视频无长时间卡顿,至少X264编解码效果要与VP8一致。
一、延时计算原理
1)jitter延时计算公式
JitterDelay由两部分延迟造成:传输大帧引起的延迟和网络噪声引起的延迟。计算公式如下:
JitterDelay = theta[0] * (MaxFS – AvgFS) + [noiseStdDevs * sqrt(varNoise) – noiseStdDevOffset]
该公式详细推导过程,请参见大牛的文章《WebRTC视频接收缓冲区基于KalmanFilter的延迟模型》
theta[0]:信道传输速率的倒数
MaxFS:自会话开始以来所收到的最大帧大小
AvgFS:表示平均帧大小
noiseStdDevs:表示噪声系数2.33
varNoise:表示噪声方差
noiseStdDevOffset:是噪声扣除常数30
2)jitter延时更新流程
Medooze作为WebRTC流媒体服务器,实现了将WebRTC/RTP流的录制为文件,以及将文件转换为RTP流。
mp4recorder.cpp 包含2个类:MP4Track和MP4Recorder,下面一个个分析。
MP4录制功能由MP4V2库提供支持。
我们首先了解一下 RTP H.264 相关的 RFC,下面的内容是对两篇 RFC 的总结:RTP: A Transport Protocol for Real-Time Applications, RTP Payload Format for H.264 Video。
RTP 包结构
包头有固定 12 个字节部分,以及可选的 csrc
和 ext
数据(在为 janus-pp-rec 增加视频旋正功能一文中有更详细的介绍):
在实时流媒体系统中,jitterbuffer是在解码端,起到如下几个作用:
对于1,2点比较简单。核心的是去抖动,去抖动实现的目标就是使视频能平滑播放,不因为抖动忽快忽慢。
简单的视频jitterbuffer可以只针对rtp包进行处理,只对rtp进行排序,去重。并不处理视频帧。如下图
这种简单的实现并不关心视频帧是否有错,是否可以解码。视频帧间是否可以持续解码。(针对视频帧内的RTP包,如果经过排序和去重,可以认为是可解码的)。这些全部放在解码模块去做。
当然这种形式的Jitterbuffer无法处理抖动。因为对视频帧而言,抖动的处理是针对帧间的,而不是RTP包间的。把它叫做rtp buffer应该更合适些。
webrtc中的jitterBuffer也是QOS机制中的核心,它会估算抖动,丢包,决定是否通过Nack来重传。这里我先忽略与QOS相关的一些逻辑,先看看jitterBuffer中的一些核心的基础功能。
webrtc中的jitterBuffer是基于视频帧实现,在RTP的接收端存入jitterbuffer的是rtp包,在解码端取出的是视频帧。