diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /testing/web-platform/tests/webrtc/RTCPeerConnection-addTrack.https.html | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/webrtc/RTCPeerConnection-addTrack.https.html')
-rw-r--r-- | testing/web-platform/tests/webrtc/RTCPeerConnection-addTrack.https.html | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-addTrack.https.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-addTrack.https.html new file mode 100644 index 0000000000..91665822c4 --- /dev/null +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-addTrack.https.html @@ -0,0 +1,394 @@ +<!doctype html> +<meta charset=utf-8> +<title>RTCPeerConnection.prototype.addTrack</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src=/resources/testdriver.js></script> +<script src=/resources/testdriver-vendor.js></script> +<script src='../mediacapture-streams/permission-helper.js'></script> +<script src="RTCPeerConnection-helper.js"></script> +<script> + 'use strict'; + + // Test is based on the following editor draft: + // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html + + // The following helper functions are called from RTCPeerConnection-helper.js: + // getNoiseStream() + + /* + 5.1. RTCPeerConnection Interface Extensions + partial interface RTCPeerConnection { + ... + sequence<RTCRtpSender> getSenders(); + sequence<RTCRtpReceiver> getReceivers(); + sequence<RTCRtpTransceiver> getTransceivers(); + RTCRtpSender addTrack(MediaStreamTrack track, + MediaStream... streams); + RTCRtpTransceiver addTransceiver((MediaStreamTrack or DOMString) trackOrKind, + optional RTCRtpTransceiverInit init); + }; + + Note + While addTrack checks if the MediaStreamTrack given as an argument is + already being sent to avoid sending the same MediaStreamTrack twice, + the other ways do not, allowing the same MediaStreamTrack to be sent + several times simultaneously. + */ + + /* + 5.1. addTrack + 4. If connection's [[isClosed]] slot is true, throw an InvalidStateError. + */ + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const stream = await getNoiseStream({ audio: true }); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + + pc.close(); + assert_throws_dom('InvalidStateError', () => pc.addTrack(track, stream)) + }, 'addTrack when pc is closed should throw InvalidStateError'); + + /* + 5.1. addTrack + 8. If sender is null, run the following steps: + 1. Create an RTCRtpSender with track and streams and let sender be + the result. + 2. Create an RTCRtpReceiver with track.kind as kind and let receiver + be the result. + 3. Create an RTCRtpTransceiver with sender and receiver and let + transceiver be the result. + 4. Add transceiver to connection's set of transceivers. + */ + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const stream = await getNoiseStream({ audio: true }); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + + const sender = pc.addTrack(track); + + assert_true(sender instanceof RTCRtpSender, + 'Expect sender to be instance of RTCRtpSender'); + + assert_equals(sender.track, track, + `Expect sender's track to be the added track`); + + const transceivers = pc.getTransceivers(); + assert_equals(transceivers.length, 1, + 'Expect only one transceiver with sender added'); + + const [transceiver] = transceivers; + assert_equals(transceiver.sender, sender); + + assert_array_equals([sender], pc.getSenders(), + 'Expect only one sender with given track added'); + + const { receiver } = transceiver; + assert_equals(receiver.track.kind, 'audio'); + assert_array_equals([transceiver.receiver], pc.getReceivers(), + 'Expect only one receiver associated with transceiver added'); + }, 'addTrack with single track argument and no stream should succeed'); + + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const stream = await getNoiseStream({ audio: true }); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + + const sender = pc.addTrack(track, stream); + + assert_true(sender instanceof RTCRtpSender, + 'Expect sender to be instance of RTCRtpSender'); + + assert_equals(sender.track, track, + `Expect sender's track to be the added track`); + }, 'addTrack with single track argument and single stream should succeed'); + + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const stream = await getNoiseStream({ audio: true }); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + + const stream2 = new MediaStream([track]); + const sender = pc.addTrack(track, stream, stream2); + + assert_true(sender instanceof RTCRtpSender, + 'Expect sender to be instance of RTCRtpSender'); + + assert_equals(sender.track, track, + `Expect sender's track to be the added track`); + }, 'addTrack with single track argument and multiple streams should succeed'); + + /* + 5.1. addTrack + 5. Let senders be the result of executing the CollectSenders algorithm. + If an RTCRtpSender for track already exists in senders, throw an + InvalidAccessError. + */ + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const stream = await getNoiseStream({ audio: true }); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + + pc.addTrack(track, stream); + assert_throws_dom('InvalidAccessError', () => pc.addTrack(track, stream)); + }, 'Adding the same track multiple times should throw InvalidAccessError'); + + /* + 5.1. addTrack + 6. The steps below describe how to determine if an existing sender can + be reused. + + If any RTCRtpSender object in senders matches all the following + criteria, let sender be that object, or null otherwise: + - The sender's track is null. + - The transceiver kind of the RTCRtpTransceiver, associated with + the sender, matches track's kind. + - The sender has never been used to send. More precisely, the + RTCRtpTransceiver associated with the sender has never had a + currentDirection of sendrecv or sendonly. + 7. If sender is not null, run the following steps to use that sender: + 1. Set sender.track to track. + 3. Enable sending direction on the RTCRtpTransceiver associated + with sender. + */ + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const transceiver = pc.addTransceiver('audio', { direction: 'recvonly' }); + assert_equals(transceiver.sender.track, null); + assert_equals(transceiver.direction, 'recvonly'); + + await setMediaPermission("granted", ["microphone"]); + const stream = await navigator.mediaDevices.getUserMedia({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const sender = pc.addTrack(track); + + assert_equals(sender, transceiver.sender); + assert_equals(sender.track, track); + assert_equals(transceiver.direction, 'sendrecv'); + assert_array_equals([sender], pc.getSenders()); + }, 'addTrack with existing sender with null track, same kind, and recvonly direction should reuse sender'); + + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const transceiver = pc.addTransceiver('audio'); + assert_equals(transceiver.sender.track, null); + assert_equals(transceiver.direction, 'sendrecv'); + + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const sender = pc.addTrack(track); + + assert_equals(sender.track, track); + assert_equals(sender, transceiver.sender); + }, 'addTrack with existing sender that has not been used to send should reuse the sender'); + + promise_test(async t => { + const caller = new RTCPeerConnection(); + t.add_cleanup(() => caller.close()); + const callee = new RTCPeerConnection(); + t.add_cleanup(() => callee.close()); + + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const transceiver = caller.addTransceiver(track); + { + const offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + await callee.setRemoteDescription(offer); + const answer = await callee.createAnswer(); + await callee.setLocalDescription(answer); + await caller.setRemoteDescription(answer); + } + assert_equals(transceiver.currentDirection, 'sendonly'); + + caller.removeTrack(transceiver.sender); + { + const offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + await callee.setRemoteDescription(offer); + const answer = await callee.createAnswer(); + await callee.setLocalDescription(answer); + await caller.setRemoteDescription(answer); + } + assert_equals(transceiver.direction, 'recvonly'); + assert_equals(transceiver.currentDirection, 'inactive'); + + // |transceiver.sender| is currently not used for sending, but it should not + // be reused because it has been used for sending before. + const sender = caller.addTrack(track); + assert_true(sender != null); + assert_not_equals(sender, transceiver.sender); + }, 'addTrack with existing sender that has been used to send should create new sender'); + + promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const transceiver = pc.addTransceiver('video', { direction: 'recvonly' }); + assert_equals(transceiver.sender.track, null); + assert_equals(transceiver.direction, 'recvonly'); + + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const sender = pc.addTrack(track); + + assert_equals(sender.track, track); + assert_not_equals(sender, transceiver.sender); + + const senders = pc.getSenders(); + assert_equals(senders.length, 2, + 'Expect 2 senders added to connection'); + + assert_true(senders.includes(sender), + 'Expect senders list to include sender'); + + assert_true(senders.includes(transceiver.sender), + `Expect senders list to include first transceiver's sender`); + }, 'addTrack with existing sender with null track, different kind, and recvonly direction should create new sender'); + + /* + TODO + 5.1. addTrack + 3. Let streams be a list of MediaStream objects constructed from the + method's remaining arguments, or an empty list if the method was + called with a single argument. + 6. The steps below describe how to determine if an existing sender can + be reused. Doing so will cause future calls to createOffer and + createAnswer to mark the corresponding media description as sendrecv + or sendonly and add the MSID of the track added, as defined in [JSEP] + (section 5.2.2. and section 5.3.2.). + + Non-Testable + 5.1. addTrack + 7. If sender is not null, run the following steps to use that sender: + 2. Set sender's [[associated MediaStreams]] to streams. + + Tested in RTCPeerConnection-onnegotiationneeded.html: + 5.1. addTrack + 10. Update the negotiation-needed flag for connection. + + */ + + promise_test(async t => { + const caller = new RTCPeerConnection(); + t.add_cleanup(() => caller.close()); + const callee = new RTCPeerConnection(); + t.add_cleanup(() => callee.close()); + + const stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + const transceiver = caller.addTransceiver(track); + // Note that this test doesn't process canididates. + { + const offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + await callee.setRemoteDescription(offer); + const answer = await callee.createAnswer(); + await callee.setLocalDescription(answer); + await caller.setRemoteDescription(answer); + } + assert_equals(transceiver.currentDirection, 'sendonly'); + await waitForIceGatheringState(caller, ['complete']); + await waitForIceGatheringState(callee, ['complete']); + + const second_stream = await getNoiseStream({audio: true}); + t.add_cleanup(() => second_stream.getTracks().forEach(track => track.stop())); + // There may be callee candidates in flight. It seems that waiting + // for a createOffer() is enough time to let them complete processing. + // TODO(https://crbug.com/webrtc/13095): Fix bug and remove. + await caller.createOffer(); + + const [second_track] = second_stream.getTracks(); + caller.onicecandidate = t.unreached_func( + 'No caller candidates should be generated.'); + callee.onicecandidate = t.unreached_func( + 'No callee candidates should be generated.'); + caller.addTrack(second_track); + { + const offer = await caller.createOffer(); + await caller.setLocalDescription(offer); + await callee.setRemoteDescription(offer); + const answer = await callee.createAnswer(); + await callee.setLocalDescription(answer); + await caller.setRemoteDescription(answer); + } + // Check that we're bundled. + const [first_transceiver, second_transceiver] = caller.getTransceivers(); + assert_equals(first_transceiver.transport, second_transceiver.transport); + + }, 'Adding more tracks does not generate more candidates if bundled'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + + const stream = await getNoiseStream({ audio: true }); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + + pc1.addTrack(track); + const offer = await pc1.createOffer(); + // We do not await here; we want to ensure that the transceiver this creates + // is untouched by addTrack, and that addTrack creates _another_ transceiver + const srdPromise = pc2.setRemoteDescription(offer); + + const sender = pc2.addTrack(track); + + await srdPromise; + + assert_equals(pc2.getTransceivers().length, 1, "Should have 1 transceiver"); + assert_equals(pc2.getTransceivers()[0].sender, sender, "The transceiver should be the one added by addTrack"); + }, 'Calling addTrack while sRD(offer) is pending should allow the new remote transceiver to be the same one that addTrack creates'); + + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + pc1.addTransceiver('video'); + + const stream = await getNoiseStream({ audio: true }); + t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); + const [track] = stream.getTracks(); + + const offer = await pc1.createOffer(); + const srdPromise = pc2.setRemoteDescription(offer); + assert_equals(pc2.getTransceivers().length, 0); + pc2.addTrack(track); + assert_equals(pc2.getTransceivers().length, 1); + const transceiver0 = pc2.getTransceivers()[0]; + assert_equals(transceiver0.mid, null); + await srdPromise; + assert_equals(pc2.getTransceivers().length, 2); + const transceiver1 = pc2.getTransceivers()[1]; + assert_equals(transceiver0.mid, null); + assert_not_equals(transceiver1.mid, null); + }, 'When addTrack is called while sRD is in progress, and both addTrack and sRD add a transceiver of different media types, the addTrack transceiver should come first, and then the sRD transceiver.'); + +</script> |