网络延迟是一种比较常见的情况。在本地网页上,我们可以建立多个RTCPeerConnection,增加转发次数,来模拟出网络延迟的效果。

建立通话后,再往后面增加本地转发节点。

准备

页面准备,方便我们后面调试

<div id="container">
<h1><a href="https://an.rustfisher.com/webrtc/peerconnection/upgrade-to-video" title="">WebRTC插入多个转发节点</a></h1> <div id="videos">
<video id="video1" playsinline autoplay muted></video>
<video id="video2" playsinline autoplay></video>
</div> <section><input type="checkbox" id="audio"><label for="audio">包含音频(>= Chrome 49)</label></section> <div id="buttons">
<button id="start">开始</button>
<button id="call" disabled>呼叫</button>
<button id="insertRelay" disabled>插入转发</button>
<button id="hangup" disabled>挂断</button>
</div>
<div id="status"></div>
</div> <script src="../../src/js/adapter-2021.js"></script>
<script src="js/connection2.js"></script>
<script src="js/main.js"></script>

放上2个video和几个button。引入WebRTC adapter和控制脚本。

如果要使用官方的适配器adapter,按下边的地址来引入

<!-- <script src="../../src/js/adapter-2021.js"></script> -->
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

控制

分为connection2.jsmain.js

connection2.js包含新建节点的逻辑。main.js控制主流程。

connection2.js

这里能新建2个RTCPeerConnection,建立新的连接。

function doNothing() { }

function errFunc(context) {
return function (error) {
trace('报错 ' + context + ': ' + error.toString);
};
} // 新建2个节点并建立连接
function Connection2(stream, handler) {
let servers = null;
let pc1 = new RTCPeerConnection(servers);
let pc2 = new RTCPeerConnection(servers); pc1.addStream(stream);
pc1.onicecandidate = function (event) {
if (event.candidate) {
pc2.addIceCandidate(new RTCIceCandidate(event.candidate),
doNothing, errFunc('AddIceCandidate'));
}
};
pc2.onicecandidate = function (event) {
if (event.candidate) {
pc1.addIceCandidate(new RTCIceCandidate(event.candidate),
doNothing, errFunc('AddIceCandidate'));
}
};
pc2.onaddstream = function (e) {
handler(e.stream);
};
pc1.createOffer(function (desc) {
pc1.setLocalDescription(desc);
pc2.setRemoteDescription(desc);
pc2.createAnswer(function (desc2) {
pc2.setLocalDescription(desc2);
pc1.setRemoteDescription(desc2);
}, errFunc('pc2.createAnswer'));
}, errFunc('pc1.createOffer'));
this.pc1 = pc1;
this.pc2 = pc2;
} Connection2.prototype.close = function () {
this.pc1.close();
this.pc2.close();
};

Connection2(stream, handler)新建pc1和pc2,将传入的stream作为数据源交给pc1。

随后在pc1和pc2之间建立连接。pc2接到数据流后再交回给handler

main.js

先拿到ui

'use strict';

// 获取ui
const video1 = document.querySelector('video#video1');
const video2 = document.querySelector('video#video2');
const statusDiv = document.querySelector('div#status');
const audioCheckbox = document.querySelector('input#audio');
const startBtn = document.querySelector('button#start');
const callBtn = document.querySelector('button#call');
const insertRelayBtn = document.querySelector('button#insertRelay');
const hangupBtn = document.querySelector('button#hangup');

记录一些变量

const connectionList = []; // 连接点
let localStream;
let remoteStream;

启动,获取数据流。可以选择是否带音频流。拿到数据流后,交给video1显示,并且记录为localStream

function gotStream(stream) {
video1.srcObject = stream;
localStream = stream;
callBtn.disabled = false;
} function start() {
startBtn.disabled = true;
const options = audioCheckbox.checked ? { audio: true, video: true } : { audio: false, video: true };
navigator.mediaDevices.getUserMedia(options)
.then(gotStream)
.catch(function (e) {
alert(`获取媒体失败 ${e}`);
});
}

发起呼叫

function gotremoteStream(stream) {
remoteStream = stream;
video2.srcObject = stream;
console.log('接收到了传输后的数据流');
statusDiv.textContent = `当前节点数: ${connectionList.length * 2}`;
insertRelayBtn.disabled = false;
} function call() {
callBtn.disabled = true;
insertRelayBtn.disabled = false;
hangupBtn.disabled = false;
console.log('呼叫!');
connectionList.push(new Connection2(localStream, gotremoteStream));
}

呼叫的方法是call(),使用Connection2来建立第一级连接。

连接的记录存放在connectionList

插入转发和call有点类似,都使用了Connection2。但是输入的是remoteStream

function insertRelay() {
connectionList.push(new Connection2(remoteStream, gotremoteStream));
insertRelayBtn.disabled = true;
}

多次调用insertRelay(),可以模拟出多层转发的情况。转发次数多了,视频延迟得也就越明显。

挂断/结束方法hangup()

function hangup() {
console.log('挂断');
while (connectionList.length > 0) {
const pipe = connectionList.pop();
pipe.close();
}
insertRelayBtn.disabled = true;
hangupBtn.disabled = true;
callBtn.disabled = false;
}

connectionList里面的连接全部拿出来结束掉。

如果数量比较多,结束耗时也会比较长。

效果预览

效果预览请参考WebRTC插入多个转发节点

可以尝试多点击几次插入转发按钮。观察视频的延迟情况。

小结

