268 lines
8.5 KiB
JavaScript
268 lines
8.5 KiB
JavaScript
var SimulcastLogger = require("./SimulcastLogger");
|
|
var SimulcastUtils = require("./SimulcastUtils");
|
|
var MediaStreamType = require("../../service/RTC/MediaStreamTypes");
|
|
|
|
function SimulcastReceiver() {
|
|
this.simulcastUtils = new SimulcastUtils();
|
|
this.logger = new SimulcastLogger('SimulcastReceiver', 1);
|
|
}
|
|
|
|
SimulcastReceiver.prototype._remoteVideoSourceCache = '';
|
|
SimulcastReceiver.prototype._remoteMaps = {
|
|
msid2Quality: {},
|
|
ssrc2Msid: {},
|
|
msid2ssrc: {},
|
|
receivingVideoStreams: {}
|
|
};
|
|
|
|
SimulcastReceiver.prototype._cacheRemoteVideoSources = function (lines) {
|
|
this._remoteVideoSourceCache = this.simulcastUtils._getVideoSources(lines);
|
|
};
|
|
|
|
SimulcastReceiver.prototype._restoreRemoteVideoSources = function (lines) {
|
|
this.simulcastUtils._replaceVideoSources(lines, this._remoteVideoSourceCache);
|
|
};
|
|
|
|
SimulcastReceiver.prototype._ensureGoogConference = function (lines) {
|
|
var sb;
|
|
|
|
this.logger.info('Ensuring x-google-conference flag...')
|
|
|
|
if (this.simulcastUtils._indexOfArray('a=x-google-flag:conference', lines) === this.simulcastUtils._emptyCompoundIndex) {
|
|
// TODO(gp) do that for the audio as well as suggested by fippo.
|
|
// Add the google conference flag
|
|
sb = this.simulcastUtils._getVideoSources(lines);
|
|
sb = ['a=x-google-flag:conference'].concat(sb);
|
|
this.simulcastUtils._replaceVideoSources(lines, sb);
|
|
}
|
|
};
|
|
|
|
SimulcastReceiver.prototype._restoreSimulcastGroups = function (sb) {
|
|
this._restoreRemoteVideoSources(sb);
|
|
};
|
|
|
|
/**
|
|
* Restores the simulcast groups of the remote description. In
|
|
* transformRemoteDescription we remove those in order for the set remote
|
|
* description to succeed. The focus needs the signal the groups to new
|
|
* participants.
|
|
*
|
|
* @param desc
|
|
* @returns {*}
|
|
*/
|
|
SimulcastReceiver.prototype.reverseTransformRemoteDescription = function (desc) {
|
|
var sb;
|
|
|
|
if (!this.simulcastUtils.isValidDescription(desc)) {
|
|
return desc;
|
|
}
|
|
|
|
if (config.enableSimulcast) {
|
|
sb = desc.sdp.split('\r\n');
|
|
|
|
this._restoreSimulcastGroups(sb);
|
|
|
|
desc = new RTCSessionDescription({
|
|
type: desc.type,
|
|
sdp: sb.join('\r\n')
|
|
});
|
|
}
|
|
|
|
return desc;
|
|
};
|
|
|
|
SimulcastUtils.prototype._ensureOrder = function (lines) {
|
|
var videoSources, sb;
|
|
|
|
videoSources = this.parseMedia(lines, ['video'])[0];
|
|
sb = this._compileVideoSources(videoSources);
|
|
|
|
this._replaceVideoSources(lines, sb);
|
|
};
|
|
|
|
SimulcastReceiver.prototype._updateRemoteMaps = function (lines) {
|
|
var remoteVideoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0],
|
|
videoSource, quality;
|
|
|
|
// (re) initialize the remote maps.
|
|
this._remoteMaps.msid2Quality = {};
|
|
this._remoteMaps.ssrc2Msid = {};
|
|
this._remoteMaps.msid2ssrc = {};
|
|
|
|
var self = this;
|
|
if (remoteVideoSources.groups && remoteVideoSources.groups.length !== 0) {
|
|
remoteVideoSources.groups.forEach(function (group) {
|
|
if (group.semantics === 'SIM' && group.ssrcs && group.ssrcs.length !== 0) {
|
|
quality = 0;
|
|
group.ssrcs.forEach(function (ssrc) {
|
|
videoSource = remoteVideoSources.sources[ssrc];
|
|
self._remoteMaps.msid2Quality[videoSource.msid] = quality++;
|
|
self._remoteMaps.ssrc2Msid[videoSource.ssrc] = videoSource.msid;
|
|
self._remoteMaps.msid2ssrc[videoSource.msid] = videoSource.ssrc;
|
|
});
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
SimulcastReceiver.prototype._setReceivingVideoStream = function (resource, ssrc) {
|
|
this._remoteMaps.receivingVideoStreams[resource] = ssrc;
|
|
};
|
|
|
|
/**
|
|
* Returns a stream with single video track, the one currently being
|
|
* received by this endpoint.
|
|
*
|
|
* @param stream the remote simulcast stream.
|
|
* @returns {webkitMediaStream}
|
|
*/
|
|
SimulcastReceiver.prototype.getReceivingVideoStream = function (stream) {
|
|
var tracks, i, electedTrack, msid, quality = 0, receivingTrackId;
|
|
|
|
var self = this;
|
|
if (config.enableSimulcast) {
|
|
|
|
stream.getVideoTracks().some(function (track) {
|
|
return Object.keys(self._remoteMaps.receivingVideoStreams).some(function (resource) {
|
|
var ssrc = self._remoteMaps.receivingVideoStreams[resource];
|
|
var msid = self._remoteMaps.ssrc2Msid[ssrc];
|
|
if (msid == [stream.id, track.id].join(' ')) {
|
|
electedTrack = track;
|
|
return true;
|
|
}
|
|
});
|
|
});
|
|
|
|
if (!electedTrack) {
|
|
// we don't have an elected track, choose by initial quality.
|
|
tracks = stream.getVideoTracks();
|
|
for (i = 0; i < tracks.length; i++) {
|
|
msid = [stream.id, tracks[i].id].join(' ');
|
|
if (this._remoteMaps.msid2Quality[msid] === quality) {
|
|
electedTrack = tracks[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// TODO(gp) if the initialQuality could not be satisfied, lower
|
|
// the requirement and try again.
|
|
}
|
|
}
|
|
|
|
return (electedTrack)
|
|
? new webkitMediaStream([electedTrack])
|
|
: stream;
|
|
};
|
|
|
|
SimulcastReceiver.prototype.getReceivingSSRC = function (jid) {
|
|
var resource = Strophe.getResourceFromJid(jid);
|
|
var ssrc = this._remoteMaps.receivingVideoStreams[resource];
|
|
|
|
// If we haven't receiving a "changed" event yet, then we must be receiving
|
|
// low quality (that the sender always streams).
|
|
if(!ssrc)
|
|
{
|
|
var remoteStreamObject = APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
|
|
var remoteStream = remoteStreamObject.getOriginalStream();
|
|
var tracks = remoteStream.getVideoTracks();
|
|
if (tracks) {
|
|
for (var k = 0; k < tracks.length; k++) {
|
|
var track = tracks[k];
|
|
var msid = [remoteStream.id, track.id].join(' ');
|
|
var _ssrc = this._remoteMaps.msid2ssrc[msid];
|
|
var quality = this._remoteMaps.msid2Quality[msid];
|
|
if (quality == 0) {
|
|
ssrc = _ssrc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ssrc;
|
|
};
|
|
|
|
SimulcastReceiver.prototype.getReceivingVideoStreamBySSRC = function (ssrc)
|
|
{
|
|
var sid, electedStream;
|
|
var i, j, k;
|
|
var jid = APP.xmpp.getJidFromSSRC(ssrc);
|
|
if(jid && APP.RTC.remoteStreams[jid])
|
|
{
|
|
var remoteStreamObject = APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
|
|
var remoteStream = remoteStreamObject.getOriginalStream();
|
|
var tracks = remoteStream.getVideoTracks();
|
|
if (tracks) {
|
|
for (k = 0; k < tracks.length; k++) {
|
|
var track = tracks[k];
|
|
var msid = [remoteStream.id, track.id].join(' ');
|
|
var tmp = this._remoteMaps.msid2ssrc[msid];
|
|
if (tmp == ssrc) {
|
|
electedStream = new webkitMediaStream([track]);
|
|
sid = remoteStreamObject.sid;
|
|
// stream found, stop.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
console.debug(APP.RTC.remoteStreams, jid, ssrc);
|
|
}
|
|
|
|
return {
|
|
sid: sid,
|
|
stream: electedStream
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Gets the fully qualified msid (stream.id + track.id) associated to the
|
|
* SSRC.
|
|
*
|
|
* @param ssrc
|
|
* @returns {*}
|
|
*/
|
|
SimulcastReceiver.prototype.getRemoteVideoStreamIdBySSRC = function (ssrc) {
|
|
return this._remoteMaps.ssrc2Msid[ssrc];
|
|
};
|
|
|
|
/**
|
|
* Removes the ssrc-group:SIM from the remote description bacause Chrome
|
|
* either gets confused and thinks this is an FID group or, if an FID group
|
|
* is already present, it fails to set the remote description.
|
|
*
|
|
* @param desc
|
|
* @returns {*}
|
|
*/
|
|
SimulcastReceiver.prototype.transformRemoteDescription = function (desc) {
|
|
|
|
if (desc && desc.sdp) {
|
|
var sb = desc.sdp.split('\r\n');
|
|
|
|
this._updateRemoteMaps(sb);
|
|
this._cacheRemoteVideoSources(sb);
|
|
|
|
// NOTE(gp) this needs to be called after updateRemoteMaps because we
|
|
// need the simulcast group in the _updateRemoteMaps() method.
|
|
this.simulcastUtils._removeSimulcastGroup(sb);
|
|
|
|
if (desc.sdp.indexOf('a=ssrc-group:SIM') !== -1) {
|
|
// We don't need the goog conference flag if we're not doing
|
|
// simulcast.
|
|
this._ensureGoogConference(sb);
|
|
}
|
|
|
|
desc = new RTCSessionDescription({
|
|
type: desc.type,
|
|
sdp: sb.join('\r\n')
|
|
});
|
|
|
|
this.logger.fine(['Transformed remote description', desc.sdp].join(' '));
|
|
}
|
|
|
|
return desc;
|
|
};
|
|
|
|
module.exports = SimulcastReceiver; |