WebRTC


文章链接
https://gitee.com/fakerlove/web-rtc

WebRTC

1. 简介

1.1 概述

WebRTC是一个用于音视频处理和即时通讯的开源库,支持跨平台,于2010年Google将其开源,

众所周知,浏览器本身不支持相互之间直接建立信道进行通信,都是通过服务器进行中转。比如现在有两个客户端,甲和乙,他们俩想要通信,首先需要甲和服务器、乙和服务器之间建立信道。甲给乙发送消息时,甲先将消息发送到服务器上,服务器对甲的消息进行中转,发送到乙处,反过来也是一样。这样甲与乙之间的一次消息要通过两段信道,通信的效率同时受制于这两段信道的带宽。同时这样的信道并不适合数据流的传输,如何建立浏览器之间的点对点传输,一直困扰着开发者。WebRTC应运而生

WebRTC是一个开源项目,旨在使得浏览器能为实时通信(RTC)提供简单的JavaScript接口。说的简单明了一点就是让浏览器提供JS的即时通信接口。这个接口所创立的信道并不是像WebSocket一样,打通一个浏览器与WebSocket服务器之间的通信,而是通过一系列的信令,建立一个浏览器与浏览器之间(peer-to-peer)的信道,这个信道可以发送任何数据,而不需要经过服务器。并且WebRTC通过实现MediaStream,通过浏览器调用设备的摄像头、话筒,使得浏览器之间可以传递音频和视频

1.2 应用场景

  • 音视频直播
  • 游戏,即时通讯,文件传输等
  • 音视频处理(回音消除,降噪等)

商用的直播平台一般会包括信令服务器、流媒体服务器、客户端(共享端和接收端)三部分, 而我们的实验环境可以再简化,只包括流媒体服务器和端户端两部分。

1.3 整体架构

这个是音视频核心技术

webrtc api 指导

WebRTC 整体架构图

(1)紫色部分是Web应用开发者API层

(2)蓝色实线部分是面向浏览器厂商的API层

(3)蓝色虚线部分浏览器厂商可以自定义实现

1.3.1 Transport / Session

传输/会话层:会话层组件采用了libjingle库的部分组件实现,无须使用xmpp/jingle协议。

  • a. RTP Stack协议栈:Real Time Protocol;
  • b. STUN/ICE:可以通过STUN和ICE组件来建立不同类型网络间的呼叫连接;
  • c. Session Management:一个抽象的会话层,提供会话建立和管理功能。该层协议留给应用开发者自定义实现。

1.3.2 VoiceEngine

音频引擎是包含一系列音频多媒体处理的框架,包括从视频采集卡到网络传输端等整个解决方案。

VoiceEngine是WebRTC极具价值的技术之一,是Google收购GIPS公司后开源的。在VoIP上,技术业界领先。

  • iSAC Internet Speech Audio Codec:针对VoIP和音频流的宽带和超宽带音频编解码器,是WebRTC音频引擎的默认的编解码器。

    • 采样频率:16khz,24khz,32khz;(默认为16khz)
    • 自适应速率为10kbit/s ~ 52kbit/;
    • 自适应包大小:30~60ms;
    • 算法延时:frame + 3ms
  • iLBC Internet Low Bitrate Codec:VoIP音频流的窄带语音编解码器。标准由IETF RFC3951和RFC3952定义。

    • 采样频率:8khz;
    • 20ms帧比特率为15.2kbps
    • 30ms帧比特率为13.33kbps
    • NetEQ for Voice 针对音频软件实现的语音信号处理元件。NetEQ算法:自适应抖动控制算法以及语音包丢失隐藏算法。使其能够快速且高解析度地适应不断变化的网络环境,确保音质优美且缓冲延迟最小。是GIPS公司独步天下的技术,能够有效的处理由于网络抖动和语音包丢失时候对语音质量产生的影响。NetEQ 也是WebRTC中一个极具价值的技术,对于提高VoIP质量有明显效果,加以AECNRAGC等模块集成使用,效果更好。
  • Acoustic Echo Canceler (AEC) 回声消除器是一个基于软件的信号处理元件,能实时的去除mic采集到的回声。

  • Noise Reduction (NR) 噪声抑制也是一个基于软件的信号处理元件,用于消除与相关VoIP的某些类型的背景噪声(嘶嘶声,风扇噪音等等… …)

