Fixes SSRC=1 issue. Renames VideoSSRCHack to LocalSSRCReplacement.

This commit is contained in:
paweldomas 2015-08-07 12:55:45 +02:00
parent ab4c29eddc
commit fb875423a9
3 changed files with 91 additions and 27 deletions

View File

@ -7,7 +7,7 @@ var async = require("async");
var transform = require("sdp-transform"); var transform = require("sdp-transform");
var XMPPEvents = require("../../service/xmpp/XMPPEvents"); var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var RTCBrowserType = require("../RTC/RTCBrowserType"); var RTCBrowserType = require("../RTC/RTCBrowserType");
var VideoSSRCHack = require("./VideoSSRCHack"); var SSRCReplacement = require("./LocalSSRCReplacement");
// Jingle stuff // Jingle stuff
function JingleSession(me, sid, connection, service, eventEmitter) { function JingleSession(me, sid, connection, service, eventEmitter) {
@ -254,7 +254,7 @@ JingleSession.prototype.accept = function () {
//console.log('setLocalDescription success'); //console.log('setLocalDescription success');
self.setLocalDescription(); self.setLocalDescription();
VideoSSRCHack.processSessionInit(accept); SSRCReplacement.processSessionInit(accept);
self.connection.sendIQ(accept, self.connection.sendIQ(accept,
function () { function () {
@ -348,7 +348,7 @@ JingleSession.prototype.sendIceCandidate = function (candidate) {
self.initiator == self.me ? 'initiator' : 'responder', self.initiator == self.me ? 'initiator' : 'responder',
ssrc); ssrc);
VideoSSRCHack.processSessionInit(init); SSRCReplacement.processSessionInit(init);
self.connection.sendIQ(init, self.connection.sendIQ(init,
function () { function () {
@ -467,7 +467,7 @@ JingleSession.prototype.createdOffer = function (sdp) {
this.initiator == this.me ? 'initiator' : 'responder', this.initiator == this.me ? 'initiator' : 'responder',
this.localStreamsSSRC); this.localStreamsSSRC);
VideoSSRCHack.processSessionInit(init); SSRCReplacement.processSessionInit(init);
self.connection.sendIQ(init, self.connection.sendIQ(init,
function () { function () {
@ -733,7 +733,7 @@ JingleSession.prototype.createdAnswer = function (sdp, provisional) {
self.initiator == self.me ? 'initiator' : 'responder', self.initiator == self.me ? 'initiator' : 'responder',
ssrcs); ssrcs);
VideoSSRCHack.processSessionInit(accept); SSRCReplacement.processSessionInit(accept);
self.connection.sendIQ(accept, self.connection.sendIQ(accept,
function () { function () {
@ -1135,7 +1135,7 @@ JingleSession.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) {
// Let 'source-remove' IQ through the hack and see if we're allowed to send // Let 'source-remove' IQ through the hack and see if we're allowed to send
// it in the current form // it in the current form
if (removed) if (removed)
remove = VideoSSRCHack.processSourceRemove(remove); remove = SSRCReplacement.processSourceRemove(remove);
if (removed && remove) { if (removed && remove) {
console.info("Sending source-remove", remove); console.info("Sending source-remove", remove);
@ -1166,7 +1166,7 @@ JingleSession.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) {
// Let 'source-add' IQ through the hack and see if we're allowed to send // Let 'source-add' IQ through the hack and see if we're allowed to send
// it in the current form // it in the current form
if (added) if (added)
add = VideoSSRCHack.processSourceAdd(add); add = SSRCReplacement.processSourceAdd(add);
if (added && add) { if (added && add) {
console.info("Sending source-add", add); console.info("Sending source-add", add);

View File

@ -1,8 +1,21 @@
/* global $ */ /* global $ */
/* /*
The purpose of this hack is to re-use SSRC of first video stream ever created Here we do modifications of local video SSRCs. There are 2 situations we have
for any video streams created later on. In order to do that this hack: to handle:
1. We generate SSRC for local recvonly video stream. This is the case when we
have no local camera and it is not generated automatically, but SSRC=1 is
used implicitly. If that happens RTCP packets will be dropped by the JVB
and we won't be able to request video key frames correctly.
2. A hack to re-use SSRC of the first video stream for any new stream created
in future. It turned out that Chrome may keep on using the SSRC of removed
video stream in RTCP even though a new one has been created. So we just
want to avoid that by re-using it. Jingle 'source-remove'/'source-add'
notifications are blocked once first video SSRC is advertised to the focus.
What this hack does:
1. Stores the SSRC of the first video stream created by 1. Stores the SSRC of the first video stream created by
a) scanning Jingle session-accept/session-invite for existing video SSRC a) scanning Jingle session-accept/session-invite for existing video SSRC
@ -33,6 +46,18 @@ var isEnabled = !RTCBrowserType.isFirefox();
*/ */
var localVideoSSRC; var localVideoSSRC;
/**
* SSRC used for recvonly video stream when we have no local camera.
* This is in order to tell Chrome what SSRC should be used in RTCP requests
* instead of 1.
*/
var localRecvOnlySSRC;
/**
* cname for <tt>localRecvOnlySSRC</tt>
*/
var localRecvOnlyCName;
/** /**
* Method removes <source> element which describes <tt>localVideoSSRC</tt> * Method removes <source> element which describes <tt>localVideoSSRC</tt>
* from given Jingle IQ. * from given Jingle IQ.
@ -79,21 +104,41 @@ var storeLocalVideoSSRC = function (jingleIq) {
$(jingleIq.tree()) $(jingleIq.tree())
.find('>jingle>content[name="video"]>description>source'); .find('>jingle>content[name="video"]>description>source');
console.info('Video desc: ', videoSSRCs); videoSSRCs.each(function (idx, ssrcElem) {
if (!videoSSRCs.length) if (localVideoSSRC)
return; return;
// We consider SSRC real only if it has msid attribute
var ssrc = videoSSRCs.attr('ssrc'); // recvonly streams in FF do not have it as well as local SSRCs
if (ssrc) { // we generate for recvonly streams in Chrome
localVideoSSRC = ssrc; var ssrSel = $(ssrcElem);
console.info( var msid = ssrSel.find('>parameter[name="msid"]');
'Stored local video SSRC for future re-use: ' + localVideoSSRC); if (msid.length) {
} else { var ssrcVal = ssrSel.attr('ssrc');
console.error('No "ssrc" attribute present in <source> element'); if (ssrcVal) {
localVideoSSRC = ssrcVal;
console.info('Stored local video SSRC' +
' for future re-use: ' + localVideoSSRC);
} }
}
});
}; };
var LocalVideoSSRCHack = { /**
* Generates new SSRC for local video recvonly stream.
* FIXME what about eventual SSRC collision ?
*/
function generateRecvonlySSRC() {
//
localRecvOnlySSRC =
Math.random().toString(10).substring(2, 11);
localRecvOnlyCName =
Math.random().toString(36).substring(2);
console.info(
"Generated local recvonly SSRC: " + localRecvOnlySSRC +
", cname: " + localRecvOnlyCName);
}
var LocalSSRCReplacement = {
/** /**
* Method must be called before 'session-initiate' or 'session-invite' is * Method must be called before 'session-initiate' or 'session-invite' is
* sent. Scans the IQ for local video SSRC and stores it if detected. * sent. Scans the IQ for local video SSRC and stores it if detected.
@ -144,6 +189,25 @@ var LocalVideoSSRCHack = {
new RegExp('a=ssrc:' + newSSRC, 'g'), new RegExp('a=ssrc:' + newSSRC, 'g'),
'a=ssrc:' + localVideoSSRC); 'a=ssrc:' + localVideoSSRC);
} }
} else {
// Make sure we have any SSRC for recvonly video stream
var sdp = new SDP(localDescription.sdp);
if (sdp.media[1] && sdp.media[1].indexOf('a=ssrc:') === -1 &&
sdp.media[1].indexOf('a=recvonly') !== -1) {
if (!localRecvOnlySSRC) {
generateRecvonlySSRC();
}
console.info('No SSRC in video recvonly stream' +
' - adding SSRC: ' + localRecvOnlySSRC);
sdp.media[1] += 'a=ssrc:' + localRecvOnlySSRC +
' cname:' + localRecvOnlyCName + '\r\n';
localDescription.sdp = sdp.session + sdp.media.join('');
}
} }
return localDescription; return localDescription;
}, },
@ -196,4 +260,4 @@ var LocalVideoSSRCHack = {
} }
}; };
module.exports = LocalVideoSSRCHack; module.exports = LocalSSRCReplacement;

View File

@ -1,7 +1,7 @@
var RTC = require('../RTC/RTC'); var RTC = require('../RTC/RTC');
var RTCBrowserType = require("../RTC/RTCBrowserType.js"); var RTCBrowserType = require("../RTC/RTCBrowserType.js");
var XMPPEvents = require("../../service/xmpp/XMPPEvents"); var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var VideoSSRCHack = require("./VideoSSRCHack"); var SSRCReplacement = require("./LocalSSRCReplacement");
function TraceablePeerConnection(ice_config, constraints, session) { function TraceablePeerConnection(ice_config, constraints, session) {
var self = this; var self = this;
@ -213,7 +213,7 @@ if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
function() { function() {
var desc = this.peerconnection.localDescription; var desc = this.peerconnection.localDescription;
desc = VideoSSRCHack.mungeLocalVideoSSRC(desc); desc = SSRCReplacement.mungeLocalVideoSSRC(desc);
this.trace('getLocalDescription::preTransform', dumpSDP(desc)); this.trace('getLocalDescription::preTransform', dumpSDP(desc));
@ -372,7 +372,7 @@ TraceablePeerConnection.prototype.createOffer
self.trace('createOfferOnSuccess::postTransform (Plan B)', dumpSDP(offer)); self.trace('createOfferOnSuccess::postTransform (Plan B)', dumpSDP(offer));
} }
offer = VideoSSRCHack.mungeLocalVideoSSRC(offer); offer = SSRCReplacement.mungeLocalVideoSSRC(offer);
if (config.enableSimulcast && self.simulcast.isSupported()) { if (config.enableSimulcast && self.simulcast.isSupported()) {
offer = self.simulcast.mungeLocalDescription(offer); offer = self.simulcast.mungeLocalDescription(offer);
@ -402,7 +402,7 @@ TraceablePeerConnection.prototype.createAnswer
} }
// munge local video SSRC // munge local video SSRC
answer = VideoSSRCHack.mungeLocalVideoSSRC(answer); answer = SSRCReplacement.mungeLocalVideoSSRC(answer);
if (config.enableSimulcast && self.simulcast.isSupported()) { if (config.enableSimulcast && self.simulcast.isSupported()) {
answer = self.simulcast.mungeLocalDescription(answer); answer = self.simulcast.mungeLocalDescription(answer);