tor-browser

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

RTCRtpParameters-codec.html (20612B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8">
      3 <title>RTCRtpEncodingParameters codec property</title>
      4 <meta name="timeout" content="long">
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src="RTCPeerConnection-helper.js"></script>
      8 <script src="third_party/sdp/sdp.js"></script>
      9 <script src="simulcast/simulcast.js"></script>
     10 <script>
     11  'use strict';
     12 
     13  function arrayEquals(a, b) {
     14    return Array.isArray(a) && Array.isArray(b) &&
     15        a.length === b.length &&
     16        a.every((val, i) => val === b[i]);
     17  }
     18 
     19  async function sleep(timeout) {
     20    return new Promise(resolve => {
     21      step_timeout(() => {
     22        resolve();
     23      }, timeout);
     24    });
     25  }
     26 
     27  function findFirstCodec(name) {
     28    return RTCRtpReceiver.getCapabilities(name.split('/')[0]).codecs.filter(c => c.mimeType.localeCompare(name, undefined, { sensitivity: 'base' }) === 0)[0];
     29  }
     30 
     31  function codecsNotMatching(mimeType) {
     32    return RTCRtpReceiver.getCapabilities(mimeType.split('/')[0]).codecs.filter(c => c.mimeType.localeCompare(mimeType, undefined, {sensitivity: 'base'}) !== 0);
     33  }
     34 
     35  function assertCodecEquals(a, b) {
     36    assert_equals(a.mimeType, b.mimeType);
     37    assert_equals(a.clockRate, b.clockRate);
     38    assert_equals(a.channels, b.channels);
     39    assert_equals(a.sdpFmtpLine, b.sdpFmtpLine);
     40  }
     41 
     42  async function codecsForSender(sender) {
     43    const rids = sender.getParameters().encodings.map(e => e.rid);
     44    const stats = await sender.getStats();
     45    const codecs = [...stats]
     46      .filter(([k, v]) => v.type === 'outbound-rtp')
     47      .sort(([k, v], [k2, v2]) => rids.indexOf(v.rid) - rids.indexOf(v2.rid))
     48      .map(([k, v]) => stats.get(v.codecId).mimeType);
     49    return codecs;
     50  }
     51 
     52  async function waitForAllLayers(t, sender) {
     53    const encodings_count = sender.getParameters().encodings.length;
     54    return step_wait_async(t, async () => {
     55      const stats = await sender.getStats();
     56      return [...stats]
     57        .filter(([k, v]) => v.type === 'outbound-rtp').length == encodings_count;
     58    }, `Wait for ${encodings_count} layers to start`);
     59  }
     60 
     61  function step_wait_async(t, cond, description, timeout=3000, interval=100) {
     62    return new Promise(resolve => {
     63      var timeout_full = timeout * t.timeout_multiplier;
     64      var remaining = Math.ceil(timeout_full / interval);
     65 
     66      var wait_for_inner = t.step_func(async () => {
     67          if (await cond()) {
     68              resolve();
     69          } else {
     70              if(remaining === 0) {
     71                  assert(false, "step_wait_async", description,
     72                          "Timed out waiting on condition");
     73              }
     74              remaining--;
     75              await sleep(interval);
     76              wait_for_inner();
     77          }
     78      });
     79 
     80      wait_for_inner();
     81    });
     82  }
     83 
     84  promise_test(async t => {
     85    const pc = new RTCPeerConnection();
     86    t.add_cleanup(() => pc.close());
     87 
     88    const { sender } = pc.addTransceiver('audio');
     89 
     90    let param = sender.getParameters();
     91    let encoding = param.encodings[0];
     92 
     93    assert_equals(encoding.codec, undefined);
     94  }, `Codec should be undefined by default on audio encodings`);
     95 
     96  promise_test(async t => {
     97    const pc = new RTCPeerConnection();
     98    t.add_cleanup(() => pc.close());
     99 
    100    const { sender } = pc.addTransceiver('video');
    101 
    102    let param = sender.getParameters();
    103    let encoding = param.encodings[0];
    104 
    105    assert_equals(encoding.codec, undefined);
    106  }, `Codec should be undefined by default on video encodings`);
    107 
    108  promise_test(async t => {
    109    const pc = new RTCPeerConnection();
    110    t.add_cleanup(() => pc.close());
    111 
    112    const opus = findFirstCodec('audio/opus');
    113 
    114    const { sender } = pc.addTransceiver('audio', {
    115      sendEncodings: [{codec: opus}],
    116    });
    117 
    118    let param = sender.getParameters();
    119    let encoding = param.encodings[0];
    120 
    121    assertCodecEquals(opus, encoding.codec);
    122  }, `Creating an audio sender with addTransceiver and codec should work`);
    123 
    124  promise_test(async t => {
    125    const pc = new RTCPeerConnection();
    126    t.add_cleanup(() => pc.close());
    127 
    128    const vp8 = findFirstCodec('video/VP8');
    129 
    130    const { sender } = pc.addTransceiver('video', {
    131      sendEncodings: [{codec: vp8}],
    132    });
    133 
    134    let param = sender.getParameters();
    135    let encoding = param.encodings[0];
    136 
    137    assertCodecEquals(vp8, encoding.codec);
    138  }, `Creating a video sender with addTransceiver and codec should work`);
    139 
    140  promise_test(async t => {
    141    const pc = new RTCPeerConnection();
    142    t.add_cleanup(() => pc.close());
    143 
    144    const opus = findFirstCodec('audio/opus');
    145 
    146    const { sender } = pc.addTransceiver('audio');
    147 
    148    let param = sender.getParameters();
    149    let encoding = param.encodings[0];
    150 
    151    encoding.codec = opus;
    152    await sender.setParameters(param);
    153    param = sender.getParameters();
    154    encoding = param.encodings[0];
    155 
    156    assertCodecEquals(opus, encoding.codec);
    157 
    158    delete encoding.codec;
    159    await sender.setParameters(param);
    160    param = sender.getParameters();
    161    encoding = param.encodings[0];
    162 
    163    assert_equals(encoding.codec, undefined);
    164  }, `Setting codec on an audio sender with setParameters should work`);
    165 
    166  promise_test(async t => {
    167    const pc = new RTCPeerConnection();
    168    t.add_cleanup(() => pc.close());
    169 
    170    const vp8 = findFirstCodec('video/VP8');
    171 
    172    const { sender } = pc.addTransceiver('video');
    173 
    174    let param = sender.getParameters();
    175    let encoding = param.encodings[0];
    176 
    177    encoding.codec = vp8;
    178    await sender.setParameters(param);
    179    param = sender.getParameters();
    180    encoding = param.encodings[0];
    181 
    182    assertCodecEquals(vp8, encoding.codec);
    183 
    184    delete encoding.codec;
    185    await sender.setParameters(param);
    186    param = sender.getParameters();
    187    encoding = param.encodings[0];
    188 
    189    assert_equals(encoding.codec, undefined);
    190  }, `Setting codec on a video sender with setParameters should work`);
    191 
    192  promise_test(async t => {
    193    const pc = new RTCPeerConnection();
    194    t.add_cleanup(() => pc.close());
    195 
    196    const newCodec = {
    197      mimeType: "audio/newCodec",
    198      clockRate: 90000,
    199      channel: 2,
    200    };
    201 
    202    assert_throws_dom('OperationError', () => pc.addTransceiver('audio', {
    203      sendEncodings: [{codec: newCodec}],
    204    }));
    205  }, `Creating an audio sender with addTransceiver and non-existing codec should throw OperationError`);
    206 
    207  promise_test(async t => {
    208    const pc = new RTCPeerConnection();
    209    t.add_cleanup(() => pc.close());
    210 
    211    const newCodec = {
    212      mimeType: "dummy/newCodec",
    213      clockRate: 90000,
    214      channel: 2,
    215    };
    216 
    217    assert_throws_dom('OperationError', () => pc.addTransceiver('audio', {
    218      sendEncodings: [{codec: newCodec}],
    219    }));
    220  }, `Creating an audio sender with addTransceiver and non-existing codec type should throw OperationError`);
    221 
    222  promise_test(async t => {
    223    const pc = new RTCPeerConnection();
    224    t.add_cleanup(() => pc.close());
    225 
    226    const newCodec = {
    227      mimeType: "video/newCodec",
    228      clockRate: 90000,
    229    };
    230 
    231    assert_throws_dom('OperationError', () => pc.addTransceiver('video', {
    232      sendEncodings: [{codec: newCodec}],
    233    }));
    234  }, `Creating a video sender with addTransceiver and non-existing codec should throw OperationError`);
    235 
    236  promise_test(async t => {
    237    const pc = new RTCPeerConnection();
    238    t.add_cleanup(() => pc.close());
    239 
    240    const newCodec = {
    241      mimeType: "dummy/newCodec",
    242      clockRate: 90000,
    243    };
    244 
    245    assert_throws_dom('OperationError', () => pc.addTransceiver('video', {
    246      sendEncodings: [{codec: newCodec}],
    247    }));
    248  }, `Creating a video sender with addTransceiver and non-existing codec type should throw OperationError`);
    249 
    250  promise_test(async t => {
    251    const pc = new RTCPeerConnection();
    252    t.add_cleanup(() => pc.close());
    253 
    254    const newCodec = {
    255      mimeType: "audio/newCodec",
    256      clockRate: 90000,
    257      channel: 2,
    258    };
    259 
    260    const { sender } = pc.addTransceiver('audio');
    261 
    262    let param = sender.getParameters();
    263    let encoding = param.encodings[0];
    264 
    265    encoding.codec = newCodec;
    266    await promise_rejects_dom(t, "InvalidModificationError", sender.setParameters(param));
    267  }, `Setting a non-existing codec on an audio sender with setParameters should throw InvalidModificationError`);
    268 
    269  promise_test(async t => {
    270    const pc = new RTCPeerConnection();
    271    t.add_cleanup(() => pc.close());
    272 
    273    const newCodec = {
    274      mimeType: "video/newCodec",
    275      clockRate: 90000,
    276    };
    277 
    278    const { sender } = pc.addTransceiver('video');
    279 
    280    let param = sender.getParameters();
    281    let encoding = param.encodings[0];
    282 
    283    encoding.codec = newCodec;
    284    await promise_rejects_dom(t, "InvalidModificationError", sender.setParameters(param));
    285  }, `Setting a non-existing codec on a video sender with setParameters should throw InvalidModificationError`);
    286 
    287  promise_test(async t => {
    288    const pc1 = new RTCPeerConnection();
    289    t.add_cleanup(() => pc1.close());
    290    const pc2 = new RTCPeerConnection();
    291    t.add_cleanup(() => pc2.close());
    292 
    293    const opus = findFirstCodec('audio/opus');
    294    const nonOpus = codecsNotMatching(opus.mimeType);
    295 
    296    const transceiver = pc1.addTransceiver('audio');
    297    exchangeIceCandidates(pc1, pc2);
    298    const negotiated = exchangeOfferAnswer(pc1, pc2);
    299    const trackEvent = await new Promise(r => pc2.ontrack = r);
    300    trackEvent.transceiver.setCodecPreferences(nonOpus);
    301    await negotiated;
    302 
    303    const sender = transceiver.sender;
    304    let param = sender.getParameters();
    305    let encoding = param.encodings[0];
    306 
    307    encoding.codec = opus;
    308    await promise_rejects_dom(t, "InvalidModificationError", sender.setParameters(param));
    309  }, `Setting a non-preferred codec on an audio sender with setParameters should throw InvalidModificationError`);
    310 
    311  promise_test(async t => {
    312    const pc1 = new RTCPeerConnection();
    313    t.add_cleanup(() => pc1.close());
    314    const pc2 = new RTCPeerConnection();
    315    t.add_cleanup(() => pc2.close());
    316 
    317    const vp8 = findFirstCodec('video/VP8');
    318    const nonVP8 = codecsNotMatching(vp8.mimeType);
    319 
    320    const transceiver = pc1.addTransceiver('video');
    321    exchangeIceCandidates(pc1, pc2);
    322    const negotiated = exchangeOfferAnswer(pc1, pc2);
    323    const trackEvent = await new Promise(r => pc2.ontrack = r);
    324    trackEvent.transceiver.setCodecPreferences(nonVP8);
    325    await negotiated;
    326 
    327    const sender = transceiver.sender;
    328    let param = sender.getParameters();
    329    let encoding = param.encodings[0];
    330 
    331    encoding.codec = vp8;
    332    await promise_rejects_dom(t, "InvalidModificationError", sender.setParameters(param));
    333  }, `Setting a non-preferred codec on a video sender with setParameters should throw InvalidModificationError`);
    334 
    335  promise_test(async (t) => {
    336    const pc1 = new RTCPeerConnection();
    337    const pc2 = new RTCPeerConnection();
    338    t.add_cleanup(() => pc1.close());
    339    t.add_cleanup(() => pc2.close());
    340 
    341    const opus = findFirstCodec('audio/opus');
    342    const nonOpus = codecsNotMatching(opus.mimeType);
    343 
    344    const transceiver = pc1.addTransceiver('audio');
    345 
    346    exchangeIceCandidates(pc1, pc2);
    347    const negotiated = exchangeOfferAnswer(pc1, pc2);
    348    const trackEvent = await new Promise(r => pc2.ontrack = r);
    349    trackEvent.transceiver.setCodecPreferences(nonOpus);
    350    await negotiated;
    351 
    352    const sender = transceiver.sender;
    353    let param = sender.getParameters();
    354    let encoding = param.encodings[0];
    355 
    356    encoding.codec = opus;
    357    await promise_rejects_dom(t, "InvalidModificationError", sender.setParameters(param));
    358  }, `Setting a non-negotiated codec on an audio sender with setParameters should throw InvalidModificationError`);
    359 
    360  promise_test(async (t) => {
    361    const pc1 = new RTCPeerConnection();
    362    const pc2 = new RTCPeerConnection();
    363    t.add_cleanup(() => pc1.close());
    364    t.add_cleanup(() => pc2.close());
    365 
    366    const vp8 = findFirstCodec('video/VP8');
    367    const nonVP8 = codecsNotMatching(vp8.mimeType);
    368 
    369    const transceiver = pc1.addTransceiver('video');
    370    exchangeIceCandidates(pc1, pc2);
    371    const negotiated = exchangeOfferAnswer(pc1, pc2);
    372    const trackEvent = await new Promise(r => pc2.ontrack = r);
    373    trackEvent.transceiver.setCodecPreferences(nonVP8);
    374    await negotiated;
    375 
    376    const sender = transceiver.sender;
    377    let param = sender.getParameters();
    378    let encoding = param.encodings[0];
    379 
    380    encoding.codec = vp8;
    381    await promise_rejects_dom(t, "InvalidModificationError", sender.setParameters(param));
    382  }, `Setting a non-negotiated codec on a video sender with setParameters should throw InvalidModificationError`);
    383 
    384  promise_test(async (t) => {
    385    const pc1 = new RTCPeerConnection();
    386    const pc2 = new RTCPeerConnection();
    387    t.add_cleanup(() => pc1.close());
    388    t.add_cleanup(() => pc2.close());
    389 
    390    const opus = findFirstCodec('audio/opus');
    391    const nonOpus = codecsNotMatching(opus.mimeType);
    392 
    393    const transceiver = pc1.addTransceiver('audio', {
    394      sendEncodings: [{codec: opus}],
    395    });
    396    const sender = transceiver.sender;
    397 
    398    exchangeIceCandidates(pc1, pc2);
    399    await exchangeOfferAnswer(pc1, pc2);
    400 
    401    let param = sender.getParameters();
    402    let encoding = param.encodings[0];
    403 
    404    assertCodecEquals(opus, encoding.codec);
    405 
    406    pc2.getTransceivers()[0].setCodecPreferences(nonOpus);
    407    await exchangeOfferAnswer(pc1, pc2);
    408 
    409    param = sender.getParameters();
    410    encoding = param.encodings[0];
    411 
    412    assert_equals(encoding.codec, undefined);
    413  }, `Codec should be undefined after negotiating away the currently set codec on an audio sender`);
    414  promise_test(async (t) => {
    415    const pc1 = new RTCPeerConnection();
    416    const pc2 = new RTCPeerConnection();
    417    t.add_cleanup(() => pc1.close());
    418    t.add_cleanup(() => pc2.close());
    419 
    420    const vp8 = findFirstCodec('video/VP8');
    421    const nonVP8 = codecsNotMatching(vp8.mimeType);
    422 
    423    const transceiver = pc1.addTransceiver('video', {
    424      sendEncodings: [{codec: vp8}],
    425    });
    426    const sender = transceiver.sender;
    427 
    428    exchangeIceCandidates(pc1, pc2);
    429    await exchangeOfferAnswer(pc1, pc2);
    430 
    431    let param = sender.getParameters();
    432    let encoding = param.encodings[0];
    433 
    434    assertCodecEquals(vp8, encoding.codec);
    435 
    436    pc2.getTransceivers()[0].setCodecPreferences(nonVP8);
    437    await exchangeOfferAnswer(pc1, pc2);
    438 
    439    param = sender.getParameters();
    440    encoding = param.encodings[0];
    441 
    442    assert_equals(encoding.codec, undefined);
    443  }, `Codec should be undefined after negotiating away the currently set codec on a video sender`);
    444 
    445  promise_test(async (t) => {
    446    const pc1 = new RTCPeerConnection();
    447    const pc2 = new RTCPeerConnection();
    448    t.add_cleanup(() => pc1.close());
    449    t.add_cleanup(() => pc2.close());
    450    const stream = await getNoiseStream({audio:true});
    451    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    452 
    453    const opus = findFirstCodec('audio/opus');
    454    const nonOpus = codecsNotMatching(opus.mimeType);
    455 
    456    const transceiver = pc1.addTransceiver(stream.getTracks()[0]);
    457    const sender = transceiver.sender;
    458 
    459    exchangeIceCandidates(pc1, pc2);
    460    const negotiated = exchangeOfferAnswer(pc1, pc2);
    461    const trackEvent = await new Promise(r => pc2.ontrack = r);
    462    trackEvent.transceiver.setCodecPreferences(nonOpus.concat([opus]));
    463    await negotiated;
    464 
    465 
    466    let codecs = await codecsForSender(sender);
    467    assert_not_equals(codecs[0], opus.mimeType);
    468 
    469    let param = sender.getParameters();
    470    let encoding = param.encodings[0];
    471    encoding.codec = opus;
    472 
    473    await sender.setParameters(param);
    474 
    475    await step_wait_async(t, async () => {
    476      let old_codecs = codecs;
    477      codecs = await codecsForSender(sender);
    478      return !arrayEquals(codecs, old_codecs);
    479    }, 'Waiting for current codecs to change', 5000, 200);
    480 
    481    assert_array_equals(codecs, [opus.mimeType]);
    482  }, `Stats output-rtp should match the selected codec in non-simulcast usecase on an audio sender`);
    483 
    484  promise_test(async (t) => {
    485    const pc1 = new RTCPeerConnection();
    486    const pc2 = new RTCPeerConnection();
    487    t.add_cleanup(() => pc1.close());
    488    t.add_cleanup(() => pc2.close());
    489    const stream = await getNoiseStream({video:true});
    490    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    491 
    492    const vp8 = findFirstCodec('video/VP8');
    493    const nonVP8 = codecsNotMatching(vp8.mimeType);
    494 
    495    const transceiver = pc1.addTransceiver(stream.getTracks()[0]);
    496    const sender = transceiver.sender;
    497 
    498    exchangeIceCandidates(pc1, pc2);
    499    const negotiated = exchangeOfferAnswer(pc1, pc2);
    500    const trackEvent = await new Promise(r => pc2.ontrack = r);
    501    trackEvent.transceiver.setCodecPreferences(nonVP8.concat([vp8]));
    502    await negotiated;
    503 
    504    let codecs = await codecsForSender(sender);
    505    assert_not_equals(codecs[0], vp8.mimeType);
    506 
    507    let param = sender.getParameters();
    508    let encoding = param.encodings[0];
    509    encoding.codec = vp8;
    510 
    511    await sender.setParameters(param);
    512 
    513    await step_wait_async(t, async () => {
    514      let old_codecs = codecs;
    515      codecs = await codecsForSender(sender);
    516      return !arrayEquals(codecs, old_codecs);
    517    }, 'Waiting for current codecs to change', 5000, 200);
    518 
    519    assert_array_equals(codecs, [vp8.mimeType]);
    520  }, `Stats output-rtp should match the selected codec in non-simulcast usecase on a video sender`);
    521 
    522  promise_test(async (t) => {
    523    const pc1 = new RTCPeerConnection();
    524    const pc2 = new RTCPeerConnection();
    525    t.add_cleanup(() => pc1.close());
    526    t.add_cleanup(() => pc2.close());
    527    const stream = await getNoiseStream({video:true});
    528    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    529 
    530    const vp8 = findFirstCodec('video/VP8');
    531    const h264 = findFirstCodec('video/H264');
    532 
    533    const transceiver = pc1.addTransceiver(stream.getTracks()[0], {
    534      sendEncodings: [{rid: '0'}, {rid: '1'}, {rid: '2'}],
    535    });
    536    const sender = transceiver.sender;
    537 
    538    exchangeIceCandidates(pc1, pc2);
    539    const negotiated =  doOfferToSendSimulcastAndAnswer(pc1, pc2, ['0', '1', '2']);
    540    const trackEvent = await new Promise(r => pc2.ontrack = r);
    541    trackEvent.transceiver.setCodecPreferences([h264, vp8]);
    542    await negotiated;
    543 
    544    await waitForAllLayers(t, sender);
    545 
    546    let codecs = await codecsForSender(sender);
    547    assert_not_equals(codecs[0], vp8.mimeType);
    548    assert_not_equals(codecs[1], vp8.mimeType);
    549    assert_not_equals(codecs[2], vp8.mimeType);
    550 
    551    let param = sender.getParameters();
    552    param.encodings[0].codec = vp8;
    553    param.encodings[1].codec = vp8;
    554    param.encodings[2].codec = vp8;
    555 
    556    await sender.setParameters(param);
    557 
    558    // Waiting for 10s as ramp-up time can be slow in the runners.
    559    await step_wait_async(t, async () => {
    560      let old_codecs = codecs;
    561      codecs = await codecsForSender(sender);
    562      return !arrayEquals(codecs, old_codecs);
    563    }, 'Waiting for current codecs to change', 10000, 200);
    564 
    565    assert_array_equals(codecs, [vp8.mimeType, vp8.mimeType, vp8.mimeType]);
    566  }, `Stats output-rtp should match the selected codec in simulcast usecase on a video sender`);
    567 
    568  promise_test(async (t) => {
    569    const pc1 = new RTCPeerConnection();
    570    const pc2 = new RTCPeerConnection();
    571    t.add_cleanup(() => pc1.close());
    572    t.add_cleanup(() => pc2.close());
    573    const stream = await getNoiseStream({video:true});
    574    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    575 
    576    const vp8 = findFirstCodec('video/VP8');
    577    const h264 = findFirstCodec('video/H264');
    578 
    579    const transceiver = pc1.addTransceiver(stream.getTracks()[0], {
    580      sendEncodings: [{rid: '0'}, {rid: '1'}, {rid: '2'}],
    581    });
    582    const sender = transceiver.sender;
    583 
    584    exchangeIceCandidates(pc1, pc2);
    585    const negotiated =  doOfferToSendSimulcastAndAnswer(pc1, pc2, ['0', '1', '2']);
    586    const trackEvent = await new Promise(r => pc2.ontrack = r);
    587    trackEvent.transceiver.setCodecPreferences([h264, vp8]);
    588    await negotiated;
    589 
    590 
    591    await waitForAllLayers(t, sender);
    592 
    593    let codecs = await codecsForSender(sender);
    594    assert_not_equals(codecs[0], vp8.mimeType);
    595    assert_not_equals(codecs[1], vp8.mimeType);
    596    assert_not_equals(codecs[2], vp8.mimeType);
    597 
    598    let param = sender.getParameters();
    599    param.encodings[1].codec = vp8;
    600 
    601    await sender.setParameters(param);
    602 
    603    await step_wait_async(t, async () => {
    604      let old_codecs = codecs;
    605      codecs = await codecsForSender(sender);
    606      return !arrayEquals(codecs, old_codecs);
    607    }, 'Waiting for current codecs to change', 5000, 200);
    608 
    609    assert_not_equals(codecs[0], vp8.mimeType);
    610    assert_equals(codecs[1], vp8.mimeType);
    611    assert_not_equals(codecs[2], vp8.mimeType);
    612  }, `Stats output-rtp should match the selected mixed codecs in simulcast usecase on a video sender`);
    613 
    614 </script>