import { RtpEncodingParameters } from '../../RtpParameters'; export function getRtpEncodings( { offerMediaObject }: { offerMediaObject: any } ): RtpEncodingParameters[] { const ssrcs = new Set(); for (const line of offerMediaObject.ssrcs || []) { const ssrc = line.id; ssrcs.add(ssrc); } if (ssrcs.size === 0) throw new Error('no a=ssrc lines found'); const ssrcToRtxSsrc = new Map(); // First assume RTX is used. for (const line of offerMediaObject.ssrcGroups || []) { if (line.semantics !== 'FID') continue; let [ ssrc, rtxSsrc ] = line.ssrcs.split(/\s+/); ssrc = Number(ssrc); rtxSsrc = Number(rtxSsrc); if (ssrcs.has(ssrc)) { // Remove both the SSRC and RTX SSRC from the set so later we know that they // are already handled. ssrcs.delete(ssrc); ssrcs.delete(rtxSsrc); // Add to the map. ssrcToRtxSsrc.set(ssrc, rtxSsrc); } } // If the set of SSRCs is not empty it means that RTX is not being used, so take // media SSRCs from there. for (const ssrc of ssrcs) { // Add to the map. ssrcToRtxSsrc.set(ssrc, null); } const encodings: RtpEncodingParameters[] = []; for (const [ ssrc, rtxSsrc ] of ssrcToRtxSsrc) { const encoding: RtpEncodingParameters = { ssrc }; if (rtxSsrc) encoding.rtx = { ssrc: rtxSsrc }; encodings.push(encoding); } return encodings; } /** * Adds multi-ssrc based simulcast into the given SDP media section offer. */ export function addLegacySimulcast( { offerMediaObject, numStreams }: { offerMediaObject: any; numStreams: number; } ): void { if (numStreams <= 1) throw new TypeError('numStreams must be greater than 1'); // Get the SSRC. const ssrcMsidLine = (offerMediaObject.ssrcs || []) .find((line: any) => line.attribute === 'msid'); if (!ssrcMsidLine) throw new Error('a=ssrc line with msid information not found'); const [ streamId, trackId ] = ssrcMsidLine.value.split(' '); const firstSsrc = ssrcMsidLine.id; let firstRtxSsrc; // Get the SSRC for RTX. (offerMediaObject.ssrcGroups || []) .some((line: any) => { if (line.semantics !== 'FID') return false; const ssrcs = line.ssrcs.split(/\s+/); if (Number(ssrcs[0]) === firstSsrc) { firstRtxSsrc = Number(ssrcs[1]); return true; } else { return false; } }); const ssrcCnameLine = offerMediaObject.ssrcs .find((line: any) => line.attribute === 'cname'); if (!ssrcCnameLine) throw new Error('a=ssrc line with cname information not found'); const cname = ssrcCnameLine.value; const ssrcs = []; const rtxSsrcs = []; for (let i = 0; i < numStreams; ++i) { ssrcs.push(firstSsrc + i); if (firstRtxSsrc) rtxSsrcs.push(firstRtxSsrc + i); } offerMediaObject.ssrcGroups = []; offerMediaObject.ssrcs = []; offerMediaObject.ssrcGroups.push( { semantics : 'SIM', ssrcs : ssrcs.join(' ') }); for (let i = 0; i < ssrcs.length; ++i) { const ssrc = ssrcs[i]; offerMediaObject.ssrcs.push( { id : ssrc, attribute : 'cname', value : cname }); offerMediaObject.ssrcs.push( { id : ssrc, attribute : 'msid', value : `${streamId} ${trackId}` }); } for (let i = 0; i < rtxSsrcs.length; ++i) { const ssrc = ssrcs[i]; const rtxSsrc = rtxSsrcs[i]; offerMediaObject.ssrcs.push( { id : rtxSsrc, attribute : 'cname', value : cname }); offerMediaObject.ssrcs.push( { id : rtxSsrc, attribute : 'msid', value : `${streamId} ${trackId}` }); offerMediaObject.ssrcGroups.push( { semantics : 'FID', ssrcs : `${ssrc} ${rtxSsrc}` }); } }