| <!doctype html> | |
| <meta charset=utf-8> | |
| <title>RTCPeerConnection Insertable Streams Simulcast</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="../webrtc/RTCPeerConnection-helper.js"></script> | |
| <script src="../webrtc/third_party/sdp/sdp.js"></script> | |
| <script src="../webrtc/simulcast/simulcast.js"></script> | |
| <script> | |
| // Test based on wpt/webrtc/simulcast/basic.https.html | |
| promise_test(async t => { | |
| const rids = [0, 1, 2]; | |
| const pc1 = new RTCPeerConnection({encodedInsertableStreams:true}); | |
| t.add_cleanup(() => pc1.close()); | |
| const pc2 = new RTCPeerConnection({encodedInsertableStreams:true}); | |
| t.add_cleanup(() => pc2.close()); | |
| exchangeIceCandidates(pc1, pc2); | |
| const metadataToBeLoaded = []; | |
| let receiverSSRCs = [] | |
| pc2.ontrack = t.step_func(e => { | |
| const receiverTransformer = new TransformStream({ | |
| async transform(encodedFrame, controller) { | |
| let ssrc = encodedFrame.getMetadata().synchronizationSource; | |
| if (receiverSSRCs.indexOf(ssrc) == -1) | |
| receiverSSRCs.push(ssrc); | |
| controller.enqueue(encodedFrame); | |
| } | |
| }); | |
| const receiverStreams = e.receiver.createEncodedStreams(); | |
| receiverStreams.readable | |
| .pipeThrough(receiverTransformer) | |
| .pipeTo(receiverStreams.writable); | |
| const stream = e.streams[0]; | |
| const v = document.createElement('video'); | |
| v.autoplay = true; | |
| v.srcObject = stream; | |
| v.id = stream.id | |
| metadataToBeLoaded.push(new Promise((resolve) => { | |
| v.addEventListener('loadedmetadata', () => { | |
| resolve(); | |
| }); | |
| })); | |
| }); | |
| await setMediaPermission("granted", ["camera"]); | |
| const stream = await navigator.mediaDevices.getUserMedia({video: {width: 1280, height: 720}}); | |
| t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); | |
| const transceiver = pc1.addTransceiver(stream.getVideoTracks()[0], { | |
| streams: [stream], | |
| sendEncodings: rids.map(rid => {rid}), | |
| }); | |
| const senderStreams = transceiver.sender.createEncodedStreams(); | |
| let senderSSRCs = []; | |
| const senderTransformer = new TransformStream({ | |
| async transform(encodedFrame, controller) { | |
| if (senderSSRCs.indexOf(encodedFrame.getMetadata().synchronizationSource) == -1) | |
| senderSSRCs.push(encodedFrame.getMetadata().synchronizationSource); | |
| controller.enqueue(encodedFrame); | |
| } | |
| }); | |
| senderStreams.readable | |
| .pipeThrough(senderTransformer) | |
| .pipeTo(senderStreams.writable); | |
| const offer = await pc1.createOffer(); | |
| await pc1.setLocalDescription(offer), | |
| await pc2.setRemoteDescription({ | |
| type: 'offer', | |
| sdp: swapRidAndMidExtensionsInSimulcastOffer(offer, rids), | |
| }); | |
| const answer = await pc2.createAnswer(); | |
| await pc2.setLocalDescription(answer); | |
| await pc1.setRemoteDescription({ | |
| type: 'answer', | |
| sdp: swapRidAndMidExtensionsInSimulcastAnswer(answer, pc1.localDescription, rids), | |
| }); | |
| assert_equals(metadataToBeLoaded.length, 3); | |
| await Promise.all(metadataToBeLoaded); | |
| // Ensure that frames from the 3 simulcast layers are exposed. | |
| assert_equals(senderSSRCs.length, 3); | |
| assert_equals(receiverSSRCs.length, 3); | |
| }, 'Basic simulcast setup with three spatial layers'); | |
| </script> |