tor-browser

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

RTCPeerConnection-addTransceiver.https.html (17284B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title>RTCPeerConnection.prototype.addTransceiver</title>
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <script src="RTCPeerConnection-helper.js"></script>
      7 <script>
      8  'use strict';
      9 
     10  // Test is based on the following editor draft:
     11  // https://rawgit.com/w3c/webrtc-pc/cc8d80f455b86c8041d63bceb8b457f45c72aa89/webrtc.html
     12 
     13  /*
     14    5.1.  RTCPeerConnection Interface Extensions
     15 
     16      partial interface RTCPeerConnection {
     17          sequence<RTCRtpSender>      getSenders();
     18          sequence<RTCRtpReceiver>    getReceivers();
     19          sequence<RTCRtpTransceiver> getTransceivers();
     20          RTCRtpTransceiver           addTransceiver((MediaStreamTrack or DOMString) trackOrKind,
     21                                                     optional RTCRtpTransceiverInit init);
     22          ...
     23      };
     24 
     25      dictionary RTCRtpTransceiverInit {
     26          RTCRtpTransceiverDirection         direction = "sendrecv";
     27          sequence<MediaStream>              streams;
     28          sequence<RTCRtpEncodingParameters> sendEncodings;
     29      };
     30 
     31      enum RTCRtpTransceiverDirection {
     32        "sendrecv",
     33        "sendonly",
     34        "recvonly",
     35        "inactive"
     36      };
     37 
     38    5.2.  RTCRtpSender Interface
     39 
     40      interface RTCRtpSender {
     41        readonly attribute MediaStreamTrack? track;
     42        ...
     43      };
     44 
     45    5.3.  RTCRtpReceiver Interface
     46 
     47      interface RTCRtpReceiver {
     48        readonly attribute MediaStreamTrack  track;
     49        ...
     50      };
     51 
     52    5.4.  RTCRtpTransceiver Interface
     53 
     54      interface RTCRtpTransceiver {
     55        readonly attribute DOMString?                  mid;
     56        [SameObject]
     57        readonly attribute RTCRtpSender                sender;
     58        [SameObject]
     59        readonly attribute RTCRtpReceiver              receiver;
     60        readonly attribute boolean                     stopped;
     61        readonly attribute RTCRtpTransceiverDirection  direction;
     62        readonly attribute RTCRtpTransceiverDirection? currentDirection;
     63        ...
     64      };
     65 
     66      Note
     67        While addTrack checks if the MediaStreamTrack given as an argument is
     68        already being sent to avoid sending the same MediaStreamTrack twice,
     69        the other ways do not, allowing the same MediaStreamTrack to be sent
     70        several times simultaneously.
     71   */
     72 
     73  /*
     74    5.1.  addTransceiver
     75      3.  If the first argument is a string, let it be kind and run the following steps:
     76        1.  If kind is not a legal MediaStreamTrack kind, throw a TypeError.
     77   */
     78  test(t => {
     79    const pc = new RTCPeerConnection();
     80    t.add_cleanup(() => pc.close());
     81 
     82    assert_idl_attribute(pc, 'addTransceiver');
     83    assert_throws_js(TypeError, () => pc.addTransceiver('invalid'));
     84  }, 'addTransceiver() with string argument as invalid kind should throw TypeError');
     85 
     86  /*
     87    5.1.  addTransceiver
     88      The initial value of mid is null.
     89 
     90      3.  If the dictionary argument is present, let direction be the value of the
     91          direction member. Otherwise let direction be sendrecv.
     92      4.  If the first argument is a string, let it be kind and run the following steps:
     93        2.  Let track be null.
     94      8.  Create an RTCRtpSender with track, streams and sendEncodings and let
     95          sender be the result.
     96      9.  Create an RTCRtpReceiver with kind and let receiver be the result.
     97      10. Create an RTCRtpTransceiver with sender, receiver and direction, and let
     98          transceiver be the result.
     99      11. Add transceiver to connection's set of transceivers.
    100 
    101    5.2.  RTCRtpSender Interface
    102      Create an RTCRtpSender
    103        2.  Set sender.track to track.
    104 
    105    5.3.  RTCRtpReceiver Interface
    106      Create an RTCRtpReceiver
    107        2.  Let track be a new MediaStreamTrack object [GETUSERMEDIA]. The source of
    108            track is a remote source provided by receiver.
    109        3.  Initialize track.kind to kind.
    110        5.  Initialize track.label to the result of concatenating the string "remote "
    111            with kind.
    112        6.  Initialize track.readyState to live.
    113        7.  Initialize track.muted to true.
    114        8.  Set receiver.track to track.
    115 
    116    5.4.  RTCRtpTransceiver Interface
    117      Create an RTCRtpTransceiver
    118        2.  Set transceiver.sender to sender.
    119        3.  Set transceiver.receiver to receiver.
    120        4.  Let transceiver have a [[Direction]] internal slot, initialized to direction.
    121        5.  Let transceiver have a [[CurrentDirection]] internal slot, initialized
    122            to null.
    123        6.  Set transceiver.stopped to false.
    124   */
    125  test(t => {
    126    const pc = new RTCPeerConnection();
    127    t.add_cleanup(() => pc.close());
    128 
    129    assert_idl_attribute(pc, 'addTransceiver');
    130 
    131    const transceiver = pc.addTransceiver('audio');
    132    assert_true(transceiver instanceof RTCRtpTransceiver,
    133      'Expect transceiver to be instance of RTCRtpTransceiver');
    134 
    135    assert_equals(transceiver.mid, null);
    136    assert_equals(transceiver.stopped, false);
    137    assert_equals(transceiver.direction, 'sendrecv');
    138    assert_equals(transceiver.currentDirection, null);
    139 
    140    assert_array_equals([transceiver], pc.getTransceivers(),
    141      `Expect added transceiver to be the only element in connection's list of transceivers`);
    142 
    143    const sender = transceiver.sender;
    144 
    145    assert_true(sender instanceof RTCRtpSender,
    146      'Expect sender to be instance of RTCRtpSender');
    147 
    148    assert_equals(sender.track, null);
    149 
    150    assert_array_equals([sender], pc.getSenders(),
    151      `Expect added sender to be the only element in connection's list of senders`);
    152 
    153    const receiver = transceiver.receiver;
    154    assert_true(receiver instanceof RTCRtpReceiver,
    155      'Expect receiver to be instance of RTCRtpReceiver');
    156 
    157    const track = receiver.track;
    158    assert_true(track instanceof MediaStreamTrack,
    159      'Expect receiver.track to be instance of MediaStreamTrack');
    160 
    161    assert_equals(track.kind, 'audio');
    162    assert_equals(track.readyState, 'live');
    163    assert_equals(track.muted, true);
    164 
    165    assert_array_equals([receiver], pc.getReceivers(),
    166      `Expect added receiver to be the only element in connection's list of receivers`);
    167 
    168  }, `addTransceiver('audio') should return an audio transceiver`);
    169 
    170  test(t => {
    171    const pc = new RTCPeerConnection();
    172    t.add_cleanup(() => pc.close());
    173 
    174    assert_idl_attribute(pc, 'addTransceiver');
    175 
    176    const transceiver = pc.addTransceiver('video');
    177    assert_true(transceiver instanceof RTCRtpTransceiver,
    178      'Expect transceiver to be instance of RTCRtpTransceiver');
    179 
    180    assert_equals(transceiver.mid, null);
    181    assert_equals(transceiver.stopped, false);
    182    assert_equals(transceiver.direction, 'sendrecv');
    183 
    184    assert_array_equals([transceiver], pc.getTransceivers(),
    185      `Expect added transceiver to be the only element in connection's list of transceivers`);
    186 
    187    const sender = transceiver.sender;
    188 
    189    assert_true(sender instanceof RTCRtpSender,
    190      'Expect sender to be instance of RTCRtpSender');
    191 
    192    assert_equals(sender.track, null);
    193 
    194    assert_array_equals([sender], pc.getSenders(),
    195      `Expect added sender to be the only element in connection's list of senders`);
    196 
    197    const receiver = transceiver.receiver;
    198    assert_true(receiver instanceof RTCRtpReceiver,
    199      'Expect receiver to be instance of RTCRtpReceiver');
    200 
    201    const track = receiver.track;
    202    assert_true(track instanceof MediaStreamTrack,
    203      'Expect receiver.track to be instance of MediaStreamTrack');
    204 
    205    assert_equals(track.kind, 'video');
    206    assert_equals(track.readyState, 'live');
    207    assert_equals(track.muted, true);
    208 
    209    assert_array_equals([receiver], pc.getReceivers(),
    210      `Expect added receiver to be the only element in connection's list of receivers`);
    211 
    212  }, `addTransceiver('video') should return a video transceiver`);
    213 
    214  test(t => {
    215    const pc = new RTCPeerConnection();
    216    t.add_cleanup(() => pc.close());
    217 
    218    const transceiver = pc.addTransceiver('audio', { direction: 'sendonly' });
    219    assert_equals(transceiver.direction, 'sendonly');
    220  }, `addTransceiver() with direction sendonly should have result transceiver.direction be the same`);
    221 
    222  test(t => {
    223    const pc = new RTCPeerConnection();
    224    t.add_cleanup(() => pc.close());
    225 
    226    const transceiver = pc.addTransceiver('audio', { direction: 'inactive' });
    227    assert_equals(transceiver.direction, 'inactive');
    228  }, `addTransceiver() with direction inactive should have result transceiver.direction be the same`);
    229 
    230  test(t => {
    231    const pc = new RTCPeerConnection();
    232    t.add_cleanup(() => pc.close());
    233 
    234    assert_idl_attribute(pc, 'addTransceiver');
    235    assert_throws_js(TypeError, () =>
    236      pc.addTransceiver('audio', { direction: 'invalid' }));
    237  }, `addTransceiver() with invalid direction should throw TypeError`);
    238 
    239  /*
    240    5.1.  addTransceiver
    241      5.  If the first argument is a MediaStreamTrack , let it be track and let
    242          kind be track.kind.
    243   */
    244  promise_test(async t => {
    245    const pc = new RTCPeerConnection();
    246    t.add_cleanup(() => pc.close());
    247 
    248    const stream = await getNoiseStream({audio: true});
    249    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    250    const [track] = stream.getTracks();
    251    const transceiver = pc.addTransceiver(track);
    252    const { sender, receiver } = transceiver;
    253 
    254    assert_true(sender instanceof RTCRtpSender,
    255      'Expect sender to be instance of RTCRtpSender');
    256 
    257    assert_true(receiver instanceof RTCRtpReceiver,
    258      'Expect receiver to be instance of RTCRtpReceiver');
    259 
    260    assert_equals(sender.track, track,
    261      'Expect sender.track should be the track that is added');
    262 
    263    const receiverTrack = receiver.track;
    264    assert_true(receiverTrack instanceof MediaStreamTrack,
    265      'Expect receiver.track to be instance of MediaStreamTrack');
    266 
    267    assert_equals(receiverTrack.kind, 'audio',
    268      `receiver.track should have the same kind as added track's kind`);
    269 
    270    assert_equals(receiverTrack.readyState, 'live');
    271    assert_equals(receiverTrack.muted, true);
    272 
    273    assert_array_equals([transceiver], pc.getTransceivers(),
    274      `Expect added transceiver to be the only element in connection's list of transceivers`);
    275 
    276    assert_array_equals([sender], pc.getSenders(),
    277      `Expect added sender to be the only element in connection's list of senders`);
    278 
    279    assert_array_equals([receiver], pc.getReceivers(),
    280      `Expect added receiver to be the only element in connection's list of receivers`);
    281 
    282  }, 'addTransceiver(track) should have result with sender.track be given track');
    283 
    284  promise_test(async t => {
    285    const pc = new RTCPeerConnection();
    286    t.add_cleanup(() => pc.close());
    287 
    288    const stream = await getNoiseStream({audio: true});
    289    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
    290    const [track] = stream.getTracks();
    291    const transceiver1 = pc.addTransceiver(track);
    292    const transceiver2 = pc.addTransceiver(track);
    293 
    294    assert_not_equals(transceiver1, transceiver2);
    295 
    296    const sender1 = transceiver1.sender;
    297    const sender2 = transceiver2.sender;
    298 
    299    assert_not_equals(sender1, sender2);
    300    assert_equals(transceiver1.sender.track, track);
    301    assert_equals(transceiver2.sender.track, track);
    302 
    303    const transceivers = pc.getTransceivers();
    304    assert_equals(transceivers.length, 2);
    305    assert_true(transceivers.includes(transceiver1));
    306    assert_true(transceivers.includes(transceiver2));
    307 
    308    const senders = pc.getSenders();
    309    assert_equals(senders.length, 2);
    310    assert_true(senders.includes(sender1));
    311    assert_true(senders.includes(sender2));
    312 
    313  }, 'addTransceiver(track) multiple times should create multiple transceivers');
    314 
    315  /*
    316    5.1.  addTransceiver
    317      6.  Verify that each rid value in sendEncodings is composed only of
    318          case-sensitive alphanumeric characters (a-z, A-Z, 0-9) up to a maximum
    319          of 16 characters. If one of the RIDs does not meet these requirements,
    320          throw a TypeError.
    321   */
    322 
    323 ["video", "audio"].forEach(kind => {
    324 
    325  test(t => {
    326    const pc = new RTCPeerConnection();
    327    t.add_cleanup(() => pc.close());
    328    assert_idl_attribute(pc, 'addTransceiver');
    329 
    330    assert_throws_js(TypeError, () =>
    331      pc.addTransceiver(kind, {
    332        sendEncodings: [{
    333          rid: '@Invalid!'
    334        }]
    335      }));
    336  }, `addTransceiver("${kind}") with rid containing invalid non-alphanumeric characters should throw TypeError`);
    337 
    338  test(t => {
    339    const pc = new RTCPeerConnection();
    340    t.add_cleanup(() => pc.close());
    341    assert_idl_attribute(pc, 'addTransceiver');
    342 
    343    assert_throws_js(TypeError, () =>
    344      pc.addTransceiver(kind, {
    345        sendEncodings: [{
    346          rid: 'a'.repeat(17)
    347        }]
    348      }));
    349  }, `addTransceiver("${kind}") with rid longer than 16 characters should throw TypeError`);
    350 
    351  test(t => {
    352    const pc = new RTCPeerConnection();
    353    t.add_cleanup(() => pc.close());
    354    assert_idl_attribute(pc, 'addTransceiver');
    355 
    356    assert_throws_js(TypeError, () =>
    357      pc.addTransceiver(kind, {
    358        sendEncodings: [{rid: 'a'}, {rid: 'a'}]
    359      }));
    360  }, `addTransceiver("${kind}") with duplicate rids should throw TypeError`);
    361 
    362  test(t => {
    363    const pc = new RTCPeerConnection();
    364    t.add_cleanup(() => pc.close());
    365    pc.addTransceiver(kind, {
    366      sendEncodings: [{
    367        rid: 'foo'
    368      }]
    369    });
    370  }, `addTransceiver("${kind}") with valid rid value should succeed`);
    371 
    372  test(t => {
    373    const pc = new RTCPeerConnection();
    374    t.add_cleanup(() => pc.close());
    375    pc.addTransceiver('video', {
    376      sendEncodings: [{rid: 'a'}, {rid: 'b'}]
    377    });
    378  }, `addTransceiver("${kind}") with multiple rid values should succeed`);
    379 
    380  test(t => {
    381    const pc = new RTCPeerConnection();
    382    t.add_cleanup(() => pc.close());
    383 
    384    pc.addTransceiver('video', {
    385      sendEncodings: [{
    386        dtx: 'enabled',
    387        active: false,
    388        ptime: 5,
    389        maxBitrate: 8,
    390        maxFramerate: 25,
    391        rid: 'foo'
    392      }]
    393    });
    394  }, `addTransceiver("${kind}") with valid sendEncodings should succeed`);
    395 });
    396 
    397  /*
    398    TODO
    399      5.1.  addTransceiver
    400        - Adding a transceiver will cause future calls to createOffer to add a media
    401          description for the corresponding transceiver, as defined in [JSEP]
    402          (section 5.2.2.).
    403 
    404        - Setting a new RTCSessionDescription may change mid to a non-null value,
    405          as defined in [JSEP] (section 5.5. and section 5.6.).
    406 
    407        1.  If the dictionary argument is present, and it has a streams member, let
    408            streams be that list of MediaStream objects.
    409 
    410      5.2.  RTCRtpSender Interface
    411        Create an RTCRtpSender
    412          3.  Let sender have an [[associated MediaStreams]] internal slot, representing
    413              a list of MediaStream objects that the MediaStreamTrack object of this
    414              sender is associated with.
    415 
    416          4.  Set sender's [[associated MediaStreams]] slot to streams.
    417 
    418          5.  Let sender have a [[send encodings]] internal slot, representing a list
    419              of RTCRtpEncodingParameters dictionaries.
    420 
    421          6.  If sendEncodings is given as input to this algorithm, and is non-empty,
    422              set the [[send encodings]] slot to sendEncodings. Otherwise, set it to a
    423              list containing a single RTCRtpEncodingParameters with active set to true.
    424 
    425      5.3.  RTCRtpReceiver Interface
    426        Create an RTCRtpReceiver
    427          4.  If an id string, id, was given as input to this algorithm, initialize
    428              track.id to id. (Otherwise the value generated when track was created
    429              will be used.)
    430 
    431    Tested in RTCPeerConnection-onnegotiationneeded.html
    432      5.1.  addTransceiver
    433        12. Update the negotiation-needed flag for connection.
    434 
    435    Out of Scope
    436      5.1.  addTransceiver
    437        8.  If sendEncodings is set, then subsequent calls to createOffer will be
    438            configured to send multiple RTP encodings as defined in [JSEP]
    439            (section 5.2.2. and section 5.2.1.).
    440 
    441            When setRemoteDescription is called with a corresponding remote
    442            description that is able to receive multiple RTP encodings as defined
    443            in [JSEP] (section 3.7.), the RTCRtpSender may send multiple RTP
    444            encodings and the parameters retrieved via the transceiver's
    445            sender.getParameters() will reflect the encodings negotiated.
    446 
    447        9.  This specification does not define how to configure createOffer to
    448            receive multiple RTP encodings. However when setRemoteDescription is
    449            called with a corresponding remote description that is able to send
    450            multiple RTP encodings as defined in [JSEP], the RTCRtpReceiver may
    451            receive multiple RTP encodings and the parameters retrieved via the
    452            transceiver's receiver.getParameters() will reflect the encodings
    453            negotiated.
    454 
    455    Coverage Report
    456                            Tested    Not-Tested  Non-Testable  Total
    457      addTransceiver          14          1           3           18
    458      Create Sender            3          4           0            7
    459      Create Receiver          8          1           0            9
    460      Create Transceiver       7          0           0            7
    461 
    462      Total                   32          6           3           41
    463   */
    464 </script>