| <!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> |