node.js 如何控制WebRTC视频通话的带宽?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16712224/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to control bandwidth in WebRTC video call?
提问by user2413226
I am trying to develop a Video Calling/Conferencing application using WebRTC and node.js. Right now there is no facility to control bandwidth during during video call. Is there any way to control/reduce bandwidth. (like I want make whole my web application to work on 150 kbps while video conferencing).
我正在尝试使用 WebRTC 和 node.js 开发视频通话/会议应用程序。现在没有工具可以在视频通话期间控制带宽。有什么方法可以控制/减少带宽。(就像我想让整个 Web 应用程序在视频会议时以 150 kbps 的速度运行一样)。
Any suggestions are highly appreciated. Thanks in advance.
任何建议都非常感谢。提前致谢。
回答by Muaz Khan
Try this demo. You can inject bandwidth attributes (b=AS) in the session descriptions:
试试这个演示。您可以b=AS在会话描述中注入带宽属性 ( ):
audioBandwidth = 50;
videoBandwidth = 256;
function setBandwidth(sdp) {
sdp = sdp.replace(/a=mid:audio\r\n/g, 'a=mid:audio\r\nb=AS:' + audioBandwidth + '\r\n');
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + videoBandwidth + '\r\n');
return sdp;
}
// ----------------------------------------------------------
peer.createOffer(function (sessionDescription) {
sessionDescription.sdp = setBandwidth(sessionDescription.sdp);
peer.setLocalDescription(sessionDescription);
}, null, constraints);
peer.createAnswer(function (sessionDescription) {
sessionDescription.sdp = setBandwidth(sessionDescription.sdp);
peer.setLocalDescription(sessionDescription);
}, null, constraints);
b=ASis already present in sdp for data m-line; its default value is 50.
b=AS已经存在于 sdp 中data m-line;它的默认值是50。
Updated at Sept 23, 2015
2015 年 9 月 23 日更新
Here is a library that provides full control over both audio/video tracks' bitrates:
这是一个可以完全控制音频/视频轨道比特率的库:
// here is how to use it
var bandwidth = {
screen: 300, // 300kbits minimum
audio: 50, // 50kbits minimum
video: 256 // 256kbits (both min-max)
};
var isScreenSharing = false;
sdp = BandwidthHandler.setApplicationSpecificBandwidth(sdp, bandwidth, isScreenSharing);
sdp = BandwidthHandler.setVideoBitrates(sdp, {
min: bandwidth.video,
max: bandwidth.video
});
sdp = BandwidthHandler.setOpusAttributes(sdp);
Here is the library code. Its quite big but it works!
这是库代码。它相当大,但它的工作原理!
// BandwidthHandler.js
var BandwidthHandler = (function() {
function setBAS(sdp, bandwidth, isScreen) {
if (!!navigator.mozGetUserMedia || !bandwidth) {
return sdp;
}
if (isScreen) {
if (!bandwidth.screen) {
console.warn('It seems that you are not using bandwidth for screen. Screen sharing is expected to fail.');
} else if (bandwidth.screen < 300) {
console.warn('It seems that you are using wrong bandwidth value for screen. Screen sharing is expected to fail.');
}
}
// if screen; must use at least 300kbs
if (bandwidth.screen && isScreen) {
sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, '');
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.screen + '\r\n');
}
// remove existing bandwidth lines
if (bandwidth.audio || bandwidth.video || bandwidth.data) {
sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, '');
}
if (bandwidth.audio) {
sdp = sdp.replace(/a=mid:audio\r\n/g, 'a=mid:audio\r\nb=AS:' + bandwidth.audio + '\r\n');
}
if (bandwidth.video) {
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + (isScreen ? bandwidth.screen : bandwidth.video) + '\r\n');
}
return sdp;
}
// Find the line in sdpLines that starts with |prefix|, and, if specified,
// contains |substr| (case-insensitive search).
function findLine(sdpLines, prefix, substr) {
return findLineInRange(sdpLines, 0, -1, prefix, substr);
}
// Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix|
// and, if specified, contains |substr| (case-insensitive search).
function findLineInRange(sdpLines, startLine, endLine, prefix, substr) {
var realEndLine = endLine !== -1 ? endLine : sdpLines.length;
for (var i = startLine; i < realEndLine; ++i) {
if (sdpLines[i].indexOf(prefix) === 0) {
if (!substr ||
sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) {
return i;
}
}
}
return null;
}
// Gets the codec payload type from an a=rtpmap:X line.
function getCodecPayloadType(sdpLine) {
var pattern = new RegExp('a=rtpmap:(\d+) \w+\/\d+');
var result = sdpLine.match(pattern);
return (result && result.length === 2) ? result[1] : null;
}
function setVideoBitrates(sdp, params) {
params = params || {};
var xgoogle_min_bitrate = params.min;
var xgoogle_max_bitrate = params.max;
var sdpLines = sdp.split('\r\n');
// VP8
var vp8Index = findLine(sdpLines, 'a=rtpmap', 'VP8/90000');
var vp8Payload;
if (vp8Index) {
vp8Payload = getCodecPayloadType(sdpLines[vp8Index]);
}
if (!vp8Payload) {
return sdp;
}
var rtxIndex = findLine(sdpLines, 'a=rtpmap', 'rtx/90000');
var rtxPayload;
if (rtxIndex) {
rtxPayload = getCodecPayloadType(sdpLines[rtxIndex]);
}
if (!rtxIndex) {
return sdp;
}
var rtxFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + rtxPayload.toString());
if (rtxFmtpLineIndex !== null) {
var appendrtxNext = '\r\n';
appendrtxNext += 'a=fmtp:' + vp8Payload + ' x-google-min-bitrate=' + (xgoogle_min_bitrate || '228') + '; x-google-max-bitrate=' + (xgoogle_max_bitrate || '228');
sdpLines[rtxFmtpLineIndex] = sdpLines[rtxFmtpLineIndex].concat(appendrtxNext);
sdp = sdpLines.join('\r\n');
}
return sdp;
}
function setOpusAttributes(sdp, params) {
params = params || {};
var sdpLines = sdp.split('\r\n');
// Opus
var opusIndex = findLine(sdpLines, 'a=rtpmap', 'opus/48000');
var opusPayload;
if (opusIndex) {
opusPayload = getCodecPayloadType(sdpLines[opusIndex]);
}
if (!opusPayload) {
return sdp;
}
var opusFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + opusPayload.toString());
if (opusFmtpLineIndex === null) {
return sdp;
}
var appendOpusNext = '';
appendOpusNext += '; stereo=' + (typeof params.stereo != 'undefined' ? params.stereo : '1');
appendOpusNext += '; sprop-stereo=' + (typeof params['sprop-stereo'] != 'undefined' ? params['sprop-stereo'] : '1');
if (typeof params.maxaveragebitrate != 'undefined') {
appendOpusNext += '; maxaveragebitrate=' + (params.maxaveragebitrate || 128 * 1024 * 8);
}
if (typeof params.maxplaybackrate != 'undefined') {
appendOpusNext += '; maxplaybackrate=' + (params.maxplaybackrate || 128 * 1024 * 8);
}
if (typeof params.cbr != 'undefined') {
appendOpusNext += '; cbr=' + (typeof params.cbr != 'undefined' ? params.cbr : '1');
}
if (typeof params.useinbandfec != 'undefined') {
appendOpusNext += '; useinbandfec=' + params.useinbandfec;
}
if (typeof params.usedtx != 'undefined') {
appendOpusNext += '; usedtx=' + params.usedtx;
}
if (typeof params.maxptime != 'undefined') {
appendOpusNext += '\r\na=maxptime:' + params.maxptime;
}
sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].concat(appendOpusNext);
sdp = sdpLines.join('\r\n');
return sdp;
}
return {
setApplicationSpecificBandwidth: function(sdp, bandwidth, isScreen) {
return setBAS(sdp, bandwidth, isScreen);
},
setVideoBitrates: function(sdp, params) {
return setVideoBitrates(sdp, params);
},
setOpusAttributes: function(sdp, params) {
return setOpusAttributes(sdp, params);
}
};
})();
Here is how to set advance opus bitrate parameters:
以下是设置高级作品比特率参数的方法:
sdp = BandwidthHandler.setOpusAttributes(sdp, {
'stereo': 0, // to disable stereo (to force mono audio)
'sprop-stereo': 1,
'maxaveragebitrate': 500 * 1024 * 8, // 500 kbits
'maxplaybackrate': 500 * 1024 * 8, // 500 kbits
'cbr': 0, // disable cbr
'useinbandfec': 1, // use inband fec
'usedtx': 1, // use dtx
'maxptime': 3
});
回答by Sam Dutton
Not sure if this helps, but you can limit the video resolution from getUserMedia with constraints: see demo at simpl.info/getusermedia/constraints/.
不确定这是否有帮助,但您可以通过约束限制来自 getUserMedia 的视频分辨率:请参阅simpl.info/getusermedia/constraints/ 上的演示。
回答by Adnan Y
A more up-to-date answer
一个更新的答案
const videobitrate = 20000;
var offer = pc.localDescription;
// Set bandwidth for video
offer.sdp = offer.sdp.replace(/(m=video.*\r\n)/g, `b=AS:${videobitrate}\r\n`);
pc.setLocalDescription(offer);
Explanation: a=mid:videois not a guaranteed tag. For receive only video, you might not see it or see a=mid:0. Generally it's a better bet to look for the m=video xxxx xxxx(or similar audio) tag and append the bandwidth parameters underneath
说明:a=mid:video不是保证标签。对于仅接收视频,您可能看不到或看不到a=mid:0。通常最好寻找m=video xxxx xxxx(或类似的音频)标签并将带宽参数附加到下面
回答by carloseme
I did it Yesterday and it works like a charm! in my case, it was needed to prevent slow and old phones get freeze during a videocall! have a look
我昨天做到了,它就像一个魅力!就我而言,需要防止慢速和旧手机在视频通话期间冻结!看一看
function handle_offer_sdp(offer) {
let sdp = offer.sdp.split('\r\n');//convert to an concatenable array
let new_sdp = '';
let position = null;
sdp = sdp.slice(0, -1); //remove the last comma ','
for(let i = 0; i < sdp.length; i++) {//look if exists already a b=AS:XXX line
if(sdp[i].match(/b=AS:/)) {
position = i; //mark the position
}
}
if(position) {
sdp.splice(position, 1);//remove if exists
}
for(let i = 0; i < sdp.length; i++) {
if(sdp[i].match(/m=video/)) {//modify and add the new lines for video
new_sdp += sdp[i] + '\r\n' + 'b=AS:' + '128' + '\r\n';
}
else {
if(sdp[i].match(/m=audio/)) { //modify and add the new lines for audio
new_sdp += sdp[i] + '\r\n' + 'b=AS:' + 64 + '\r\n';
}
else {
new_sdp += sdp[i] + '\r\n';
}
}
}
return new_sdp; //return the new sdp
}
pc.createOffer(function(offer) {
offer.sdp = handle_offer_sdp(offer); //invoke function saving the new sdp
pc.setLocalDescription(offer);
}, function(error) {
console.log('error -> ' + error);
});
回答by tom
You should also be able to use bandwidth constraints on the stream (see this demo), but it doesn't appear to be working, even in the latest canary (29.0.1529.3).
您还应该能够对流使用带宽限制(请参阅此演示),但它似乎不起作用,即使在最新的金丝雀 (29.0.1529.3) 中也是如此。
There's some discussion of the SDP-based approach on the discuss-webrtcmailing list, which links to WebRTC bug 1846.
在Discussion-webrtc邮件列表中有一些关于基于 SDP 的方法的讨论,该邮件列表链接到 WebRTC 错误 1846。
回答by Konga Raju
WebRTC is for peer to peer communication, you cannot control bandwidth in video call.
WebRTC 用于点对点通信,您无法控制视频通话中的带宽。
In google chrome there are these properties on a video element:
在谷歌浏览器中,视频元素有以下属性:
webkitVideoDecodedByteCount: 0
webkitAudioDecodedByteCount: 0
These are useful to know how fast the client can decode the video. As the video plays you would keep track of the delta amount of these bytes which gives you bytes/s the client is processing the video.(SO thread)
这些对于了解客户端解码视频的速度很有用。在视频播放时,您将跟踪这些字节的增量数量,这为您提供了客户端正在处理视频的字节/秒。(SO 线程)
you should use Network Information APIto know bandwidth ( it is still under implementation).
您应该使用 网络信息 API来了解带宽(它仍在实施中)。