1.3.3 VideoEngine

WebRTC视频处理引擎:VideoEngine是包含一系列视频处理的整体框架,从摄像头采集视频到视频信息网络传输再到视频显示整个完整过程的解决方案。

  • VP8 视频图像编解码器,是WebRTC视频引擎的默认的编解码器。VP8适合实时通信应用场景,因为它主要是针对低延时而设计的编解码器。VPx编解码器是Google收购ON2公司后开源的,VPx现在是WebM项目的一部分,而WebM项目是Google致力于推动的HTML5标准之一。
  • Video Jitter Buffer 视频抖动缓冲器,可以降低由于视频抖动和视频信息包丢失带来的不良影响。
  • Image enhancements 图像质量增强模块:对网络摄像头采集到的图像进行处理,包括明暗度检测、颜色增强、降噪处理等功能,用来提升视频质量。

1.4 相关网址

测试网址

https://appr.tc/

测试网址

https://www.webrtc-experiment.com/

官方英文文档

https://www.w3.org/TR/webrtc/

官方中文文档

https://developer.mozilla.org/zh-CN/docs/Web/API/WebRTC_API

案例介绍

https://github.com/muaz-khan/WebRTC-Experiment

中文社区

https://webrtc.org.cn/

1.5 核心API

1) MediaStream

通过MediaStream的API能够通过设备的摄像头及话筒获得视频、音频的同步流

2) RTCPeerConnection

RTCPeerConnection是WebRTC用于构建点对点之间稳定、高效的流传输的组件

3) RTCDataChannel

RTCDataChannel使得浏览器之间(点对点)建立一个高吞吐量、低延时的信道,用于传输任意数据

2. 获取流-MediaStream

2.1 音视频设备信息获取

  • deviceID:设备id
  • label:设备名字
  • kind:设备种类,
  • goupID:两个设备groupID相同说明是同一个设备
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <script src="jquery.js"></script>
</head>

<body>
<div>
    <label>输入设备</label>
    <select id="audioSource"></select>
</div>
<div>
    <label>输出设备</label>
    <select id="audioOutput"></select>
</div>
<div>
    <label>视屏输入设备</label>
    <select id="videoSource"></select>
</div>

<script>

    (async function () {
        // 获取元素
        let audioSource = document.querySelector("#audioSource");
        let audioOutput = document.querySelector("#audioOutput");
        let videoSource = document.querySelector("#videoSource");
        // 获取媒体信息
        let devices = navigator.mediaDevices.enumerateDevices();
        console.log(devices)

        if (devices == null) {
            alert("对不起媒体信息不支持")
        } else {
            devices.then(gotDevices)
                .catch(handleError);
        }

        function gotDevices(devicesInfo) {

            // 对于所有设备开始获取遍历
            devicesInfo.forEach(function (deviceInfo) {
                console.log(deviceInfo.kind +
                    ": 名称 = " + deviceInfo.label +
                    ": 设备id  = " + deviceInfo.deviceId +
                    ": 设备groupid = " + deviceInfo.groupId);

                var option = document.createElement('option');
                option.html = deviceInfo.label;
                option.value = deviceInfo.deviceId;

                if (deviceInfo.kind == 'audioinput') {
                    audioSource.appendChild(option);
                } else if (deviceInfo.kind == 'audiooutput') {
                    audioOutput.appendChild(option);
                } else if (deviceInfo.kind == 'videoinput') {
                    videoSource.appendChild(option);
                }
            })

        }
    })();


    // 错误的时候发生的信息
    function handleError(err) {
        console.log(err.name + "--" + err.message);
    }
</script>
</body>

</html>

2.2 获取音频

