草庐IT

一文带你了解webrtc基本原理(动手实现1v1视频通话)

王清培 2023-10-01 原文

webrtc (Web Real-Time Communications) 是一个实时通讯技术,也是实时音视频技术的标准和框架。
大白话讲,webrtc是一个集大成的实时音视频技术集,包含了各种客户端api、音视频编/解码lib、流媒体传输协议、回声消除、安全传输等。
对于开发者来说可以借助webrtc非常方便的实现低延时视频通话能力。
现在主流的直播系统、会议系统基本都是基于webrtc来实现。

一、webrtc 三种架构

我们先大概了解下webrtc的几种架构及各自适用场景。

【Mesh】

Mesh架构,需要所有参与连接的peer建立与所有其他peer的媒体连接。
该架构需要n-1个上下行,以此带来的带宽消耗(流量)、编/解码消耗(手机性能)成线性增长。
该架构只能适用3-4个人的小型会议场景。

【MCU】

所有本房间的peer将本地媒体流推到远程媒体服务器,由媒体服务器进行混流,然后再推到所有连接的peer端。
该架构的优点就是只需要1路上下行,随着peer人数不断增加,依然不会对用户造成带宽、手机性能影响。
该架构将压力转嫁到服务端,由专用媒体服务器来完成混流,转推等功能。

【SFU】

相对于MCU来说SFU只做转发,媒体服务器压力有限。与mesh架构相比,只需要n-1个下行,1个上行。
在大规模的场合该架构具有伸缩性。

二、实现 1v1 视频通话

废话不多说,动手实践下。
(麻雀虽小,五脏俱全。通过实现1v1的功能,来整体了解下webrtc协议的原理。)
github:https://github.com/Plen-wang/webrtc-demo-1v1

由于是私有证书问题,chrome会有安全提示。(demo地址暂时还能用 -_- )

有两个方法可以试下。

第一个方法,手动设置一个类似不安全白名单列表,然后重启浏览器。

chrome://flags/#unsafely-treat-insecure-origin-as-secure

如果不行,我们试下第二个方法肯定可以。

点击空白页输入 thisisunsafe字符。

动手之前,我们先简单了解下webrtc的连接的大致流程和涉及的相关技术点。

【WebRTC P2P】

【NAT穿透】
peer基本都在内网,需要通过nat穿透技术来与peer建立连接。
根据nat的拓扑情况大致分为如下几种:完全锥形、IP锥形、端口锥形、对称形。
stun\turn协议:stun协议用来拿到peer公网ip,turn用来做relay数据转发。

【SDP】
sdp是会话描述协议。
是媒体协商时使用,用于将本地支持的媒体(编解码等)信息、candidate(连接候选者)信息打包发送到信令服务器。
sdp的交换是通过中间服务器(信令服务器)来完成的。

【ICE】
ICE是一个不断尝试连接的协议,不同的网络情况下ICE大概会尝试如下几种方式来建立通讯通道。
host(peers都在内网)、 srflx(nat穿透)、prflx(nat穿透-Full Cone)、relay(中继)

【服务端】
在整个连接生命周期中都是需要服务端参与。参与webrtc协作的服务端大概分为这几种类型。
stun/turn服务器(p2p穿透)、信令服务器、媒体服务器(媒体信息处理)、业务服务器(可选)

整体流程大致如下。

(上述技术点较多,感兴趣可以自行查询相关资料)

【部署STUN\TURN服务器】

为了支持1v1公网访问,我们需要搭建一个stun/turn服务器。
这里我们使用 Coturn 开源组件,coturn的镜像有很多,可自行选择。
(注意准备coturn配置文件时,记得设置用户名和密码。)

    docker run -d  --rm --name turn-server --network=host   \
               -v ${pwd}/turnserver.conf:/etc/coturn/turnserver.conf \
           instrumentisto/coturn

部署好之后可以通过ICE测试工具测试下
https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice

    stun:1.15.11.173:3478?transport=tcp
    turn:1.15.11.173:3478?transport=tcp:user:pwd

如果正常返回了ICE尝试的连接类型,说明部署没有问题。

【实现信令服务器与客户端代码】

