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 XMPPEvents = require("../../service/xmpp/XMPPEvents");
var RTCBrowserType = require("../RTC/RTCBrowserType");
var VideoSSRCHack = require("./VideoSSRCHack");
var SSRCReplacement = require("./LocalSSRCReplacement");
// Jingle stuff
function JingleSession(me, sid, connection, service, eventEmitter) {
@ -254,7 +254,7 @@ JingleSession.prototype.accept = function () {
//console.log('setLocalDescription success');
self.setLocalDescription();
VideoSSRCHack.processSessionInit(accept);
SSRCReplacement.processSessionInit(accept);
self.connection.sendIQ(accept,
function () {
@ -348,7 +348,7 @@ JingleSession.prototype.sendIceCandidate = function (candidate) {
self.initiator == self.me ? 'initiator' : 'responder',
ssrc);
VideoSSRCHack.processSessionInit(init);
SSRCReplacement.processSessionInit(init);
self.connection.sendIQ(init,
function () {
@ -467,7 +467,7 @@ JingleSession.prototype.createdOffer = function (sdp) {
this.initiator == this.me ? 'initiator' : 'responder',
this.localStreamsSSRC);
VideoSSRCHack.processSessionInit(init);
SSRCReplacement.processSessionInit(init);
self.connection.sendIQ(init,
function () {
@ -733,7 +733,7 @@ JingleSession.prototype.createdAnswer = function (sdp, provisional) {
self.initiator == self.me ? 'initiator' : 'responder',
ssrcs);
VideoSSRCHack.processSessionInit(accept);
SSRCReplacement.processSessionInit(accept);
self.connection.sendIQ(accept,
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
// it in the current form
if (removed)
remove = VideoSSRCHack.processSourceRemove(remove);
remove = SSRCReplacement.processSourceRemove(remove);
if (removed && 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
// it in the current form
if (added)
add = VideoSSRCHack.processSourceAdd(add);
add = SSRCReplacement.processSourceAdd(add);
if (added && add) {
console.info("Sending source-add", add);

View File

@ -1,8 +1,21 @@
/* global $ */
/*
The purpose of this hack is to re-use SSRC of first video stream ever created
for any video streams created later on. In order to do that this hack:
Here we do modifications of local video SSRCs. There are 2 situations we have
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
a) scanning Jingle session-accept/session-invite for existing video SSRC
@ -33,6 +46,18 @@ var isEnabled = !RTCBrowserType.isFirefox();
*/
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>
* from given Jingle IQ.
@ -79,21 +104,41 @@ var storeLocalVideoSSRC = function (jingleIq) {
$(jingleIq.tree())
.find('>jingle>content[name="video"]>description>source');
console.info('Video desc: ', videoSSRCs);
if (!videoSSRCs.length)
videoSSRCs.each(function (idx, ssrcElem) {
if (localVideoSSRC)
return;
var ssrc = videoSSRCs.attr('ssrc');
if (ssrc) {
localVideoSSRC = ssrc;
console.info(
'Stored local video SSRC for future re-use: ' + localVideoSSRC);
} else {
console.error('No "ssrc" attribute present in <source> element');
// We consider SSRC real only if it has msid attribute
// recvonly streams in FF do not have it as well as local SSRCs
// we generate for recvonly streams in Chrome
var ssrSel = $(ssrcElem);
var msid = ssrSel.find('>parameter[name="msid"]');
if (msid.length) {
var ssrcVal = ssrSel.attr('ssrc');
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
* 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'),
'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;
},
@ -196,4 +260,4 @@ var LocalVideoSSRCHack = {
}
};
module.exports = LocalVideoSSRCHack;
module.exports = LocalSSRCReplacement;

View File

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