Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/handlers/sdp/commonUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,15 @@ export function extractRtpCapabilities({
}): RtpCapabilities {
// Map of RtpCodecParameters indexed by payload type.
const codecsMap: Map<number, RtpCodecCapability> = new Map();
// Map of RtpHeaderExtensions indexed by preferred id.
const headerExtensionMap: Map<number, RtpHeaderExtension> = new Map();
// Map of RtpHeaderExtensions indexed by kind and preferred id.
//
// NOTE: It must be indexed by kind and id rather than just by id. Since all
// m= sections are bundled, the browser gives the same id to a given extension
// in every m= section that supports it, so indexing just by id would collapse
// the audio and the video entry of such an extension into a single one with
// the kind of the last processed m= section, making the extension vanish for
// the other kind.
const headerExtensionMap: Map<string, RtpHeaderExtension> = new Map();

for (const m of sdpObject.media) {
const kind = m.type;
Expand Down Expand Up @@ -122,7 +129,10 @@ export function extractRtpCapabilities({
preferredId: ext.value,
};

headerExtensionMap.set(headerExtension.preferredId, headerExtension);
headerExtensionMap.set(
`${kind}:${headerExtension.preferredId}`,
headerExtension
);
}
}

Expand Down
61 changes: 61 additions & 0 deletions src/test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
*/

import { FakeMediaStreamTrack } from 'fake-mediastreamtrack';
import * as sdpTransform from 'sdp-transform';
import * as mediasoupClient from '../index';
import * as sdpCommonUtils from '../handlers/sdp/commonUtils';
import { UnsupportedError, InvalidStateError } from '../errors';
import * as utils from '../utils';
import type { RtpCapabilities } from '../RtpParameters';
Expand Down Expand Up @@ -1762,3 +1764,62 @@ describe('detectDevice() and detectDeviceAsync() assign proper handler based on
);
}
});

test('sdpCommonUtils.extractRtpCapabilities() does not mix up audio and video extensions sharing the same id', () => {
// NOTE: Since all m= sections are bundled, the browser gives the same id to
// a given RTP header extension in every m= section that supports it (here
// sdes:mid has id 4 in both the audio and the video m= section).
const sdp = [
'v=0',
'o=- 0 0 IN IP4 127.0.0.1',
's=-',
't=0 0',
'a=group:BUNDLE 0 1',
'm=audio 9 UDP/TLS/RTP/SAVPF 111',
'c=IN IP4 0.0.0.0',
'a=mid:0',
'a=sendonly',
'a=rtpmap:111 opus/48000/2',
'a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level',
'a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid',
'm=video 9 UDP/TLS/RTP/SAVPF 96',
'c=IN IP4 0.0.0.0',
'a=mid:1',
'a=sendonly',
'a=rtpmap:96 VP8/90000',
'a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid',
'a=extmap:13 urn:3gpp:video-orientation',
'',
].join('\r\n');

const rtpCapabilities = sdpCommonUtils.extractRtpCapabilities({
sdpObject: sdpTransform.parse(sdp),
});

expect(rtpCapabilities.headerExtensions).toEqual(
expect.arrayContaining([
{
kind: 'audio',
uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level',
preferredId: 1,
},
{
kind: 'audio',
uri: 'urn:ietf:params:rtp-hdrext:sdes:mid',
preferredId: 4,
},
{
kind: 'video',
uri: 'urn:ietf:params:rtp-hdrext:sdes:mid',
preferredId: 4,
},
{
kind: 'video',
uri: 'urn:3gpp:video-orientation',
preferredId: 13,
},
])
);

expect(rtpCapabilities.headerExtensions!.length).toBe(4);
});
Loading