| <!doctype html> |
| <meta charset=utf-8> |
| <title>RTCPeerConnection RTP extensions</title> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="../third_party/sdp/sdp.js"></script> |
| <script> |
| 'use strict'; |
| |
| async function setup() { |
| const pc1 = new RTCPeerConnection(); |
| pc1.addTransceiver('audio'); |
| // Make sure there is more than one rid, since there's no reason to use |
| // rtp-stream-id/repaired-rtp-stream-id otherwise. Some implementations |
| // may use them for unicast anyway, which isn't a spec violation, just |
| // a little silly. |
| pc1.addTransceiver('video', {sendEncodings: [{rid: '0'}, {rid: '1'}]}); |
| const offer = await pc1.createOffer(); |
| pc1.close(); |
| return offer.sdp; |
| } |
| |
| // Extensions that MUST be supported |
| const mandatoryExtensions = [ |
| // Directly referenced in WebRTC RTP usage |
| 'urn:ietf:params:rtp-hdrext:ssrc-audio-level', // RFC 8834 5.2.2 |
| 'urn:ietf:params:rtp-hdrext:sdes:mid', // RFC 8834 5.2.4 |
| 'urn:3gpp:video-orientation', // RFC 8834 5.2.5 |
| // Required for support of simulcast with RID |
| 'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id', // RFC 8852 4.3 |
| 'urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id', // RFC 8852 4.4 |
| ]; |
| |
| // For further testing: |
| // - Add test for rapid synchronization - RFC 8834 5.2.1 |
| // - Add test for encrypted header extensions (RFC 6904) |
| // - Separate tests for extensions in audio and video sections |
| |
| for (const extension of mandatoryExtensions) { |
| promise_test(async t => { |
| const sdp = await setup(); |
| const extensions = SDPUtils.matchPrefix(sdp, 'a=extmap:') |
| .map(SDPUtils.parseExtmap); |
| assert_true(!!extensions.find(ext => ext.uri === extension)); |
| }, `RTP header extension ${extension} is present in offer`); |
| } |
| |
| // Test for illegal remote behavior: Reassignment of hdrext ID |
| // in a subsequent offer/answer cycle. |
| promise_test(async t => { |
| const pc1 = new RTCPeerConnection(); |
| t.add_cleanup(() => pc1.close()); |
| const pc2 = new RTCPeerConnection(); |
| t.add_cleanup(() => pc2.close()); |
| |
| pc1.addTransceiver('audio'); |
| await pc1.setLocalDescription(); |
| await pc2.setRemoteDescription(pc1.localDescription); |
| await pc2.setLocalDescription(); |
| await pc1.setRemoteDescription(pc2.localDescription); |
| // Do a second offer/answer cycle. |
| await pc1.setLocalDescription(); |
| await pc2.setRemoteDescription(pc1.localDescription); |
| const answer = await pc2.createAnswer(); |
| |
| // Swap the extension number of the two required extensions |
| answer.sdp = answer.sdp.replace('urn:ietf:params:rtp-hdrext:ssrc-audio-level', |
| 'xyzzy') |
| .replace('urn:ietf:params:rtp-hdrext:sdes:mid', |
| 'urn:ietf:params:rtp-hdrext:ssrc-audio-level') |
| .replace('xyzzy', |
| 'urn:ietf:params:rtp-hdrext:sdes:mid'); |
| |
| return promise_rejects_dom(t, 'InvalidAccessError', |
| pc1.setRemoteDescription(answer)); |
| }, 'RTP header extension reassignment causes failure'); |
| |
| </script> |