Skip to content
This repository was archived by the owner on May 13, 2024. It is now read-only.

Commit 63a46f7

Browse files
authored
Merge pull request #24 from fippo/replaceTrack
add replaceTrack test
2 parents 1951157 + c04d347 commit 63a46f7

File tree

3 files changed

+372
-0
lines changed

3 files changed

+372
-0
lines changed

src/replaceTrack/css/main.css

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
3+
*
4+
* Use of this source code is governed by a BSD-style license
5+
* that can be found in the LICENSE file in the root of the source
6+
* tree.
7+
*/
8+
button {
9+
margin: 0 20px 0 0;
10+
width: 83px;
11+
}
12+
13+
button#hangupButton {
14+
margin: 0;
15+
}
16+
17+
video {
18+
height: 225px;
19+
margin: 0 0 20px 0;
20+
vertical-align: top;
21+
width: calc(50% - 12px);
22+
}
23+
24+
video#localVideo {
25+
margin: 0 20px 20px 0;
26+
}
27+
28+
@media screen and (max-width: 400px) {
29+
button {
30+
width: 83px;
31+
}
32+
33+
button {
34+
margin: 0 11px 10px 0;
35+
}
36+
37+
38+
video {
39+
height: 90px;
40+
margin: 0 0 10px 0;
41+
width: calc(50% - 7px);
42+
}
43+
video#localVideo {
44+
margin: 0 10px 20px 0;
45+
}
46+
47+
}

src/replaceTrack/index.html

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!DOCTYPE html>
2+
<!--
3+
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by a BSD-style license
6+
* that can be found in the LICENSE file in the root of the source
7+
* tree.
8+
-->
9+
<html>
10+
<head>
11+
<title>ReplaceTrack</title>
12+
<link rel="stylesheet" href="css/main.css" />
13+
</head>
14+
15+
<body>
16+
17+
<div id="container">
18+
<video id="localVideo" autoplay muted></video>
19+
<video id="remoteVideo" autoplay></video>
20+
21+
<div>
22+
<button id="startButton">Start</button>
23+
<button id="callButton">Call</button>
24+
<button id="restartButton">Restart video with replaceTrack</button>
25+
<button id="muteButton" disabled>toggle audio with replaceTrack</button>
26+
<button id="hangupButton">Hang Up</button>
27+
</div>
28+
29+
</div>
30+
31+
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
32+
<script src="js/main.js"></script>
33+
</body>
34+
</html>