我们采用golang来实现一个简单的信令服务器,使用开源组件go-socket。
同时还需要实现一个web客户端。
demo代码就不贴到文章里了,放在github上。整体代码比较简单,感兴趣可以看下。
git@github.com:Plen-wang/webrtc-demo-1v1.git

【部署信令服务器】
当在本地debug的差不多了,我们把信令服务器打个镜像发到云主机上。
(如果部署本demo,可以直接使用此镜像。)

    docker push wangqingpei/rtc-signal-server:latest
    docker run --name signal-server -d -p:8080:8080 wangqingpei/rtc-signal-server
    

【部署web服务器】
部署好信令服务器之后,我们把静态文件放到web服务器里,直接使用nginx镜像部署非常简单。

    docker run -d -p 80:80 -p 443:443  --rm --name webrtc-nginx   \
    -v /data/rtc-nginx.conf:/etc/nginx/nginx.conf  \
    -v /data/pem/server.key:/etc/nginx/server.key  \
    -v /data/pem/server.pem:/etc/nginx/server.pem  \
    -v /data/rtc-static-file:/usr/share/nginx/html  nginx

部署前,记得修改js里的stun服务器地址。

//创建RTCPeerConnection对象
function createRTCPeerConnection() {
    try {
	    
	    const configuration = {'iceServers': [{'urls': 'stun:1.15.11.173:3478?transport=tcp'}]}
	    
        rtcConnObject = new RTCPeerConnection(configuration);
        rtcConnObject.onicecandidate = handleRtcICECandidate;//ice 交互
        rtcConnObject.onaddstream = handleRtcAddStream;//远程stream加入
        rtcConnObject.onremovestream = handleRtcRemoveStream;//远程stream移除
        rtcConnObject.addStream(localStreamObject);//添加本地stream
        console.log("create local RTCPeerConnection object ok.");
    } catch (e) {
        console.error("create RTCPeerConnection err.", e);
    }
}

两边peer就可以借助stun服务器拿到公网ip实现nat穿透。

三、实现MCU/SFU 多人通话

MCU/SFU架构需要 专用媒体服务器 参与。

【媒体服务器选择】

专用媒体服务器有 OWT(open webrtc toolkit)、TWS(Kurento Media Server)等重量级的开源产品。

这两款开源框架都支持MCU、SFU架构功能。

我们选择OWT捣鼓下。

先看下部署起来的效果,默认MCU模式。

红框部分是服务端混流之后的效果。

【部署OWT】

注意,owt-server-4.3镜像与最新版chrome有兼容性问题,会报错 Empty candidate 错误。

我们直接使用5.0的镜像部署。

docker run -d --name owt-demo --network host lmshao/owt-server

由于该镜像是使用默认配置打的,启动后手动进入容器修改下相关配置,换成你云主机的公网ip,然后重启服务。

配置文件路径

vi dist/webrtc_agent/agent.toml

配置项,这里修改成你的公网ip

network_interfaces = [{name = "eth0", replaced_ip_address = "1.116.175.232"}]  # default: []

stun服务器可选

stunport = 3478 #default: 0
stunserver = "1.15.11.173" #default: ""

然后修改下portal.toml文件,文件路径。

vi dist/portal/portal.toml

修改成公网ip

ip_address = "1.116.175.232" #default: ""

重启下相关服务

./bin.restart-all.sh 

注意启动日志里有一个id、key,这是用来进入管理页面用的。(没错,owt提供了后台管理页面 -_-)

superServiceId: xxx
superServiceKey: xxx

sampleServiceId: xxx
sampleServiceKey: xxx

默认3004端口下是mcu模式,连线的人多了就会明显卡顿(看服务器配置)。

我们切到SFU模式试下流畅度和服务器负载情况。

通过 ?forward=true 参数控制

https://1.116.175.232:3004/?forward=true

OWT还配有管理后台用于控制媒体服务器的相关参数。

OWT还是比较强大的,有兴趣可以研究研究。

参考资料:
github.com/googollee/go-socket.io
《WebRTC技术详解:从0到1构建多人视频会议系统》
《WebRTC音视频实时互动技术:原理、实战与源码分析》
《FFmpeg 音视频开发基础与实战》

