<!DOCTYPE HTML> <html> <head> <script type="application/javascript" src="pc.js"></script> </head> <body> <pre id="test"> <script type="application/javascript;version=1.8"> createHTML({ bug: "1032839", title: "Replace video and audio (with WebAudio) tracks", visible: true }); function allLocalStreamsHaveSender(pc) { return pc.getLocalStreams() .every(s => s.getTracks() // Every local stream, .some(t => pc.getSenders() // should have some track, .some(sn => sn.track == t))) // that's being sent over |pc|. } function allRemoteStreamsHaveReceiver(pc) { return pc.getRemoteStreams() .every(s => s.getTracks() // Every remote stream, .some(t => pc.getReceivers() // should have some track, .some(sn => sn.track == t))) // that's being received over |pc|. } function replacetest(wrapper) { var pc = wrapper._pc; var oldSenderCount = pc.getSenders().length; var sender = pc.getSenders().find(sn => sn.track.kind == "video"); var oldTrack = sender.track; ok(sender, "We have a sender for video"); ok(allLocalStreamsHaveSender(pc), "Shouldn't have any local streams without a corresponding sender"); ok(allRemoteStreamsHaveReceiver(pc), "Shouldn't have any remote streams without a corresponding receiver"); var newTrack; var audiotrack; return navigator.mediaDevices.getUserMedia({video:true, audio:true}) .then(newStream => { window.grip = newStream; newTrack = newStream.getVideoTracks()[0]; audiotrack = newStream.getAudioTracks()[0]; isnot(newTrack, sender.track, "replacing with a different track"); ok(!pc.getLocalStreams().some(s => s == newStream), "from a different stream"); return sender.replaceTrack(newTrack); }) .then(() => { is(pc.getSenders().length, oldSenderCount, "same sender count"); is(sender.track, newTrack, "sender.track has been replaced"); ok(!pc.getSenders().map(sn => sn.track).some(t => t == oldTrack), "old track not among senders"); ok(pc.getLocalStreams().some(s => s.getTracks() .some(t => t == sender.track)), "track exists among pc's local streams"); return sender.replaceTrack(audiotrack) .then(() => ok(false, "replacing with different kind should fail"), e => is(e.name, "IncompatibleMediaStreamTrackError", "replacing with different kind should fail")); }); } runNetworkTest(function () { test = new PeerConnectionTest(); test.audioCtx = new AudioContext(); test.setMediaConstraints([{video: true, audio: true}], [{video: true}]); test.chain.removeAfter("PC_REMOTE_WAIT_FOR_MEDIA_FLOW"); // Test replaceTrack on pcRemote separately since it's video only. test.chain.append([ function PC_REMOTE_VIDEOONLY_REPLACE_VIDEOTRACK(test) { return replacetest(test.pcRemote); }, function PC_LOCAL_NEW_VIDEOTRACK_WAIT_FOR_MEDIA_FLOW(test) { return test.pcLocal.waitForMediaFlow(); } ]); // Replace video twice on pcLocal to make sure it still works // (does audio twice too, but hey) test.chain.append([ function PC_LOCAL_AUDIOVIDEO_REPLACE_VIDEOTRACK_1(test) { return replacetest(test.pcLocal); }, function PC_REMOTE_NEW_VIDEOTRACK_WAIT_FOR_MEDIA_FLOW_1(test) { return test.pcRemote.waitForMediaFlow(); }, function PC_LOCAL_AUDIOVIDEO_REPLACE_VIDEOTRACK_2(test) { return replacetest(test.pcLocal); }, function PC_REMOTE_NEW_VIDEOTRACK_WAIT_FOR_MEDIA_FLOW_2(test) { return test.pcRemote.waitForMediaFlow(); } ]); test.chain.append([ function PC_LOCAL_AUDIOVIDEO_REPLACE_VIDEOTRACK_WITHSAME(test) { var pc = test.pcLocal._pc; var sender = pc.getSenders().find(sn => sn.track.kind == "video"); ok(sender, "should still have a sender of video"); return sender.replaceTrack(sender.track) .then(() => ok(true, "replacing with itself should succeed")); }, function PC_REMOTE_NEW_SAME_VIDEOTRACK_WAIT_FOR_MEDIA_FLOW(test) { return test.pcRemote.waitForMediaFlow(); } ]); // Replace the gUM audio track on pcLocal with a WebAudio track. test.chain.append([ function PC_LOCAL_AUDIOVIDEO_REPLACE_AUDIOTRACK_WEBAUDIO(test) { var pc = test.pcLocal._pc; var sender = pc.getSenders().find(sn => sn.track.kind == "audio"); ok(sender, "track has a sender"); var oldSenderCount = pc.getSenders().length; var oldTrack = sender.track; var sourceNode = test.audioCtx.createOscillator(); sourceNode.type = 'sine'; // We need a frequency not too close to the fake audio track // (440Hz for loopback devices, 1kHz for fake tracks). sourceNode.frequency.value = 2000; sourceNode.start(); var destNode = test.audioCtx.createMediaStreamDestination(); sourceNode.connect(destNode); var newTrack = destNode.stream.getAudioTracks()[0]; return sender.replaceTrack(newTrack) .then(() => { is(pc.getSenders().length, oldSenderCount, "same sender count"); ok(!pc.getSenders().some(sn => sn.track == oldTrack), "Replaced track should be removed from senders"); ok(allLocalStreamsHaveSender(pc), "Shouldn't have any streams without a corresponding sender"); is(sender.track, newTrack, "sender.track has been replaced"); ok(pc.getLocalStreams().some(s => s.getTracks() .some(t => t == sender.track)), "track exists among pc's local streams"); }); } ]); test.chain.append([ function PC_LOCAL_CHECK_WEBAUDIO_FLOW_PRESENT(test) { return test.pcRemote.checkReceivingToneFrom(test.audioCtx, test.pcLocal); } ]); test.chain.append([ function PC_LOCAL_INVALID_ADD_VIDEOTRACKS(test) { var stream = test.pcLocal._pc.getLocalStreams()[0]; var track = stream.getVideoTracks()[0]; try { test.pcLocal._pc.addTrack(track, stream); ok(false, "addTrack existing track should fail"); } catch (e) { is(e.name, "InvalidParameterError", "addTrack existing track should fail"); } try { test.pcLocal._pc.addTrack(track, stream); ok(false, "addTrack existing track should fail"); } catch (e) { is(e.name, "InvalidParameterError", "addTrack existing track should fail"); } } ]); test.run(); }); </script> </pre> </body> </html>