/* global __filename */
import { getLogger } from 'jitsi-meet-logger';
import * as MediaType from '../../service/RTC/MediaType';
import { SdpTransformWrap } from '../xmpp/SdpTransformUtil';
const logger = getLogger(__filename);
/**
* Fakes local SDP exposed to {@link JingleSessionPC} through the local
* description getter. Modifies the SDP, so that it will contain muted local
* video tracks description, even though their underlying {MediaStreamTrack}s
* are no longer in the WebRTC peerconnection. That prevents from SSRC updates
* being sent to Jicofo/remote peer and prevents sRD/sLD cycle on the remote
* side.
*/
export default class LocalSdpMunger {
/**
* Creates new LocalSdpMunger instance.
*
* @param {TraceablePeerConnection} tpc
*/
constructor(tpc) {
this.tpc = tpc;
}
/**
* Makes sure that muted local video tracks associated with the parent
* {@link TraceablePeerConnection} are described in the local SDP. It's done
* in order to prevent from sending 'source-remove'/'source-add' Jingle
* notifications when local video track is muted (MediaStream is
* removed from the peerconnection).
*
* NOTE 1 video track is assumed
*
* @param {SdpTransformWrap} transformer the transformer instance which will
* be used to process the SDP.
* @return {boolean} true if there were any modifications to
* the SDP wrapped by transformer.
* @private
*/
_addMutedLocalVideoTracksToSDP(transformer) {
// Go over each video tracks and check if the SDP has to be changed
const localVideos = this.tpc.getLocalTracks(MediaType.VIDEO);
if (!localVideos.length) {
return false;
} else if (localVideos.length !== 1) {
logger.error(
`${this.tpc} there is more than 1 video track ! `
+ 'Strange things may happen !', localVideos);
}
const videoMLine = transformer.selectMedia('video');
if (!videoMLine) {
logger.error(
`${this.tpc} unable to hack local video track SDP`
+ '- no "video" media');
return false;
}
let modified = false;
for (const videoTrack of localVideos) {
const isMuted = videoTrack.isMuted();
const muteInProgress = videoTrack.inMuteOrUnmuteProgress;
const shouldFakeSdp = isMuted || muteInProgress;
logger.debug(
`${this.tpc} ${videoTrack
} isMuted: ${isMuted
}, is mute in progress: ${muteInProgress
} => should fake sdp ? : ${shouldFakeSdp}`);
if (!shouldFakeSdp) {
// eslint-disable-next-line no-continue
continue;
}
// Inject removed SSRCs
const requiredSSRCs
= this.tpc.isSimulcastOn()
? this.tpc.simulcast.ssrcCache
: [ this.tpc.sdpConsistency.cachedPrimarySsrc ];
if (!requiredSSRCs.length) {
logger.error(
`No SSRCs stored for: ${videoTrack} in ${this.tpc}`);
// eslint-disable-next-line no-continue
continue;
}
modified = true;
// We need to fake sendrecv.
// NOTE the SDP produced here goes only to Jicofo and is never set
// as localDescription. That's why
// {@link TraceablePeerConnection.mediaTransferActive} is ignored
// here.
videoMLine.direction = 'sendrecv';
// Check if the recvonly has MSID
const primarySSRC = requiredSSRCs[0];
// FIXME the cname could come from the stream, but may
// turn out to be too complex. It is fine to come up
// with any value, as long as we only care about
// the actual SSRC values when deciding whether or not
// an update should be sent
const primaryCname = `injected-${primarySSRC}`;
for (const ssrcNum of requiredSSRCs) {
// Remove old attributes
videoMLine.removeSSRC(ssrcNum);
// Inject
logger.debug(
`${this.tpc} injecting video SSRC: `
+ `${ssrcNum} for ${videoTrack}`);
videoMLine.addSSRCAttribute({
id: ssrcNum,
attribute: 'cname',
value: primaryCname
});
videoMLine.addSSRCAttribute({
id: ssrcNum,
attribute: 'msid',
value: videoTrack.storedMSID
});
}
if (requiredSSRCs.length > 1) {
const group = {
ssrcs: requiredSSRCs.join(' '),
semantics: 'SIM'
};
if (!videoMLine.findGroup(group.semantics, group.ssrcs)) {
// Inject the group
logger.debug(
`${this.tpc} injecting SIM group for ${videoTrack}`,
group);
videoMLine.addSSRCGroup(group);
}
}
// Insert RTX
// FIXME in P2P RTX is used by Chrome regardless of config option
// status. Because of that 'source-remove'/'source-add'
// notifications are still sent to remove/add RTX SSRC and FID group
if (!this.tpc.options.disableRtx) {
this.tpc.rtxModifier.modifyRtxSsrcs2(videoMLine);
}
}
return modified;
}
/**
* Maybe modifies local description to fake local video tracks SDP when
* those are muted.
*
* @param {object} desc the WebRTC SDP object instance for the local
* description.
*/
maybeMungeLocalSdp(desc) {
// Nothing to be done in early stage when localDescription
// is not available yet
if (!desc || !desc.sdp) {
return;
}
const transformer = new SdpTransformWrap(desc.sdp);
if (this._addMutedLocalVideoTracksToSDP(transformer)) {
// Write
desc.sdp = transformer.toRawSDP();
// logger.info("Post TRANSFORM: ", desc.sdp);
}
}
}