同门可以通过调用navigator.getUserMedia(),这个方法接受三个参数:

  1. 一个约束对象(constraints object),这个后面会单独讲
  2. 一个调用成功的回调函数,如果调用成功,传递给它一个流对象
  3. 一个调用失败的回调函数,如果调用失败,传递给它一个错误对象
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="jquery.js"></script>
    <style>
        .red {
            border: 1px red solid;
            height: 300px;
            width: 400px;
        }

        .blue {
            border: 1px blue solid;
            height: 50px;
            width: 200px;
        }
    </style>
</head>

<body>
<video id="video"  autoplay playsinline  class="red"></video>
<button onclick="video()">开始视屏</button>
<audio id="audio" controls autoplay  class="blue" >
</audio><button onclick="audio()">开始声音</button>
</body>
<script type="text/javascript">
    let stream=null;
    async function video(){
        let constraints = {
            video: true,
            audio: false
        };
      stream = await navigator.mediaDevices.getUserMedia(constraints);
        document.getElementById("video").srcObject=stream;
    }

    async function audio(){
        stream=null;
        let constraints = {
            video: false,
            audio: true
        };

        stream = await navigator.mediaDevices.getUserMedia(constraints);
        document.getElementById("audio").srcObject=stream;
    }


</script>

</html>

2.3 拍照

2.4 获取桌面

为了实现兼容性,所以需要js 来实现这样子的兼容性

https://github.com/webrtchacks/adapter

3. 推流

3.1 NAT

NAT全称network adress translation,即网络地址转换。其存在的意义是将私有的IP地址转化为公有IP地址。 NAT图示

3.2 stun

告诉我你的公网IP地址+端口是什么。搭建STUN服务器很简单

STUN服务器:用户数据报协议UDP简单穿越网络地址转换器NAT,它允许所有的NAT客户终端(如防火墙后边的计算机)与位于局区域网以外的VOIP服务商实现电话通话。

NAT给设备提供了一个IP地址以使用专用局域网,但是这个地址不能在外部使用。由于没有公用地址,WebRTC对等端就无法进行通信。而WebRTC使用 STUN来解决这个问题。

STUN服务器位于公共网络上,并且有一个简单的任务:检查传入请求的IP地址(来自运行在NAT后面的应用程序),并将该地址作为响应发送回去。换句话说,应用程序使用 STUN服务器从公共角度发现其IP:端口。这个过程使得WebRTC对等端为自己活得一个可公开访问的地址,然后通过信令机制将其传递给另一个对等端以建立直接链接。(实际上不同NAT工作方式都有所不同,可能有多个NAT层,但是原理是一样的)。

因为 STUN服务器不需要做太多的工作或者记特别多的东西,所以相对低规格的 STUN服务器就可以处理大量的请求。

根据webrtcstats.com的统计(2013年),大多数WebRTC通话都成功地使用 STUN进行连接,有86%。尽管对于防火墙之后的对等端之间的呼叫以及复杂的NAT配置,成功通话量会更少一些。

3.3 turn

RTCPeerConnection尝试通过UDP建立对等端之间的直接通信。如果失败的话,RTCPeerConnection就会使用TCP进行连接。如果使用TCP还失败的话,可以用 TURN服务器作为后备,在终端之间转发数据。

重申: TURN用于中继对等端之间的音频/视频/数据流,而不是信令数据。

TURN服务器具有公共地址,因此即使对等端位于防火墙或代理之后也可以与其他人联系。 TURN服务器有一个概念上来讲简单的任务—中继数据流—但是与 STUN服务器不同的是,他们会消耗大量的带宽。换句话说, TURN服务器需要更加的强大。

上图显示了 TURN的作用:单纯的 STUN没有成功建立连接,所以每个对等端还需要使用 TURN服务器。

3.4 ICE

3.5 信令

信令是协调通信的过程。为了使WebRTC应用程序能够建立一个“通话”,其客户需要交换以下信息:

  • 会话控制消息用于打开或关闭通信
  • 错误消息
  • 媒体元数据,如编解码器和编解码器设置,带宽和媒体类型
  • 密钥数据,用于建立安全的连接
  • 网络数据,如外界缩减的主机IP地址和端口

