tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RTCRtpTransceiver-setCodecPreferences.html (13679B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title>RTCRtpTransceiver.prototype.setCodecPreferences</title>
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <script src="./third_party/sdp/sdp.js"></script>
      7 <script>
      8  'use strict';
      9 
     10  test((t) => {
     11    const pc = new RTCPeerConnection();
     12    t.add_cleanup(() => pc.close());
     13    const transceiver = pc.addTransceiver('audio');
     14    const capabilities = RTCRtpReceiver.getCapabilities('audio');
     15    transceiver.setCodecPreferences(capabilities.codecs);
     16  }, `setCodecPreferences() on audio transceiver with codecs returned from RTCRtpReceiver.getCapabilities('audio') should succeed`);
     17 
     18  test((t) => {
     19    const pc = new RTCPeerConnection();
     20    t.add_cleanup(() => pc.close());
     21    const transceiver = pc.addTransceiver('video');
     22    const capabilities = RTCRtpReceiver.getCapabilities('video');
     23    transceiver.setCodecPreferences(capabilities.codecs);
     24  }, `setCodecPreferences() on video transceiver with codecs returned from RTCRtpReceiver.getCapabilities('video') should succeed`);
     25 
     26  test((t) => {
     27    const pc = new RTCPeerConnection();
     28    t.add_cleanup(() => pc.close());
     29    const transceiver = pc.addTransceiver('audio');
     30    transceiver.setCodecPreferences([]);
     31  }, `setCodecPreferences([]) should succeed`);
     32 
     33  test((t) => {
     34    const pc = new RTCPeerConnection();
     35    t.add_cleanup(() => pc.close());
     36    const transceiver = pc.addTransceiver('audio');
     37    const capabilities = RTCRtpReceiver.getCapabilities('audio');
     38    const { codecs } = capabilities;
     39 
     40    if(codecs.length >= 2) {
     41      const tmp = codecs[0];
     42      codecs[0] = codecs[1];
     43      codecs[1] = tmp;
     44    }
     45 
     46    transceiver.setCodecPreferences(codecs);
     47  }, `setCodecPreferences() with reordered codecs should succeed`);
     48 
     49  test((t) => {
     50    const pc = new RTCPeerConnection();
     51    t.add_cleanup(() => pc.close());
     52    const transceiver = pc.addTransceiver('video');
     53    const capabilities = RTCRtpReceiver.getCapabilities('video');
     54    const { codecs } = capabilities;
     55    // This test verifies that the mandatory VP8 codec is present
     56    // and can be preferred.
     57    const codec = codecs.find(c => c.mimeType === 'video/VP8');
     58    assert_true(!!codec, 'VP8 video codec was found');
     59    transceiver.setCodecPreferences([codec]);
     60  }, `setCodecPreferences() with only VP8 should succeed`);
     61 
     62  test(() => {
     63    const pc = new RTCPeerConnection();
     64    const transceiver = pc.addTransceiver('video');
     65    const capabilities = RTCRtpReceiver.getCapabilities('video');
     66    const { codecs } = capabilities;
     67    // This test verifies that the mandatory H264 codec is present
     68    // and can be preferred.
     69    const codec = codecs.find(c => c.mimeType === 'video/H264');
     70    assert_true(!!codec, 'H264 video codec was found');
     71    transceiver.setCodecPreferences([codec]);
     72  }, `setCodecPreferences() with only H264 should succeed`);
     73 
     74  async function getRTPMapLinesWithCodecAsFirst(firstCodec)
     75  {
     76     const codecs = RTCRtpReceiver.getCapabilities('video').codecs;
     77     codecs.forEach((codec, idx) => {
     78       if (codec.mimeType === firstCodec) {
     79        codecs.splice(idx, 1);
     80        codecs.unshift(codec);
     81       }
     82     });
     83 
     84     const pc = new RTCPeerConnection();
     85     const transceiver = pc.addTransceiver('video');
     86     transceiver.setCodecPreferences(codecs);
     87     const offer = await pc.createOffer();
     88 
     89     return offer.sdp.split('\r\n').filter(line => line.startsWith('a=rtpmap:'));
     90  }
     91 
     92  promise_test(async () => {
     93    const lines = await getRTPMapLinesWithCodecAsFirst('video/H264');
     94 
     95    assert_greater_than(lines.length, 1);
     96    assert_true(lines[0].indexOf('H264') !== -1, 'H264 should be the first codec');
     97  }, `setCodecPreferences() should allow setting H264 as first codec`);
     98 
     99  promise_test(async () => {
    100    const lines = await getRTPMapLinesWithCodecAsFirst('video/VP8');
    101 
    102    assert_greater_than(lines.length, 1);
    103    assert_true(lines[0].indexOf('VP8') !== -1, 'VP8 should be the first codec');
    104  }, `setCodecPreferences() should allow setting VP8 as first codec`);
    105 
    106  test((t) => {
    107    const pc = new RTCPeerConnection();
    108    t.add_cleanup(() => pc.close());
    109    const transceiver = pc.addTransceiver('audio');
    110    const capabilities = RTCRtpReceiver.getCapabilities('video');
    111    assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(capabilities.codecs));
    112  }, `setCodecPreferences() on audio transceiver with codecs returned from getCapabilities('video') should throw InvalidModificationError`);
    113 
    114  test((t) => {
    115    const pc = new RTCPeerConnection();
    116    t.add_cleanup(() => pc.close());
    117    const transceiver = pc.addTransceiver('audio');
    118    const codecs = [{
    119      mimeType: 'data',
    120      clockRate: 2000,
    121      channels: 2,
    122      sdpFmtpLine: '0-15'
    123    }];
    124 
    125    assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
    126  }, `setCodecPreferences() with user defined codec with invalid mimeType should throw InvalidModificationError`);
    127 
    128  test((t) => {
    129    const pc = new RTCPeerConnection();
    130    t.add_cleanup(() => pc.close());
    131    const transceiver = pc.addTransceiver('audio');
    132    const codecs = [{
    133      mimeType: 'audio/piepiper',
    134      clockRate: 2000,
    135      channels: 2,
    136      sdpFmtpLine: '0-15'
    137    }];
    138 
    139    assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
    140  }, `setCodecPreferences() with user defined codec should throw InvalidModificationError`);
    141 
    142  test((t) => {
    143    const pc = new RTCPeerConnection();
    144    t.add_cleanup(() => pc.close());
    145    const transceiver = pc.addTransceiver('audio');
    146    const capabilities = RTCRtpReceiver.getCapabilities('audio');
    147    const codecs = [
    148      ...capabilities.codecs,
    149      {
    150        mimeType: 'audio/piepiper',
    151        clockRate: 2000,
    152        channels: 2,
    153        sdpFmtpLine: '0-15'
    154      }];
    155 
    156    assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
    157  }, `setCodecPreferences() with user defined codec together with codecs returned from getCapabilities() should throw InvalidModificationError`);
    158 
    159  test((t) => {
    160    const pc = new RTCPeerConnection();
    161    t.add_cleanup(() => pc.close());
    162    const transceiver = pc.addTransceiver('audio');
    163    const capabilities = RTCRtpReceiver.getCapabilities('audio');
    164    const codecs = [capabilities.codecs[0]];
    165    codecs[0].clockRate = codecs[0].clockRate / 2;
    166 
    167    assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
    168  }, `setCodecPreferences() with modified codec clock rate should throw InvalidModificationError`);
    169 
    170  test((t) => {
    171    const pc = new RTCPeerConnection();
    172    t.add_cleanup(() => pc.close());
    173    const transceiver = pc.addTransceiver('audio');
    174    const capabilities = RTCRtpReceiver.getCapabilities('audio');
    175    const codecs = [capabilities.codecs[0]];
    176    codecs[0].channels = codecs[0].channels + 11;
    177 
    178    assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
    179  }, `setCodecPreferences() with modified codec channel count should throw InvalidModificationError`);
    180 
    181  test((t) => {
    182    const pc = new RTCPeerConnection();
    183    t.add_cleanup(() => pc.close());
    184    const transceiver = pc.addTransceiver('audio');
    185    const capabilities = RTCRtpReceiver.getCapabilities('audio');
    186    const codecs = [capabilities.codecs[0]];
    187    codecs[0].sdpFmtpLine = "modifiedparameter=1";
    188 
    189    assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
    190  }, `setCodecPreferences() with modified codec parameters should throw InvalidModificationError`);
    191 
    192  test((t) => {
    193    const pc = new RTCPeerConnection();
    194    t.add_cleanup(() => pc.close());
    195    const transceiver = pc.addTransceiver('audio');
    196    const capabilities = RTCRtpReceiver.getCapabilities('audio');
    197 
    198    const { codecs } = capabilities;
    199    assert_greater_than(codecs.length, 0,
    200      'Expect at least one codec available');
    201 
    202    const [ codec ] = codecs;
    203    const { channels=2 } = codec;
    204    codec.channels = channels+1;
    205 
    206    assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
    207  }, `setCodecPreferences() with modified codecs returned from getCapabilities() should throw InvalidModificationError`);
    208 
    209  promise_test(async (t) => {
    210    const pc = new RTCPeerConnection();
    211    t.add_cleanup(() => pc.close());
    212    const transceiver = pc.addTransceiver('audio');
    213    const {codecs} = RTCRtpReceiver.getCapabilities('audio');
    214    // Reorder codecs, put PCMU/PCMA first.
    215    let firstCodec;
    216    let i;
    217    for (i = 0; i < codecs.length; i++) {
    218      const codec = codecs[i];
    219      if (codec.mimeType === 'audio/PCMU' || codec.mimeType === 'audio/PCMA') {
    220        codecs.splice(i, 1);
    221        codecs.unshift(codec);
    222        firstCodec = codec.mimeType.substr(6);
    223        break;
    224      }
    225    }
    226    assert_not_equals(firstCodec, undefined, 'PCMU or PCMA codec not found');
    227    transceiver.setCodecPreferences(codecs);
    228 
    229    const offer = await pc.createOffer();
    230    const mediaSection = SDPUtils.getMediaSections(offer.sdp)[0];
    231    const rtpParameters = SDPUtils.parseRtpParameters(mediaSection);
    232    assert_equals(rtpParameters.codecs[0].name, firstCodec);
    233  }, `setCodecPreferences() modifies the order of audio codecs in createOffer`);
    234 
    235  promise_test(async (t) => {
    236    const pc = new RTCPeerConnection();
    237    t.add_cleanup(() => pc.close());
    238    const transceiver = pc.addTransceiver('video');
    239    const {codecs} = RTCRtpReceiver.getCapabilities('video');
    240    // Reorder codecs, swap H264 and VP8.
    241    let vp8 = -1;
    242    let h264 = -1;
    243    let firstCodec;
    244    let i;
    245    for (i = 0; i < codecs.length; i++) {
    246      const codec = codecs[i];
    247      if (codec.mimeType === 'video/VP8' && vp8 === -1) {
    248        vp8 = i;
    249        if (h264 !== -1) {
    250          codecs[vp8] = codecs[h264];
    251          codecs[h264] = codec;
    252          firstCodec = 'VP8';
    253          break;
    254        }
    255      }
    256      if (codec.mimeType === 'video/H264' && h264 === -1) {
    257        h264 = i;
    258        if (vp8 !== -1) {
    259          codecs[h264] = codecs[vp8];
    260          codecs[vp8] = codec;
    261          firstCodec = 'H264';
    262          break;
    263        }
    264      }
    265    }
    266    assert_not_equals(firstCodec, undefined, 'VP8 and H264 codecs not found');
    267    transceiver.setCodecPreferences(codecs);
    268 
    269    const offer = await pc.createOffer();
    270    const mediaSection = SDPUtils.getMediaSections(offer.sdp)[0];
    271    const rtpParameters = SDPUtils.parseRtpParameters(mediaSection);
    272    assert_equals(rtpParameters.codecs[0].name, firstCodec);
    273  }, `setCodecPreferences() modifies the order of video codecs in createOffer`);
    274 
    275  ['rtx', 'red', 'ulpfec'].forEach(resiliencyMechanism => {
    276    promise_test(async (t) => {
    277      const pc = new RTCPeerConnection();
    278      t.add_cleanup(() => pc.close());
    279      const transceiver = pc.addTransceiver('video');
    280      const {codecs} = RTCRtpReceiver.getCapabilities('video');
    281      const filteredCodecs = codecs.
    282        filter(codec => codec.mimeType !== 'video/' + resiliencyMechanism);
    283      assert_not_equals(codecs.length, filteredCodecs.length);
    284      transceiver.setCodecPreferences(filteredCodecs);
    285 
    286      const offer = await pc.createOffer();
    287      const mediaSection = SDPUtils.getMediaSections(offer.sdp)[0];
    288      const rtpParameters = SDPUtils.parseRtpParameters(mediaSection);
    289      assert_equals(rtpParameters.codecs.find(codec => codec.name === resiliencyMechanism),
    290                    undefined);
    291    }, `setCodecPreferences() can remove ${resiliencyMechanism}`);
    292  });
    293 
    294  // Tests the note removed as result of discussion in
    295  // https://github.com/w3c/webrtc-pc/issues/2933
    296  promise_test(async (t) => {
    297    const pc1 = new RTCPeerConnection();
    298    t.add_cleanup(() => pc1.close());
    299    const pc2 = new RTCPeerConnection();
    300    t.add_cleanup(() => pc2.close());
    301 
    302    const transceiver = pc1.addTransceiver('video');
    303    const {codecs} = RTCRtpReceiver.getCapabilities('video');
    304    const vp8 = codecs.find(codec => codec.mimeType === 'video/VP8');
    305    const h264 = codecs.find(codec => codec.mimeType === 'video/H264');
    306    const thirdCodec = codecs.find(codec => ['video/VP9', 'video/AV1'].includes(codec.mimeType));
    307    assert_true(!!vp8);
    308    assert_true(!!h264);
    309    assert_true(!!thirdCodec);
    310 
    311    transceiver.setCodecPreferences([vp8, thirdCodec]);
    312    await pc1.setLocalDescription();
    313    await pc2.setRemoteDescription(pc1.localDescription);
    314    const transceiver2 = pc2.getTransceivers()[0];
    315    transceiver2.setCodecPreferences([h264, thirdCodec, vp8]);
    316    await pc2.setLocalDescription();
    317    await pc1.setRemoteDescription(pc2.localDescription);
    318    const mediaSection = SDPUtils.getMediaSections(pc2.localDescription.sdp)[0];
    319    const rtpParameters = SDPUtils.parseRtpParameters(mediaSection);
    320    // Order is determined by pc2 but H264 is not present.
    321    assert_equals(rtpParameters.codecs.length, 2);
    322    assert_equals(rtpParameters.codecs[0].name, thirdCodec.mimeType.substring(6));
    323    assert_equals(rtpParameters.codecs[1].name, 'VP8');
    324 
    325  }, `setCodecPreferences() filters on receiver and prefers receiver order`);
    326 
    327 ["audio", "video"].forEach(kind => promise_test(async (t) => {
    328    const pc = new RTCPeerConnection();
    329    t.add_cleanup(() => pc.close());
    330    const [codec] = RTCRtpReceiver.getCapabilities(kind).codecs;
    331    codec.mimeType = codec.mimeType.toUpperCase();
    332    const transceiver = pc.addTransceiver(kind);
    333    transceiver.setCodecPreferences([codec]);
    334 
    335    codec.mimeType = codec.mimeType.toLowerCase();
    336    transceiver.setCodecPreferences([codec]);
    337  }, `setCodecPreferences should accept ${kind} codecs regardless of mimeType case`));
    338 
    339 </script>