Skip to content

Commit 5d1639d

Browse files
authored
feat(TS) Migrate modules\sdp\LocalSdpMunger.js to TS(#2860)
1 parent 37bbdb0 commit 5d1639d

4 files changed

Lines changed: 222 additions & 199 deletions

File tree

modules/sdp/LocalSdpMunger.js

Lines changed: 0 additions & 163 deletions
This file was deleted.

modules/sdp/LocalSdpMunger.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ describe('TransformSdpsForUnifiedPlan', () => {
9999

100100
it('should add missing msid', () => {
101101
// P2P case only.
102-
localSdpMunger.tpc.isP2P = true;
102+
localSdpMunger._tpc.isP2P = true;
103103

104104
const sdpStr = transform.write(SampleSdpStrings.firefoxP2pSdp);
105105
const desc = new RTCSessionDescription({

modules/sdp/LocalSdpMunger.ts

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import { isEqual } from 'lodash-es';
2+
3+
import { MediaDirection } from '../../service/RTC/MediaDirection';
4+
import { MediaType } from '../../service/RTC/MediaType';
5+
import TraceablePeerConnection from '../RTC/TraceablePeerConnection';
6+
import browser from '../browser';
7+
8+
import { MLineWrap, SdpTransformWrap } from './SdpTransformUtil';
9+
10+
11+
export interface ITPCSSRCInfo {
12+
groups: ITPCGroupInfo[];
13+
msid: string;
14+
ssrcs: number[];
15+
}
16+
17+
export interface ITPCGroupInfo {
18+
semantics: string;
19+
ssrcs: number[];
20+
}
21+
22+
export interface ISSRCAttribute {
23+
attribute: string;
24+
id: number;
25+
value: string;
26+
}
27+
28+
/**
29+
* Fakes local SDP exposed to {@link JingleSessionPC} through the local description getter. Modifies the SDP, so that
30+
* the stream identifiers are unique across all of the local PeerConnections and that the source names and video types
31+
* are injected so that Jicofo can use them to identify the sources.
32+
*/
33+
export default class LocalSdpMunger {
34+
private _localEndpointId: string;
35+
private _tpc: TraceablePeerConnection;
36+
37+
/**
38+
* Creates new <tt>LocalSdpMunger</tt> instance.
39+
*
40+
* @param {TraceablePeerConnection} tpc
41+
* @param {string} localEndpointId - The endpoint id of the local user.
42+
*/
43+
constructor(tpc: TraceablePeerConnection, localEndpointId: string) {
44+
this._tpc = tpc;
45+
this._localEndpointId = localEndpointId;
46+
}
47+
48+
/**
49+
* Updates or adds a 'msid' attribute for the local sources in the SDP. Also adds 'sourceName' and 'videoType'
50+
* (if applicable) attributes. All other source attributes like 'cname', 'label' and 'mslabel' are removed since
51+
* these are not processed by Jicofo.
52+
*
53+
* @param {MLineWrap} mediaSection - The media part (audio or video) of the session description which will be
54+
* modified in place.
55+
* @returns {void}
56+
* @private
57+
*/
58+
private _transformMediaIdentifiers(mediaSection: MLineWrap, ssrcMap: Map<string, ITPCSSRCInfo>): void {
59+
const mediaType = mediaSection._mLine.type;
60+
const mediaDirection = mediaSection._mLine.direction;
61+
const sources = [ ...new Set(mediaSection._mLine.ssrcs?.map(s => s.id)) ];
62+
let trackId = mediaSection._mLine.msid?.split(' ')[1];
63+
let sourceName: string | undefined;
64+
65+
if (ssrcMap.size) {
66+
const sortedSources = sources.slice().sort();
67+
68+
for (const [ id, trackSsrcs ] of ssrcMap.entries()) {
69+
if (isEqual(sortedSources, [ ...trackSsrcs.ssrcs ].sort())) {
70+
sourceName = id;
71+
}
72+
}
73+
for (const source of sources) {
74+
if ((mediaDirection === MediaDirection.SENDONLY || mediaDirection === MediaDirection.SENDRECV)
75+
&& sourceName) {
76+
const msid = mediaSection.ssrcs.find(ssrc => ssrc.id === source && ssrc.attribute === 'msid');
77+
78+
if (msid) {
79+
trackId = msid.value.split(' ')[1];
80+
}
81+
const generatedMsid = `${ssrcMap.get(sourceName).msid}-${this._tpc.id} ${trackId}-${this._tpc.id}`;
82+
const existingMsid = mediaSection.ssrcs
83+
.find(ssrc => ssrc.id === source && ssrc.attribute === 'msid');
84+
85+
// Always overwrite msid since we want the msid to be in this format even if the browser generates
86+
// one. '<endpoint_id>-<mediaType>-<trackIndex>-<tpcId>' example - d8ff91-video-0-1
87+
if (existingMsid) {
88+
existingMsid.value = generatedMsid;
89+
} else {
90+
mediaSection.ssrcs.push({
91+
attribute: 'msid',
92+
id: source,
93+
value: generatedMsid
94+
});
95+
}
96+
97+
// Inject source names as a=ssrc:3124985624 name:endpointA-v0
98+
mediaSection.ssrcs.push({
99+
attribute: 'name',
100+
id: source,
101+
value: sourceName
102+
});
103+
104+
const videoType = this._tpc.getLocalVideoTracks()
105+
.find(track => track.getSourceName() === sourceName)
106+
?.getVideoType();
107+
108+
if (mediaType === MediaType.VIDEO && videoType) {
109+
// Inject videoType as a=ssrc:1234 videoType:desktop.
110+
mediaSection.ssrcs.push({
111+
attribute: 'videoType',
112+
id: source,
113+
value: videoType
114+
});
115+
}
116+
}
117+
}
118+
}
119+
120+
// Ignore the 'label' and 'mslabel' attributes.
121+
mediaSection.ssrcs
122+
= mediaSection.ssrcs.filter(ssrc => ssrc.attribute !== 'label' && ssrc.attribute !== 'mslabel');
123+
124+
// Remove the 'cname' attribute on Firefox as a=ssrc line with only 'cname' attribute are present in the SDP
125+
// for recvonly SSRCs generated by createAnswer. These do not have to be signaled to the peers.
126+
if (browser.isFirefox()) {
127+
mediaSection.ssrcs = mediaSection.ssrcs.filter(ssrc => ssrc.attribute !== 'cname');
128+
}
129+
130+
// On FF when the user has started muted create answer will generate a recv only SSRC. We don't want to signal
131+
// this SSRC in order to reduce the load of the xmpp server for large calls. Therefore the SSRC needs to be
132+
// removed from the SDP.
133+
//
134+
// For all other use cases (when the user has had media but then the user has stopped it) we want to keep the
135+
// receive only SSRCs in the SDP. Otherwise source-remove will be triggered and the next time the user add a
136+
// track we will reuse the SSRCs and send source-add with the same SSRCs. This is problematic because of issues
137+
// on Chrome and FF (https://bugzilla.mozilla.org/show_bug.cgi?id=1768729) when removing and then adding the
138+
// same SSRC in the remote sdp the remote track is not rendered.
139+
if (browser.isFirefox()
140+
&& (mediaDirection === MediaDirection.RECVONLY || mediaDirection === MediaDirection.INACTIVE)
141+
&& (
142+
(mediaType === MediaType.VIDEO && !this._tpc._hasHadVideoTrack)
143+
|| (mediaType === MediaType.AUDIO && !this._tpc._hasHadAudioTrack)
144+
)
145+
) {
146+
mediaSection.ssrcs = undefined;
147+
mediaSection.ssrcGroups = undefined;
148+
}
149+
}
150+
151+
/**
152+
* This transformation will make sure that stream identifiers are unique across all of the local PeerConnections
153+
* even if the same stream is used by multiple instances at the same time. It also injects 'sourceName' and
154+
* 'videoType' attribute.
155+
*
156+
* @param {RTCSessionDescription} sessionDesc - The local session description (this instance remains unchanged).
157+
* @param {Map<string, TPCSSRCInfo>} ssrcMap - The SSRC and source map for the local tracks.
158+
* @return {RTCSessionDescription} - Transformed local session description
159+
* (a modified copy of the one given as the input).
160+
*/
161+
transformStreamIdentifiers(sessionDesc: RTCSessionDescription, ssrcMap: Map<string, ITPCSSRCInfo>): RTCSessionDescription {
162+
if (!sessionDesc?.sdp || !sessionDesc.type) {
163+
return sessionDesc;
164+
}
165+
166+
const transformer = new SdpTransformWrap(sessionDesc.sdp);
167+
const audioMLine = transformer.selectMedia(MediaType.AUDIO)?.[0];
168+
169+
if (audioMLine) {
170+
this._transformMediaIdentifiers(audioMLine, ssrcMap);
171+
}
172+
173+
const videoMlines = transformer.selectMedia(MediaType.VIDEO);
174+
175+
if (videoMlines && Array.isArray(videoMlines)) {
176+
for (const videoMLine of videoMlines) {
177+
this._transformMediaIdentifiers(videoMLine, ssrcMap);
178+
}
179+
}
180+
181+
return {
182+
sdp: transformer.toRawSDP(),
183+
type: sessionDesc.type
184+
} as RTCSessionDescription;
185+
}
186+
}

0 commit comments

Comments
 (0)