-
Notifications
You must be signed in to change notification settings - Fork 48
add replaceTrack test #24
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| /* | ||
| * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. | ||
| * | ||
| * Use of this source code is governed by a BSD-style license | ||
| * that can be found in the LICENSE file in the root of the source | ||
| * tree. | ||
| */ | ||
| button { | ||
| margin: 0 20px 0 0; | ||
| width: 83px; | ||
| } | ||
|
|
||
| button#hangupButton { | ||
| margin: 0; | ||
| } | ||
|
|
||
| video { | ||
| height: 225px; | ||
| margin: 0 0 20px 0; | ||
| vertical-align: top; | ||
| width: calc(50% - 12px); | ||
| } | ||
|
|
||
| video#localVideo { | ||
| margin: 0 20px 20px 0; | ||
| } | ||
|
|
||
| @media screen and (max-width: 400px) { | ||
| button { | ||
| width: 83px; | ||
| } | ||
|
|
||
| button { | ||
| margin: 0 11px 10px 0; | ||
| } | ||
|
|
||
|
|
||
| video { | ||
| height: 90px; | ||
| margin: 0 0 10px 0; | ||
| width: calc(50% - 7px); | ||
| } | ||
| video#localVideo { | ||
| margin: 0 10px 20px 0; | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <!DOCTYPE html> | ||
| <!-- | ||
| * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. | ||
| * | ||
| * Use of this source code is governed by a BSD-style license | ||
| * that can be found in the LICENSE file in the root of the source | ||
| * tree. | ||
| --> | ||
| <html> | ||
| <head> | ||
| <title>ReplaceTrack</title> | ||
| <link rel="stylesheet" href="css/main.css" /> | ||
| </head> | ||
|
|
||
| <body> | ||
|
|
||
| <div id="container"> | ||
| <video id="localVideo" autoplay muted></video> | ||
| <video id="remoteVideo" autoplay></video> | ||
|
|
||
| <div> | ||
| <button id="startButton">Start</button> | ||
| <button id="callButton">Call</button> | ||
| <button id="restartButton">Restart video with replaceTrack</button> | ||
| <button id="muteButton" disabled>toggle audio with replaceTrack</button> | ||
| <button id="hangupButton">Hang Up</button> | ||
| </div> | ||
|
|
||
| </div> | ||
|
|
||
| <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script> | ||
| <script src="js/main.js"></script> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,291 @@ | ||
| /* | ||
| * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. | ||
| * | ||
| * Use of this source code is governed by a BSD-style license | ||
| * that can be found in the LICENSE file in the root of the source | ||
| * tree. | ||
| */ | ||
| 'use strict'; | ||
|
|
||
| function trace(arg) { | ||
| var now = (window.performance.now() / 1000).toFixed(3); | ||
| console.log(now + ': ', arg); | ||
| } | ||
|
|
||
| var startButton = document.getElementById('startButton'); | ||
| var callButton = document.getElementById('callButton'); | ||
| var hangupButton = document.getElementById('hangupButton'); | ||
| var restartButton = document.getElementById('restartButton'); | ||
| var muteButton = document.querySelector('button#muteButton'); | ||
| callButton.disabled = true; | ||
| hangupButton.disabled = true; | ||
| restartButton.disabled = true; | ||
| startButton.onclick = start; | ||
| callButton.onclick = call; | ||
| hangupButton.onclick = hangup; | ||
| restartButton.onclick = restartVideo; | ||
| muteButton.onclick = toggleMute; | ||
|
|
||
| var supportsReplaceTrack =('RTCRtpSender' in window && | ||
| 'replaceTrack' in RTCRtpSender.prototype); | ||
|
|
||
| var startTime; | ||
| var localVideo = document.getElementById('localVideo'); | ||
| var remoteVideo = document.getElementById('remoteVideo'); | ||
|
|
||
| localVideo.addEventListener('loadedmetadata', function() { | ||
| trace('Local video videoWidth: ' + this.videoWidth + | ||
| 'px, videoHeight: ' + this.videoHeight + 'px'); | ||
| }); | ||
|
|
||
| remoteVideo.addEventListener('loadedmetadata', function() { | ||
| trace('Remote video videoWidth: ' + this.videoWidth + | ||
| 'px, videoHeight: ' + this.videoHeight + 'px'); | ||
| }); | ||
|
|
||
| remoteVideo.onresize = function() { | ||
| trace('Remote video size changed to ' + | ||
| remoteVideo.videoWidth + 'x' + remoteVideo.videoHeight); | ||
| // We'll use the first onsize callback as an indication that video has started | ||
| // playing out. | ||
| if (startTime) { | ||
| var elapsedTime = window.performance.now() - startTime; | ||
| trace('Setup time: ' + elapsedTime.toFixed(3) + 'ms'); | ||
| startTime = null; | ||
| } | ||
| }; | ||
|
|
||
| var localStream; | ||
| var pc1; | ||
| var pc2; | ||
| var offerOptions = { | ||
| offerToReceiveAudio: 1, | ||
| offerToReceiveVideo: 1 | ||
| }; | ||
|
|
||
| function getName(pc) { | ||
| return (pc === pc1) ? 'pc1' : 'pc2'; | ||
| } | ||
|
|
||
| function getOtherPc(pc) { | ||
| return (pc === pc1) ? pc2 : pc1; | ||
| } | ||
|
|
||
| function gotStream(stream) { | ||
| trace('Received local stream'); | ||
| localVideo.srcObject = stream; | ||
| localStream = stream; | ||
| callButton.disabled = false; | ||
| } | ||
|
|
||
| function start() { | ||
| trace('Requesting local stream'); | ||
| startButton.disabled = true; | ||
| navigator.mediaDevices.getUserMedia({ | ||
| audio: true, | ||
| video: true | ||
| }) | ||
| .then(gotStream) | ||
| .catch(function(e) { | ||
| alert('getUserMedia() error: ' + e.name); | ||
| }); | ||
| } | ||
|
|
||
| function call() { | ||
| callButton.disabled = true; | ||
| hangupButton.disabled = false; | ||
| restartButton.disabled = !supportsReplaceTrack; | ||
| muteButton.disabled = !supportsReplaceTrack; | ||
|
|
||
| startTime = window.performance.now(); | ||
| var videoTracks = localStream.getVideoTracks(); | ||
| var audioTracks = localStream.getAudioTracks(); | ||
| if (videoTracks.length > 0) { | ||
| trace('Using video device: ' + videoTracks[0].label); | ||
| } | ||
| if (audioTracks.length > 0) { | ||
| trace('Using audio device: ' + audioTracks[0].label); | ||
| } | ||
| var servers = null; | ||
| pc1 = new RTCPeerConnection(servers); | ||
| trace('Created local peer connection object pc1'); | ||
| pc1.onicecandidate = function(e) { | ||
| onIceCandidate(pc1, e); | ||
| }; | ||
| pc2 = new RTCPeerConnection(servers); | ||
| trace('Created remote peer connection object pc2'); | ||
| pc2.onicecandidate = function(e) { | ||
| onIceCandidate(pc2, e); | ||
| }; | ||
| pc1.oniceconnectionstatechange = function(e) { | ||
| onIceStateChange(pc1, e); | ||
| }; | ||
| pc2.oniceconnectionstatechange = function(e) { | ||
| onIceStateChange(pc2, e); | ||
| }; | ||
| pc2.ontrack = gotRemoteStream; | ||
|
|
||
| localStream.getTracks().forEach( | ||
| function(track) { | ||
| pc1.addTrack( | ||
| track, | ||
| localStream | ||
| ); | ||
| } | ||
| ); | ||
| trace('Added local stream to pc1'); | ||
|
|
||
| trace('pc1 createOffer start'); | ||
| pc1.createOffer( | ||
| offerOptions | ||
| ).then( | ||
| onCreateOfferSuccess, | ||
| onCreateSessionDescriptionError | ||
| ); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of a bunch of helper functions could this be more compactly written as? If more detailed error is desired, more try-catches or a variable keeping track of which stage we're at can be added. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure how comfortable @KaptenJansson feels with es6 and async/await. But I wouldn't want to start the migration here and end up with a weird mix of styles There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK |
||
| } | ||
|
|
||
| function onCreateSessionDescriptionError(error) { | ||
| trace('Failed to create session description: ' + error.toString()); | ||
| } | ||
|
|
||
| function onCreateOfferSuccess(desc) { | ||
| trace('Offer from pc1\n' + desc.sdp); | ||
| trace('pc1 setLocalDescription start'); | ||
| pc1.setLocalDescription(desc).then( | ||
| function() { | ||
| onSetLocalSuccess(pc1); | ||
| }, | ||
| onSetSessionDescriptionError | ||
| ); | ||
| trace('pc2 setRemoteDescription start'); | ||
| pc2.setRemoteDescription(desc).then( | ||
| function() { | ||
| onSetRemoteSuccess(pc2); | ||
| }, | ||
| onSetSessionDescriptionError | ||
| ); | ||
| trace('pc2 createAnswer start'); | ||
| // Since the 'remote' side has no media stream we need | ||
| // to pass in the right constraints in order for it to | ||
| // accept the incoming offer of audio and video. | ||
| pc2.createAnswer().then( | ||
| onCreateAnswerSuccess, | ||
| onCreateSessionDescriptionError | ||
| ); | ||
| } | ||
|
|
||
| function onSetLocalSuccess(pc) { | ||
| trace(getName(pc) + ' setLocalDescription complete'); | ||
| } | ||
|
|
||
| function onSetRemoteSuccess(pc) { | ||
| trace(getName(pc) + ' setRemoteDescription complete'); | ||
| } | ||
|
|
||
| function onSetSessionDescriptionError(error) { | ||
| trace('Failed to set session description: ' + error.toString()); | ||
| } | ||
|
|
||
| function gotRemoteStream(e) { | ||
| if (remoteVideo.srcObject !== e.streams[0]) { | ||
| remoteVideo.srcObject = e.streams[0]; | ||
| trace('pc2 received remote stream'); | ||
| } | ||
| } | ||
|
|
||
| function onCreateAnswerSuccess(desc) { | ||
| trace('Answer from pc2:\n' + desc.sdp); | ||
| trace('pc2 setLocalDescription start'); | ||
| pc2.setLocalDescription(desc).then( | ||
| function() { | ||
| onSetLocalSuccess(pc2); | ||
| }, | ||
| onSetSessionDescriptionError | ||
| ); | ||
| trace('pc1 setRemoteDescription start'); | ||
| pc1.setRemoteDescription(desc).then( | ||
| function() { | ||
| onSetRemoteSuccess(pc1); | ||
| }, | ||
| onSetSessionDescriptionError | ||
| ); | ||
| } | ||
|
|
||
| function onIceCandidate(pc, event) { | ||
| getOtherPc(pc).addIceCandidate(event.candidate) | ||
| .then( | ||
| function() { | ||
| onAddIceCandidateSuccess(pc); | ||
| }, | ||
| function(err) { | ||
| onAddIceCandidateError(pc, err); | ||
| } | ||
| ); | ||
| trace(getName(pc) + ' ICE candidate: \n' + (event.candidate ? | ||
| event.candidate.candidate : '(null)')); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about using async function and await here too? |
||
|
|
||
| function onAddIceCandidateSuccess(pc) { | ||
| trace(getName(pc) + ' addIceCandidate success'); | ||
| } | ||
|
|
||
| function onAddIceCandidateError(pc, error) { | ||
| trace(getName(pc) + ' failed to add ICE Candidate: ' + error.toString()); | ||
| } | ||
|
|
||
| function onIceStateChange(pc, event) { | ||
| if (pc) { | ||
| trace(getName(pc) + ' ICE state: ' + pc.iceConnectionState); | ||
| console.log('ICE state change event: ', event); | ||
| } | ||
| } | ||
|
|
||
| function hangup() { | ||
| trace('Ending call'); | ||
| pc1.close(); | ||
| pc2.close(); | ||
| pc1 = null; | ||
| pc2 = null; | ||
| hangupButton.disabled = true; | ||
| callButton.disabled = false; | ||
| } | ||
|
|
||
| // Stops and restarts the video with replaceTrack. | ||
| function restartVideo() { | ||
| localStream.getVideoTracks()[0].stop(); | ||
| localStream.removeTrack(localStream.getVideoTracks()[0]); | ||
| window.setTimeout(function() { | ||
| navigator.mediaDevices.getUserMedia({video: true}) | ||
| .then(function(stream) { | ||
| localStream.addTrack(stream.getVideoTracks()[0]); | ||
| var sender = pc1.getSenders().find(function(s) { | ||
| return s.track && s.track.kind === 'video'; | ||
| }); | ||
| return sender.replaceTrack(stream.getVideoTracks()[0]); | ||
| }) | ||
| .then(function() { | ||
| console.log('Replaced video track'); | ||
| }) | ||
| .catch(function(err) { | ||
| console.error(err); | ||
| }); | ||
| }, 5000); | ||
| } | ||
|
|
||
| // Toggles audio mute with replaceTrack(null/track) | ||
| function toggleMute() { | ||
| var sender = pc1.getSenders()[0]; | ||
| var p; | ||
| if (!sender.track) { | ||
| trace('re-adding audio track'); | ||
| p = sender.replaceTrack(localStream.getAudioTracks()[0]); | ||
| } else { | ||
| trace('replacing audio track with null'); | ||
| p = sender.replaceTrack(null); | ||
| } | ||
| p.then(function() { | ||
| console.log('replaced track'); | ||
| }).catch(function(err) { | ||
| console.error('during replaceTrack', err); | ||
| }); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Remove extra indentation of then()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you but eslint does not sadly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:'( saddest story