本地网页可以通过增加节点的办法,模拟出视频传输延迟的效果。

Connection2(stream, handler)里的代码写的非常简洁,也利于了解WebRTC传输建立的过程

从这个例子我们也可以看出,实际工程中要尽量减少中间节点。并且要优先选择质量高的节点。

本文链接

WebRTC本地插入多个转发节点的更多相关文章

  1. 【译】SSH隧道:本地和远程端口转发

    本文是:SSH Tunnel - Local and Remote Port Forwarding Explained With Examples 的译文 有两种方法可以创建SSH隧道,本地和远程端口 ...

  2. WebRTC本地分享屏幕,录制屏幕

    WebRTC有分享屏幕的功能.使用的是getDisplayMedia方法.用户同意分享屏幕后,可以拿到视频流. 再结合MediaRecorder和Blob,把视频流数据存下来,就能得到录制屏幕的视频. ...

  3. WebRTC本地选择codec(web本地模拟)

    视频编码后,再进行发送.WebRTC建立视频连接前,可以选择codec.一般来说支持多种codec,以VP8和H264为代表. Codec: 编码译码器,编解码器 示例代码 写一个示例,用户可以在发送 ...

  4. Android studio git 本地仓库和远程仓库节点对比

    1.初始状态 2.本地修改文件,然后commit 3.本地再次修改文件,然后commit 4.本地push 从上图可以看出,push完成后,本地仓库的节点和远程仓库的节点是一样的.

  5. Socket 接收本地短连接并转发为长连接 多线程

    import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io. ...

  6. JMeter扩展Java请求实现WebRTC本地音视频推流压测脚本

    WebRTC是Web Real-Time Communication缩写,指网页即时通讯,是一个支持Web浏览器进行实时语音或视频对话的API,实现了基于网页的视频会议,比如声网的Agora Web ...

  7. DOM操作插入新的子节点

    appendChid.insertBefore首先这两个方法都是添加子节点. append(追加),appendChid:给父节点的子节点末尾添加子节点. insertBefore(newNode, ...

  8. 如何用nginx在本地把9000端口转发到80端口上

    起因看到一个用java写的轻博客,于是就兴致冲冲的试用一下.由于是lnmp的环境,Nginx占用了80端口,新博客只能用其他的端口,这里选择了9000端口,本地测试没问题.总不能访问了域名然后在加上端 ...

  9. (四)WebRTC手记之本地音频采集

    转自:http://www.cnblogs.com/fangkm/p/4374668.html 上一篇博文介绍了本地视频采集,这一篇就介绍下音频采集流程,也是先介绍WebRTC原生的音频采集,再介绍C ...

随机推荐

  1. P2066 机器分配 解析

    小日记: 1.今天新学的字体颜色,尽管不熟悉,但玩的666,卡星(开心) ╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮ 2.今天油腔滑调,谅解亿下 P2066 ...

  2. 更通俗的理解JS原型链

    最近在网上看到一篇理解原型链的,感觉非常好非常通俗易懂,拿来记录一下~: 1)人是人他妈生的,妖是妖他妈生的.人和妖都是对象实例,而人他妈和妖他妈就是原型.原型也是对象,叫原型对象. 2)人他妈和人他 ...

  3. File与IO基础

    IO流的作用:持久化到磁盘 File类的使用 File类基本概念 文件和文件夹都是用File类来表示. File类是内存层面的对象,内存中创建出来的File对象不一定有一个真实存在的文件或文件夹,但是 ...

  4. 【NOI 2002 银河英雄传说】【带权并查集】

    题面 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集*在巴米利恩星域爆发战争.泰山压顶集 ...

  5. Codeforces 1383C - String Transformation 2(找性质+状压 dp)

    Codeforces 题面传送门 & 洛谷题面传送门 神奇的强迫症效应,一场只要 AC 了 A.B.D.E.F,就一定会把 C 补掉( 感觉这个 C 难度比 D 难度高啊-- 首先考虑对问题进 ...

  6. 洛谷 P5046 [Ynoi2019 模拟赛] Yuno loves sqrt technology I(分块+卡常)

    洛谷题面传送门 zszz,lxl 出的 DS 都是卡常题( 首先由于此题强制在线,因此考虑分块,我们那么待查询区间 \([l,r]\) 可以很自然地被分为三个部分: 左散块 中间的整块 右散块 那么这 ...

  7. .NET6控制台程序使用quartz.net

    1.新建一个名为"ConsoleQuartz"的.NET6控制台程序. 2.nuget中安装Quartz和Quartz.Plugins,这2个DLL. 3.新建一个HelloQua ...

  8. Linux内网时钟同步问题(ntp和chrony)

    我们都知道时钟同步可以使用外网服务器,在内网内不能连接外网的时候也需要时钟同步,那怎么进行呢? 选择内网的一台稳定的服务器作为时钟源,然后让其他机器都来同步这台机器即可. 注:其实ntp服务和chro ...

  9. Identity Server 4 从入门到落地(四)—— 创建Web Api

    前面的部分: Identity Server 4 从入门到落地(一)-- 从IdentityServer4.Admin开始 Identity Server 4 从入门到落地(二)-- 理解授权码模式 ...

  10. 学习java的第二十一天

    一.今日收获 1.java完全学习手册第三章算法的3.2排序,比较了跟c语言排序上的不同 2.观看哔哩哔哩上的教学视频 二.今日问题 1.快速排序法的运行调试多次 2.哔哩哔哩教学视频的一些术语不太理 ...