RTCPeerConnection是WebRTC应用程序用来创建对等端连接并传输音视频的API。

初始化这个过程RTCPeerConnection有两个工作要做:

  • 确定本地媒体条件,如分辨率和编解码器功能。这是用于提供和应答机制的元数据。
  • 获取应用程序主机的潜在网络地址,成为候选项

一旦确定了本地数据,就必须通过信令机制与远端对等端进行交换。

让我们假设一个场景:Alice正在尝试呼叫Eve。下面是完整的提供/应答机制:

  1. Alice创建一个RTCPeerConnection对象。
  2. Alice使用RTCPeerConnection createOffer()方法产生一个提供(一个SDP会话描述)。
  3. Alice用他的提供调用setLocalDescription()。
  4. Alice将提供串联起来,并使用信令机制将其发送给Eve。
  5. Eve用Alice的提供调用setRemoteDescription(),以便她的RTCPeerConnection知道Alice的设置。
  6. Eve调用createAnswer(),以及success callback函数传入本地会话描述:Eve的应答。
  7. Eve通过调用setLocalDescription()将其应答设置为本地描述。
  8. Eve然后使用信令机制将她的字符串化的应答发回给Alice。
  9. Alice使用setRemoteDescription()将Eve的应答设置为远程会话描述。

Alice和Eve也需要交换网络信息。“查找候选项”这个表达是指使用ICE框架查找网络接口和端口的过程。

​ 1. Alice使用onicecandidate处理器创建一个RTCPeerConnection对象。

​ 2. 处理器在网络候选变得可用时被调用。

​ 3. 在处理器中,Alice通过其信令通道将字符串化的候选数据发送给Eve。

​ 4. 当Eve从Alice那里获得候选消息时,她调用addIceCandidate(),将候选项添加到远端对等描述中。

JSEP支持ICE Candidate Trickling,它允许主叫方在最初的提供之后递增地向被叫方提供候选项,并使被叫方开始在通话中进行操作并建立连接而不用等所有候选项到达。

3.6 coturn

4. 流媒体服务器

4.1 Janus

4.1.1 简介

Janus 是由Meetecho设计和开发的开源、通用的基于SFU架构的WebRTC流媒体服务器,它支持在Linux的服务器或MacOS上的机器进行编译和安装。由于Janus 是使用C语言进行编写的,因此它的性能十分优秀。Janus 的整体架构图如下图所示。

img

Janus整体架构图.png

Janus 主要由三个部分组成,分别是Core、Plugin和Transport,下面是相关模块的介绍:

  1. Core: Janus的核心部分,其作用是处理数据流的转发,以及各种协议的接入,是WebRTC技术的具体实现。
  2. Plugin:Janus插件,Janus的业务管理是按照Plugin的方式管理的,因此开发者可以在Janus中根据自己的需要实现自己的业务插件。实际上,对于一般性的需求,Janus已经有相关的插件。其中,可使用 VideoRoom视频房间插件进行多人音视频互动。
  3. Transport:Janus的信令传输层,Janus并没有限定信令接口使用的信令传输协议,当前支持的协议有HTTP、WebSocket、MQTT、NanoMsg和RabbitMQ。

从整体架构上看,Janus支持众多传输协议,并且采用业务插件架构设计模式。因此,Janus流媒体服务器十分适合多种业务模型或业务经常变化的公司或项目使用。

4.1.2 安装

文档

https://janus.conf.meetecho.com/docs/

4.2 Kurento 服务框架

4.2.1 简介

教程文档

https://doc-kurento.readthedocs.io/en/latest/user/tutorials.html#webrtc-one-to-many-broadcast

Kurento是欧洲的技术团队创建的开源项目(详解访问官方网站),是马德里一所大学的研究机构创建的项目

openvidu 项目,第二次封装,基于kurento

