123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- /* 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, so that it will reflect detached local tracks associated
- * with the {@link TraceablePeerConnection} and make operations like
- * attach/detach and video mute/unmute local operations. That means it prevents
- * from SSRC updates being sent to Jicofo/remote peer, so that there is no
- * sRD/sLD cycle on the remote side.
- */
- export default class LocalSdpMunger {
-
- /**
- * Creates new <tt>LocalSdpMunger</tt> instance.
- *
- * @param {TraceablePeerConnection} tpc
- */
- constructor(tpc) {
- this.tpc = tpc;
- }
-
- /**
- * Makes sure that detached local audio tracks stored in 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 audio track is detached from
- * the {@link TraceablePeerConnection}.
- * @param {SdpTransformWrap} transformer the transformer instance which will
- * be used to process the SDP.
- * @return {boolean} <tt>true</tt> if there were any modifications to
- * the SDP wrapped by <tt>transformer</tt>.
- * @private
- */
- _addDetachedLocalAudioTracksToSDP(transformer) {
- const localAudio = this.tpc.getLocalTracks(MediaType.AUDIO);
-
- if (!localAudio.length) {
- return false;
- }
- const audioMLine = transformer.selectMedia('audio');
-
- if (!audioMLine) {
- logger.error(
- 'Unable to hack local audio track SDP - no "audio" media');
-
- return false;
- }
-
- if (audioMLine.direction === 'inactive') {
- logger.error(
- 'Not doing local audio transform for "inactive" direction');
-
- return false;
- }
-
- let modified = false;
-
- for (const audioTrack of localAudio) {
- const isAttached = audioTrack._isAttachedToPC(this.tpc);
- const shouldFake = !isAttached;
-
- logger.debug(
- `${audioTrack} isAttached: ${isAttached
- } => should fake audio SDP ?: ${shouldFake}`);
-
- if (!shouldFake) {
- // not using continue increases indentation
- // eslint-disable-next-line no-continue
- continue;
- }
-
- // Inject removed SSRCs
- const audioSSRC = this.tpc.getLocalSSRC(audioTrack);
- const audioMSID = audioTrack.storedMSID;
-
- if (!audioSSRC) {
- logger.error(
- `Can't fake SDP for ${audioTrack} - no SSRC stored`);
-
- // Aborts the forEach on this particular track,
- // but will continue with the other ones
- // eslint-disable-next-line no-continue
- continue;
- } else if (!audioMSID) {
- logger.error(
- `No MSID stored for local audio SSRC: ${audioSSRC}`);
-
- // eslint-disable-next-line no-continue
- continue;
- }
-
- if (audioMLine.getSSRCCount() > 0) {
- logger.debug(
- 'Doing nothing - audio SSRCs are still there');
-
- // audio SSRCs are still there
- // eslint-disable-next-line no-continue
- continue;
- }
-
- modified = true;
-
- // We need to fake sendrecv
- audioMLine.direction = 'sendrecv';
-
- logger.debug(`Injecting audio SSRC: ${audioSSRC}`);
- audioMLine.addSSRCAttribute({
- id: audioSSRC,
- attribute: 'cname',
- value: `injected-${audioSSRC}`
- });
- audioMLine.addSSRCAttribute({
- id: audioSSRC,
- attribute: 'msid',
- value: audioMSID
- });
- }
-
- return modified;
- }
-
- /**
- * Makes sure that detached (or 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 detached from the {@link TraceablePeerConnection} (or muted).
- *
- * NOTE 1 video track is assumed
- *
- * @param {SdpTransformWrap} transformer the transformer instance which will
- * be used to process the SDP.
- * @return {boolean} <tt>true</tt> if there were any modifications to
- * the SDP wrapped by <tt>transformer</tt>.
- * @private
- */
- _addDetachedLocalVideoTracksToSDP(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(
- 'There is more than 1 video track ! '
- + 'Strange things may happen !', localVideos);
- }
-
- const videoMLine = transformer.selectMedia('video');
-
- if (!videoMLine) {
- logger.error(
- 'Unable to hack local video track SDP - no "video" media');
-
- return false;
- }
-
- if (videoMLine.direction === 'inactive') {
- logger.error(
- 'Not doing local video transform for "inactive" direction.');
-
- return false;
- }
-
- let modified = false;
-
- for (const videoTrack of localVideos) {
- const isMuted = videoTrack.isMuted();
- const muteInProgress = videoTrack.inMuteOrUnmuteProgress;
- const isAttached = videoTrack._isAttachedToPC(this.tpc);
- const shouldFakeSdp = isMuted || muteInProgress || !isAttached;
-
- logger.debug(
- `${videoTrack
- } isMuted: ${isMuted
- }, is mute in progress: ${muteInProgress
- }, is attached ? : ${isAttached
- } => 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;
- }
- if (!videoMLine.getSSRCCount()) {
- logger.error(
- 'No video SSRCs found '
- + '(should be at least the recv-only one');
-
- // eslint-disable-next-line no-continue
- continue;
- }
-
- modified = true;
-
- // We need to fake sendrecv
- 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(
- `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(
- `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 tracks SDP when those are
- * either muted or detached from the <tt>PeerConnection</tt>.
- *
- * @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);
- let modified = this._addDetachedLocalAudioTracksToSDP(transformer);
-
- if (this._addDetachedLocalVideoTracksToSDP(transformer)) {
- modified = true;
- }
- if (modified) {
- // Write
- desc.sdp = transformer.toRawSDP();
-
- // logger.info("Post TRANSFORM: ", desc.sdp);
- }
- }
- }
|