| <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> |
| <html> |
| <head> |
| <script src="../../resources/js-test-pre.js"></script> |
| <script src="resources/promise-utils.js"></script> |
| </head> |
| <body> |
| <script> |
| let stream; |
| let offer; |
| |
| description("Test setting up media between two RTCPeerConnection instances with a single SDP dialog."); |
| |
| if (!window.testRunner) { |
| debug("This test can not be run without the testRunner"); |
| finishJSTest(); |
| } |
| |
| debug("Create RTCPeerConnection A"); |
| const pcA = new RTCPeerConnection({iceServers:[{urls:'stun:foo.com'}]}); |
| |
| shouldBe("pcA.signalingState", "'stable'"); |
| shouldBeNull("pcA.pendingLocalDescription"); |
| shouldBeNull("pcA.currentLocalDescription"); |
| shouldBeNull("pcA.pendingRemoteDescription"); |
| shouldBeNull("pcA.currentRemoteDescription"); |
| |
| debug("<br>Create RTCPeerConnection B"); |
| const pcB = new RTCPeerConnection({iceServers:[{urls:'stun:foo.com'}]}); |
| |
| shouldBe("pcB.signalingState", "'stable'"); |
| shouldBeNull("pcB.pendingLocalDescription"); |
| shouldBeNull("pcB.currentLocalDescription"); |
| shouldBeNull("pcB.pendingRemoteDescription"); |
| shouldBeNull("pcB.currentRemoteDescription"); |
| debug(""); |
| |
| pcA.ontrack = function () { |
| debug("----------"); |
| testPassed("A: got remote track event"); |
| debug("----------"); |
| }; |
| |
| pcB.ontrack = function () { |
| debug("----------"); |
| testPassed("B: got remote track event"); |
| debug("----------"); |
| }; |
| |
| navigator.mediaDevices.getUserMedia({ "audio": true }) |
| .then(function (s) { |
| stream = s; |
| |
| debug("A: add media"); |
| pcA.addTrack(stream.getAudioTracks()[0], stream); |
| |
| debug("A: create offer"); |
| return pcA.createOffer(); |
| }) |
| .then(function (offer) { |
| debug("A: got offer, set it as local description"); |
| return pcA.setLocalDescription(offer); |
| }) |
| .then(function () { |
| debug("A: local offer set"); |
| shouldBe("pcA.signalingState", "'have-local-offer'"); |
| shouldBeType("pcA.pendingLocalDescription", "RTCSessionDescription"); |
| shouldBeNull("pcA.currentLocalDescription"); |
| |
| debug("<br>A: send offer to B"); |
| debug("A --- offer --> B"); |
| debug("B: got offer from A, set it as remote description"); |
| return pcB.setRemoteDescription(pcA.localDescription); |
| }) |
| .then(function () { |
| debug("B: remote offer set"); |
| shouldBe("pcB.signalingState", "'have-remote-offer'"); |
| shouldBeType("pcB.pendingRemoteDescription", "RTCSessionDescription"); |
| shouldBeNull("pcB.currentRemoteDescription"); |
| |
| debug("<br>B: create answer (without local media)"); |
| return pcB.createAnswer(); |
| }) |
| .then(function (answer) { |
| debug("B: got answer, set it as local description"); |
| return pcB.setLocalDescription(answer); |
| }) |
| .then(function () { |
| debug("B: local answer set"); |
| shouldBe("pcB.signalingState", "'stable'"); |
| shouldBeNull("pcB.pendingLocalDescription"); |
| shouldBeType("pcB.currentLocalDescription", "RTCSessionDescription"); |
| shouldBeNull("pcB.pendingRemoteDescription"); |
| shouldBeType("pcB.currentRemoteDescription", "RTCSessionDescription"); |
| |
| debug("<br>B: send answer to A"); |
| debug("A <-- answer -- B"); |
| debug("A: got answer from B, set it as remote description"); |
| return pcA.setRemoteDescription(pcB.localDescription); |
| }) |
| .then(function () { |
| debug("A: remote answer set"); |
| shouldBe("pcA.signalingState", "'stable'"); |
| shouldBeNull("pcA.pendingLocalDescription"); |
| shouldBeType("pcA.currentLocalDescription", "RTCSessionDescription"); |
| shouldBeNull("pcA.pendingRemoteDescription"); |
| shouldBeType("pcA.currentRemoteDescription", "RTCSessionDescription"); |
| debug(""); |
| |
| testPassed("First offer/answer dialog completed") |
| debug("========================================<br>") |
| |
| shouldBe("pcB.getSenders().length", "1"); |
| debug("B: add media (should reuse sender)"); |
| pcB.addTrack(stream.getAudioTracks()[0], stream); |
| shouldBe("pcB.getSenders().length", "1"); |
| |
| debug("B: create offer"); |
| return pcB.createOffer(); |
| }) |
| .then(function (offer) { |
| debug("B: got offer, set it as local description"); |
| return pcB.setLocalDescription(offer); |
| }) |
| .then(function () { |
| debug("B: local offer set (session update so we have both pending and current local descriptions)"); |
| shouldBe("pcB.signalingState", "'have-local-offer'"); |
| shouldBeType("pcB.pendingLocalDescription", "RTCSessionDescription"); |
| shouldBeType("pcB.currentLocalDescription", "RTCSessionDescription"); |
| |
| debug("<br>B: send offer to A"); |
| debug("A <-- offer --- B"); |
| debug("A: got offer from B, set it as remote description"); |
| return pcA.setRemoteDescription(pcB.localDescription); |
| }) |
| .then(function () { |
| debug("A: remote offer set (session update so we have both pending and current remote descriptions)"); |
| shouldBe("pcA.signalingState", "'have-remote-offer'"); |
| shouldBeType("pcA.pendingRemoteDescription", "RTCSessionDescription"); |
| shouldBeType("pcA.currentRemoteDescription", "RTCSessionDescription"); |
| |
| debug("<br>A: create answer"); |
| return pcA.createAnswer(); |
| }) |
| .then(function (answer) { |
| debug("A: got answer, set it as local description"); |
| return pcA.setLocalDescription(answer); |
| }) |
| .then(function () { |
| debug("A: local answer set"); |
| shouldBe("pcA.signalingState", "'stable'"); |
| shouldBeNull("pcA.pendingLocalDescription"); |
| shouldBeType("pcA.currentLocalDescription", "RTCSessionDescription"); |
| shouldBeNull("pcA.pendingRemoteDescription"); |
| shouldBeType("pcA.currentRemoteDescription", "RTCSessionDescription"); |
| |
| debug("<br>A: send answer to B"); |
| debug("A --- answer -> B"); |
| debug("B: got answer from A, set it as remote description"); |
| return pcB.setRemoteDescription(pcA.localDescription); |
| }) |
| .then(function () { |
| debug("B: remote answer set"); |
| shouldBe("pcB.signalingState", "'stable'"); |
| shouldBeNull("pcB.pendingLocalDescription"); |
| shouldBeType("pcB.currentLocalDescription", "RTCSessionDescription"); |
| shouldBeNull("pcB.pendingRemoteDescription"); |
| shouldBeType("pcB.currentRemoteDescription", "RTCSessionDescription"); |
| debug(""); |
| |
| testPassed("Second offer/answer dialog completed") |
| debug(""); |
| |
| finishJSTest(); |
| }) |
| .catch(function (error) { |
| testFailed("Error in promise chain: " + error); |
| finishJSTest(); |
| }); |
| |
| window.jsTestIsAsync = true; |
| window.successfullyParsed = true; |
| |
| </script> |
| <script src="../../resources/js-test-post.js"></script> |
| </body> |
| </html> |