我正在尝试使用WebRTC和node.js开发视频呼叫/会议应用程序。目前,在视频通话期间无法控制带宽。有没有办法控制/减少带宽。 (就像我想让整个我的Web应用程序在视频会议上以150 kbps的速度工作)。
任何建议都非常感谢。提前致谢。
试试this demo。您可以在会话描述中注入带宽属性(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=AS
已经出现在data m-line
的sdp中;它的默认值是50
。
这是一个可以完全控制音频/视频轨道比特率的库:
// 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);
这是库代码。它很大,但它的工作原理!
// 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);
}
};
})();
以下是如何设置高级opus比特率参数:
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
});
不确定这是否有帮助,但您可以通过约束来限制getUserMedia的视频分辨率:请参阅simpl.info/getusermedia/constraints/上的demo。
您还应该能够在流上使用带宽约束(see this demo),但它似乎不起作用,即使在最新的金丝雀(29.0.1529.3)中也是如此。
在discuss-webrtc邮件列表上有一些关于基于SDP的方法的讨论,该列表链接到WebRTC bug 1846。
WebRTC用于对等通信,您无法控制视频通话中的带宽。
在谷歌浏览器中,视频元素上有以下属性:
webkitVideoDecodedByteCount: 0
webkitAudioDecodedByteCount: 0
这些对于了解客户端解码视频的速度非常有用。当视频播放时,您将跟踪这些字节的增量,这将为您提供客户端正在处理视频的字节数。(SO thread)
你应该使用Network Information API来了解带宽(它仍在实施中)。