有关一文带你了解webrtc基本原理(动手实现1v1视频通话)的更多相关文章

  1. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

    我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

  2. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  3. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  4. ruby-on-rails - 使用 HTTParty 的非常基本的 Rails 4.1 API 调用 - 2

    Rails相对较新。我正在尝试调用一个API,它应该向我返回一个唯一的URL。我的应用程序中捆绑了HTTParty。我已经创建了一个UniqueNumberController,并且我已经阅读了几个HTTParty指南,直到我想要什么,但也许我只是有点迷路,真的不知道该怎么做。基本上,我需要做的就是调用API,获取它返回的URL,然后将该URL插入到用户的数据库中。谁能给我指出正确的方向或与我分享一些代码? 最佳答案 假设API为JSON格式并返回如下数据:{"url":"http://example.com/unique-url"

  5. ruby - 了解在 Ruby 中与 lambda 一起使用的 inject 行为 - 2

    我经常将预配置的lambda插入可枚举的方法中,例如“map”、“select”等。但是“注入(inject)”的行为似乎有所不同。例如与mult4=lambda{|item|item*4}然后(5..10).map&mult4给我[20,24,28,32,36,40]但是,如果我制作一个2参数lambda用于像这样的注入(inject),multL=lambda{|product,n|product*n}我想说(5..10).inject(2)&multL因为“inject”有一个可选的单个初始值参数,但这给了我......irb(main):027:0>(5..10).inject

  6. ruby-on-rails - 如何测试自己对 Ruby/ROR 的了解? - 2

    是否有self验证的问题列表。看着那个,我可以确定我知道。我应该复习一下。在学习的过程中,我列了一个这样的list,但它只包含我在某处听说过的项目。我需要一段时间才能找到新的东西。 最佳答案 以下是针对ruby​​和Rails的一些测试列表。证书名称:RubyonRails谁提供:oDeskIncorporation认证费用:免费网站:https://www.odesk.com/tests/985?pos=0证书名称:RubyonRails提供者:Techgig.com(TimesBusinessSolutionsLimited(T

  7. ruby-on-rails - 了解 "attribute_will_change!"方法 - 2

    我想覆盖store_accessor的getter。可以查到here.代码在这里:#Fileactiverecord/lib/active_record/store.rb,line74defstore_accessor(store_attribute,*keys)keys=keys.flatten_store_accessors_module.module_evaldokeys.eachdo|key|define_method("#{key}=")do|value|write_store_attribute(store_attribute,key,value)enddefine_met

  8. ruby-on-rails - Rails 基本 Base64 身份验证 - 2

    我正在尝试复制此GETcurl请求:curl-D--XGET-H"Authorization:BasicdGVzdEB0YXByZXNlYXJjaC5jb206NGMzMTg2Mjg4YWUyM2ZkOTY2MWNiNWRmY2NlMTkzMGU="-H"Content-Type:application/json"http://staging.example.com/api/v1/campaigns在Ruby中,通过电子邮件+apikey生成身份验证:auth="Basic"+Base64::encode64("test@example.com:4c3186288ae23fd9661c

  9. ruby - 我怎样才能更好地了解/了解更多关于 Ruby 的知识? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我最近开始学习Ruby,这是我的第一门编程语言。我对语法感到满意,并且我已经完成了许多只教授相同基础知识的教程。我已经写了一些小程序(包括我自己的数组排序方法,在有人告诉我谷歌“冒泡排序”之前我认为它非常聪明),但我觉得我需要尝试更大更难的东西来理解更多关于Ruby.关于如何执行此操作的任何想法?

  10. Simulink方法总结和避坑指南(一)——Simulink入门与基本调试方法 - 2

    文章目录一、项目场景二、基本模块原理与调试方法分析——信源部分:三、信号处理部分和显示部分:四、基本的通信链路搭建:四、特殊模块:interpretedMATLABfunction:五、总结和坑点提醒一、项目场景  最近一个任务是使用simulink搭建一个MIMO串扰消除的链路,并用实际收到的数据进行测试,在搭建的过程中也遇到了不少的问题(当然这比vivado里面的debug好不知道多少倍)。准备趁着这个机会,先以一个很基本的通信链路对simulink基础和相关的debug方法进行总结。  在本篇中,主要记录simulink的基本原理和基本的SISO通信传输链路(QPSK方式),计划在下篇记

随机推荐