src/replaceTrack/js/main.js

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
/*
2+
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
3+
*
4+
* Use of this source code is governed by a BSD-style license
5+
* that can be found in the LICENSE file in the root of the source
6+
* tree.
7+
*/
8+
'use strict';
9+
10+
function trace(arg) {
11+
var now = (window.performance.now() / 1000).toFixed(3);
12+
console.log(now + ': ', arg);
13+
}
14+
15+
var startButton = document.getElementById('startButton');
16+
var callButton = document.getElementById('callButton');
17+
var hangupButton = document.getElementById('hangupButton');
18+
var restartButton = document.getElementById('restartButton');
19+
var muteButton = document.querySelector('button#muteButton');
20+
callButton.disabled = true;
21+
hangupButton.disabled = true;
22+
restartButton.disabled = true;
23+
startButton.onclick = start;
24+
callButton.onclick = call;
25+
hangupButton.onclick = hangup;
26+
restartButton.onclick = restartVideo;
27+
muteButton.onclick = toggleMute;
28+
29+
var supportsReplaceTrack =('RTCRtpSender' in window &&
30+
'replaceTrack' in RTCRtpSender.prototype);
31+
32+
var startTime;
33+
var localVideo = document.getElementById('localVideo');
34+
var remoteVideo = document.getElementById('remoteVideo');
35+
36+
localVideo.addEventListener('loadedmetadata', function() {
37+
trace('Local video videoWidth: ' + this.videoWidth +
38+
'px, videoHeight: ' + this.videoHeight + 'px');
39+
});
40+
41+
remoteVideo.addEventListener('loadedmetadata', function() {
42+
trace('Remote video videoWidth: ' + this.videoWidth +
43+
'px, videoHeight: ' + this.videoHeight + 'px');
44+
});
45+
46+
remoteVideo.onresize = function() {
47+
trace('Remote video size changed to ' +
48+
remoteVideo.videoWidth + 'x' + remoteVideo.videoHeight);
49+
// We'll use the first onsize callback as an indication that video has started
50+
// playing out.
51+
if (startTime) {
52+
var elapsedTime = window.performance.now() - startTime;
53+
trace('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
54+
startTime = null;
55+
}
56+
};
57+
58+
var localStream;
59+
var pc1;
60+
var pc2;
61+
var offerOptions = {
62+
offerToReceiveAudio: 1,
63+
offerToReceiveVideo: 1
64+
};
65+
66+
function getName(pc) {
67+
return (pc === pc1) ? 'pc1' : 'pc2';
68+
}
69+
70+
function getOtherPc(pc) {
71+
return (pc === pc1) ? pc2 : pc1;
72+
}
73+
74+
function gotStream(stream) {
75+
trace('Received local stream');
76+
localVideo.srcObject = stream;
77+
localStream = stream;
78+
callButton.disabled = false;
79+
}
80+
81+
function start() {
82+
trace('Requesting local stream');
83+
startButton.disabled = true;
84+
navigator.mediaDevices.getUserMedia({
85+
audio: true,
86+
video: true
87+
})
88+
.then(gotStream)
89+
.catch(function(e) {
90+
alert('getUserMedia() error: ' + e.name);
91+
});
92+
}
93+
94+
function call() {
95+
callButton.disabled = true;
96+
hangupButton.disabled = false;
97+
restartButton.disabled = !supportsReplaceTrack;
98+
muteButton.disabled = !supportsReplaceTrack;
99+
100+
startTime = window.performance.now();
101+
var videoTracks = localStream.getVideoTracks();
102+
var audioTracks = localStream.getAudioTracks();
103+
if (videoTracks.length > 0) {
104+
trace('Using video device: ' + videoTracks[0].label);
105+
}
106+
if (audioTracks.length > 0) {
107+
trace('Using audio device: ' + audioTracks[0].label);
108+
}
109+
var servers = null;
110+
pc1 = new RTCPeerConnection(servers);
111+
trace('Created local peer connection object pc1');
112+
pc1.onicecandidate = function(e) {
113+
onIceCandidate(pc1, e);
114+
};
115+
pc2 = new RTCPeerConnection(servers);
116+
trace('Created remote peer connection object pc2');
117+
pc2.onicecandidate = function(e) {
118+
onIceCandidate(pc2, e);
119+
};
120+
pc1.oniceconnectionstatechange = function(e) {
121+
onIceStateChange(pc1, e);
122+
};
123+
pc2.oniceconnectionstatechange = function(e) {
124+
onIceStateChange(pc2, e);
125+
};
126+
pc2.ontrack = gotRemoteStream;
127+
128+
localStream.getTracks().forEach(
129+
function(track) {
130+
pc1.addTrack(
131+
track,
132+
localStream
133+
);
134+
}
135+
);
136+
trace('Added local stream to pc1');
137+
138+
trace('pc1 createOffer start');
139+
pc1.createOffer(
140+
offerOptions
141+
).then(
142+
onCreateOfferSuccess,
143+
onCreateSessionDescriptionError
144+
);
145+
}
146+
147+
function onCreateSessionDescriptionError(error) {
148+
trace('Failed to create session description: ' + error.toString());
149+
}
150+
151+
function onCreateOfferSuccess(desc) {
152+
trace('Offer from pc1\n' + desc.sdp);
153+
trace('pc1 setLocalDescription start');
154+
pc1.setLocalDescription(desc).then(
155+
function() {
156+
onSetLocalSuccess(pc1);
157+
},
158+
onSetSessionDescriptionError
159+
);
160+
trace('pc2 setRemoteDescription start');
161+
pc2.setRemoteDescription(desc).then(
162+
function() {
163+
onSetRemoteSuccess(pc2);
164+
},
165+
onSetSessionDescriptionError
166+
);
167+
trace('pc2 createAnswer start');
168+
// Since the 'remote' side has no media stream we need
169+
// to pass in the right constraints in order for it to
170+
// accept the incoming offer of audio and video.
171+
pc2.createAnswer().then(
172+
onCreateAnswerSuccess,
173+
onCreateSessionDescriptionError
174+
);
175+
}
176+
177+
function onSetLocalSuccess(pc) {
178+
trace(getName(pc) + ' setLocalDescription complete');
179+
}
180+
181+
function onSetRemoteSuccess(pc) {
182+
trace(getName(pc) + ' setRemoteDescription complete');
183+
}
184+
185+
function onSetSessionDescriptionError(error) {
186+
trace('Failed to set session description: ' + error.toString());
187+
}
188+
189+
function gotRemoteStream(e) {
190+
if (remoteVideo.srcObject !== e.streams[0]) {
191+
remoteVideo.srcObject = e.streams[0];
192+
trace('pc2 received remote stream');
193+
}
194+
}
195+
196+
function onCreateAnswerSuccess(desc) {
197+
trace('Answer from pc2:\n' + desc.sdp);
198+
trace('pc2 setLocalDescription start');
199+
pc2.setLocalDescription(desc).then(
200+
function() {
201+
onSetLocalSuccess(pc2);
202+
},
203+
onSetSessionDescriptionError
204+
);
205+
trace('pc1 setRemoteDescription start');
206+
pc1.setRemoteDescription(desc).then(
207+
function() {
208+
onSetRemoteSuccess(pc1);
209+
},
210+
onSetSessionDescriptionError
211+
);
212+
}
213+
214+
function onIceCandidate(pc, event) {
215+
getOtherPc(pc).addIceCandidate(event.candidate)
216+
.then(
217+
function() {
218+
onAddIceCandidateSuccess(pc);
219+
},
220+
function(err) {
221+
onAddIceCandidateError(pc, err);
222+
}
223+
);
224+
trace(getName(pc) + ' ICE candidate: \n' + (event.candidate ?
225+
event.candidate.candidate : '(null)'));
226+
}
227+
228+
function onAddIceCandidateSuccess(pc) {
229+
trace(getName(pc) + ' addIceCandidate success');
230+
}
231+
232+
function onAddIceCandidateError(pc, error) {
233+
trace(getName(pc) + ' failed to add ICE Candidate: ' + error.toString());
234+
}
235+
236+
function onIceStateChange(pc, event) {
237+
if (pc) {
238+
trace(getName(pc) + ' ICE state: ' + pc.iceConnectionState);
239+
console.log('ICE state change event: ', event);
240+
}
241+
}
242+
243+
function hangup() {
244+
trace('Ending call');
245+
pc1.close();
246+
pc2.close();
247+
pc1 = null;
248+
pc2 = null;
249+
hangupButton.disabled = true;
250+
callButton.disabled = false;
251+
}
252+
253+
// Stops and restarts the video with replaceTrack.
254+
function restartVideo() {
255+
localStream.getVideoTracks()[0].stop();
256+
localStream.removeTrack(localStream.getVideoTracks()[0]);
257+
window.setTimeout(function() {
258+
navigator.mediaDevices.getUserMedia({video: true})
259+
.then(function(stream) {
260+
localStream.addTrack(stream.getVideoTracks()[0]);
261+
var sender = pc1.getSenders().find(function(s) {
262+
return s.track && s.track.kind === 'video';
263+
});
264+
return sender.replaceTrack(stream.getVideoTracks()[0]);
265+
})
266+
.then(function() {
267+
console.log('Replaced video track');
268+
})
269+
.catch(function(err) {
270+
console.error(err);
271+
});
272+
}, 5000);
273+
}
274+
275+
// Toggles audio mute with replaceTrack(null/track)
276+
function toggleMute() {
277+
var sender = pc1.getSenders()[0];
278+
var p;
279+
if (!sender.track) {
280+
trace('re-adding audio track');
281+
p = sender.replaceTrack(localStream.getAudioTracks()[0]);
282+
} else {
283+
trace('replacing audio track with null');
284+
p = sender.replaceTrack(null);
285+
}
286+
p.then(function() {
287+
console.log('replaced track');
288+
}).catch(function(err) {
289+
console.error('during replaceTrack', err);
290+
});
291+
}

0 commit comments

Comments
 (0)