1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
<!doctype html>
<meta charset=utf-8>
<title>Change of msid in remote description should trigger related track events</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
const sdpBase =`v=0
o=- 5511237691691746 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0
a=ice-options:trickle
a=ice-lite
a=msid-semantic:WMS *
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 9 102 0 8 105 13 110 113 126
c=IN IP6 ::
a=rtcp:9 IN IP6 ::
a=rtcp-mux
a=mid:0
a=sendrecv
a=ice-ufrag:z0i8R3C9C4hPRWls
a=ice-pwd:O7bPpOFAqasqoidV4yxnFVbc
a=ice-lite
a=fingerprint:sha-256 B7:9C:0D:C9:D1:42:57:97:82:4D:F9:B7:93:75:49:C3:42:21:5A:DD:9C:B5:ED:53:53:F0:B4:C8:AE:88:7A:E7
a=setup:actpass
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
a=rtpmap:0 PCMU/8000`;
const sdp0 = sdpBase + `
`;
const sdp1 = sdpBase + `
a=msid:1 2
a=ssrc:3 cname:4
a=ssrc:3 msid:1 2
`;
const sdp2 = sdpBase + `
a=ssrc:3 cname:4
a=ssrc:3 msid:1 2
`;
const sdp3 = sdpBase + `
a=msid:1 2
a=ssrc:3 cname:4
a=ssrc:3 msid:3 2
`;
const sdp4 = sdp1.replace('msid-semantic', 'unknownattr');
const sdp5 = sdpBase + `
a=msid:-
`;
const sdp6 = sdpBase + `
a=msid:1 2
a=msid:1 2
`;
async function applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp)
{
const testTrackPromise = new Promise(resolve => {
pc.ontrack = (event) => { resolve([event.track, event.streams]); };
});
await pc.setRemoteDescription({type: 'offer', sdp: sdp});
return testTrackPromise;
}
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
const [track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp0);
assert_equals(streams.length, 1, "track event has a stream");
}, "When a=msid is absent, the track should still be associated with a stream");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
const [track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp1);
assert_equals(streams.length, 1, "track event has a stream");
assert_equals(streams[0].id, "1", "msid should match");
}, "Source-level msid should be ignored if media-level msid is present");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
const [track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp2);
assert_equals(streams.length, 1, "track event has a stream");
assert_equals(streams[0].id, "1", "msid should match");
}, "Source-level msid should be parsed if media-level msid is absent");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
let track;
let streams;
try {
[track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp3);
} catch (e) {
return;
}
assert_equals(streams.length, 1, "track event has a stream");
assert_equals(streams[0].id, "1", "msid should match");
}, "Source-level msid should be ignored, or an error should be thrown, if a different media-level msid is present");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
const [track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp4);
assert_equals(streams.length, 1, "track event has a stream");
assert_equals(streams[0].id, "1", "msid should match");
}, "stream ids should be found even if msid-semantic is absent");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
const [track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp5);
assert_equals(streams.length, 0, "track event has no stream");
}, "a=msid:- should result in a track event with no streams");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
const [track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp6);
assert_equals(streams.length, 1, "track event has one stream");
}, "Duplicate a=msid should result in a track event with one stream");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
const [track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp1);
assert_equals(streams.length, 1, "track event has a stream");
assert_equals(streams[0].id, "1", "msid should match");
const stream = streams[0];
await pc.setLocalDescription(await pc.createAnswer());
const testTrackPromise = new Promise((resolve) => { stream.onremovetrack = resolve; });
await pc.setRemoteDescription({type: 'offer', 'sdp': sdp0});
await testTrackPromise;
assert_equals(stream.getAudioTracks().length, 0, "stream should be empty");
}, "Applying a remote description with removed msid should trigger firing a removetrack event on the corresponding stream");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
let [track0, streams0] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp0);
await pc.setLocalDescription(await pc.createAnswer());
let [track1, streams1] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp1);
assert_equals(streams1.length, 1, "track event has a stream");
assert_equals(streams1[0].id, "1", "msid should match");
assert_equals(streams1[0].getTracks()[0], track0, "track should match");
}, "Applying a remote description with a new msid should trigger firing an event with populated streams");
</script>
|