https://openvidu.io/

项目演示-java 版本

git clone https://github.com/Kurento/kurento-tutorial-java.git

模块框架

Kurento模块分为三类:

主要模块 与Kurento Media Server开箱即用合并:

kms-core:Kurento Media Server的主要组件。 kms-elements:Kurento Media Elements的实现(WebRtcEndpoint,PlayerEndpoint等) kms-filters:Kurento过滤器的实现(FaceOverlayFilter,ZBarFilter等) 内置模块 Kurento团队开发的额外模块,用于增强Kurento Media Server的基本功能。到目前为止,有四个内置模块,分别是:

kms-pointerdetector:基于颜色跟踪检测视频流中指针的过滤器。 kms-chroma:过滤器,它在顶层使用颜色范围并使之透明,从而在后面显示另一个图像。 kms-crowddetector:用于检测视频流中人聚集的过滤器。 kms-platedetector:用于检测视频流中的车牌的过滤器。 定制模块 Kurento Media Server的扩展,提供了新的媒体功能。

原理

4.2.2 安装Kurento和穿透服务器

参照文章

https://blog.csdn.net/huahao1989/article/details/106321369

1) 安装Kurento

docker pull kurento/kurento-media-server:6.16
docker run -itd --name kms --network host --restart always -p 8888:8888 kurento/kurento-media-server:6.16

要检查KMS是否已启动并正在侦听连接,请使用以下命令:

curl \
    --include \
    --header "Connection: Upgrade" \
    --header "Upgrade: websocket" \
    --header "Host: 127.0.0.1:8888" \
    --header "Origin: 127.0.0.1" \
    http://127.0.0.1:8888/kurento

您应该得到类似于以下内容的响应:

HTTP/1.1 500 Internal Server Error
Server: WebSocket++/0.7.0

忽略“ Server Error ”消息:这是预期的,它实际上证明KMS已启动并正在侦听连接。

2) STUN与TURN服务器的安装

git clone https://github.com/konoui/kurento-coturn-docker.git
cd kurento-coturn-docker/coturn/
sudo docker build --tag coturn .
sudo docker run --name coturn -p 3478:3478 -p 3478:3478/udp coturn

上面的测试,但是好像因为是3年前的,ubuntun 不支持了

所以换了

coturn 源

http://turnserver.open-sys.org/downloads/

可能出现的问题

WARNING: IPv4 forwarding is disabled. Networking will not work.

#需要做如下配置

解决办法:

vi /etc/sysctl.conf

net.ipv4.ip_forward=1 #添加这段代码

#重启network服务

systemctl restart network && systemctl restart docker

#查看是否修改成功 (备注:返回1,就是成功)

[root@docker-node2 ~]# sysctl net.ipv4.ip_forward net.ipv4.ip_forward = 1

下面的我自己配置stun 和turn

centos8

yum install -y make gcc  gcc-c++ wget openssl-devel libevent libevent-devel
wget http://turnserver.open-sys.org/downloads/v4.5.2/turnserver-4.5.2.tar.gz
tar -zxvf  turnserver-4.5.2.tar.gz
cd turnserver-4.5.2
./configure --prefix=/usr/local/turnserver  # 指定安装的目录
make && make install
echo  turnserver_home=/usr/local/turnserver >> ~/.bashrc
echo "PATH=\$PATH:\$turnserver_home/bin" >> ~/.bashrc
source ~/.bashrc
echo listening-port=3478 >> /usr/local/turnserver/share/examples/turnserver/etc/turnserver.conf # 监听的端口
echo listening-ip=10.206.0.5  >> /usr/local/turnserver/share/examples/turnserver/etc/turnserver.conf # 监听的内网IP
echo external-ip=119.45.206.16 >> /usr/local/turnserver/share/examples/turnserver/etc/turnserver.conf # 监听的外网IP
echo user=user:123456  >> /usr/local/turnserver/share/examples/turnserver/etc/turnserver.conf # 设置账号密码
cp /usr/local/turnserver/etc/turnserver.conf.default /etc/turnserver.conf
turnserver -v -r 119.45.206.16 -a -o -c /usr/local/turnserver/share/examples/turnserver/etc/turnserver.conf #指定配置文件启动服务

3) 测试 turn 和stun 有效性

https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/

测试stun有效性(出现两个地址加”done“才为有效):

测试turn有效性(出现三个地址加"done"才为有效): turn测试

4) kurento设置打洞服务器地址

docker exec -it kms /bin/bash  #进入镜像
apt-get update #安装vim
apt-get install vim -y
cd /etc/kurento/modules/kurento/ #进入配置文件夹
vim WebRtcEndpoint.conf.ini #编辑配置文件

若要配置打洞服务器,配置文件应该改成这样的: 修改WebRtcEndpoint.conf.ini

stunServerAddress=xx.xx.xx.xx
stunServerPort=pp
turnURL=username:userpwd@xx.xx.xx.xx:pp?transport=tcp

我的就是

stunServerAddress=119.45.206.16
stunServerPort=3478
turnURL=user:123456@119.45.206.16:3478?transport=tcp

5) 重启

docker restart kms

6) kurento-hello-world 项目展示

在kurento-hello-wrold项目文件夹中,执行以下操作:

 cd /src/main/resources/static/js/
 vim index.js

在函数function uiStart()里,增加一个叫iceservers的变量,格式如下:

 var iceservers={
    "iceServers":[
        {
          urls:"stun:139.198.123.138:3478"
        },
        {
          urls:["turn:139.198.123.138:3478"],
            username:"kurento",
            credential: "kurento"
        }
    ]
  }

再修改底下的options变量:

 const options = {
    localVideo: uiLocalVideo,
    remoteVideo: uiRemoteVideo,
    mediaConstraints: { audio: true, video: true },
    onicecandidate: (candidate) => sendMessage({
      id: 'ADD_ICE_CANDIDATE',
      candidate: candidate,
    }),
      configuration: iceservers //修改在这里,增加了一个configuration的key
  };

4.3 SRS框架

4.3.1 简介

SRS是一个采用MIT协议授权的国产的简单的RTMP/HLS 直播服务器。最新版还支持FLV模式,同时具备了RTMP的实时性,以及HLS中属于HTTP协议对各种网络环境高度适应性,并且支持更多播放器。它的功能与nginx-rtmp-module类似, 可以实现RTMP/HLS的分发。

Github地址:github.com/ossrs/srs

4.3.2 安装

centos 8

yum install -y autoconf automake cmake freetype-devel gcc gcc-c++ git libtool make mercurial nasm pkgconfig zlib-devel
git clone https://github.com/ossrs/srs
cd srs/trunk
./configure && make

4.4 licode 框架

4.5 rtmp+nagix 框架

4.6 HLS 流媒体

4.7 Jitsi

Jitsi是开源的视频会议系统,可以实现在线视频会议,文档共享和即时消息的分享。它支持网络视频会议,使用SFU模式实现视频路由器功能。开发语言是Java。它支持SIP帐号注册电话呼叫。不仅支持单机本地安装方式,还支持云平台安装。

官网地址:jitsi.org/

4.8 声网Agora

声网提供了从编解码到端到端传输的全套服务,开发者可以接入上文所述的音视频前后处理的开源项目,配合使用声网SDK可以建立高质量的实时音视频应用。在Web端,Agora Web SDK可以帮助WebRTC开发者解决服务端传输中会遇到的卡顿、延时、回声、多人视频不稳定等问题。同时,声网SDK还对多个系统平台的应用提供实时音视频通讯服务。

声网在Github上有许多可供开发者参考、实践的demo源码,覆盖了从网页端、iOS到Android平台,以及音视频直播、游戏连麦、企业会议、AR、直播答题、小程序等多种实时互动应用场景。

Github地址:github.com/AgoraIO-Com…

5. 音视频处理 ffmepg

上次编辑于: 2021/10/12 下午4:55:55
贡献者: fakerlove1