214 lines
6.3 KiB
JavaScript
214 lines
6.3 KiB
JavaScript
/*
|
|
* Copyright (c) 2015 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';
|
|
|
|
const startButton = document.getElementById('startButton');
|
|
const callButton = document.getElementById('callButton');
|
|
const hangupButton = document.getElementById('hangupButton');
|
|
callButton.disabled = true;
|
|
hangupButton.disabled = true;
|
|
startButton.addEventListener('click', start);
|
|
callButton.addEventListener('click', call);
|
|
hangupButton.addEventListener('click', hangup);
|
|
|
|
let startTime;
|
|
const localVideo = document.getElementById('localVideo');
|
|
const remoteVideo = document.getElementById('remoteVideo');
|
|
|
|
localVideo.addEventListener('loadedmetadata', function() {
|
|
console.log(`Local video videoWidth: ${this.videoWidth}px, videoHeight: ${this.videoHeight}px`);
|
|
});
|
|
|
|
remoteVideo.addEventListener('loadedmetadata', function() {
|
|
console.log(`Remote video videoWidth: ${this.videoWidth}px, videoHeight: ${this.videoHeight}px`);
|
|
});
|
|
|
|
remoteVideo.addEventListener('resize', () => {
|
|
console.log(`Remote video size changed to ${remoteVideo.videoWidth}x${remoteVideo.videoHeight} - Time since pageload ${performance.now().toFixed(0)}ms`);
|
|
// We'll use the first onsize callback as an indication that video has started
|
|
// playing out.
|
|
if (startTime) {
|
|
const elapsedTime = window.performance.now() - startTime;
|
|
console.log('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
|
|
startTime = null;
|
|
}
|
|
});
|
|
|
|
let localStream;
|
|
let pc1;
|
|
let pc2;
|
|
const offerOptions = {
|
|
offerToReceiveAudio: 1,
|
|
offerToReceiveVideo: 1
|
|
};
|
|
|
|
function getName(pc) {
|
|
return (pc === pc1) ? 'pc1' : 'pc2';
|
|
}
|
|
|
|
function getOtherPc(pc) {
|
|
return (pc === pc1) ? pc2 : pc1;
|
|
}
|
|
|
|
async function start() {
|
|
console.log('Requesting local stream');
|
|
startButton.disabled = true;
|
|
try {
|
|
const stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
|
|
console.log('Received local stream');
|
|
localVideo.srcObject = stream;
|
|
localStream = stream;
|
|
callButton.disabled = false;
|
|
} catch (e) {
|
|
alert(`getUserMedia() error: ${e.name}`);
|
|
}
|
|
}
|
|
|
|
async function call() {
|
|
callButton.disabled = true;
|
|
hangupButton.disabled = false;
|
|
console.log('Starting call');
|
|
startTime = window.performance.now();
|
|
const videoTracks = localStream.getVideoTracks();
|
|
const audioTracks = localStream.getAudioTracks();
|
|
if (videoTracks.length > 0) {
|
|
console.log(`Using video device: ${videoTracks[0].label}`);
|
|
}
|
|
if (audioTracks.length > 0) {
|
|
console.log(`Using audio device: ${audioTracks[0].label}`);
|
|
}
|
|
const configuration = {};
|
|
console.log('RTCPeerConnection configuration:', configuration);
|
|
pc1 = new RTCPeerConnection(configuration);
|
|
console.log('Created local peer connection object pc1');
|
|
pc1.addEventListener('icecandidate', e => onIceCandidate(pc1, e));
|
|
pc2 = new RTCPeerConnection(configuration);
|
|
console.log('Created remote peer connection object pc2');
|
|
pc2.addEventListener('icecandidate', e => onIceCandidate(pc2, e));
|
|
pc1.addEventListener('iceconnectionstatechange', e => onIceStateChange(pc1, e));
|
|
pc2.addEventListener('iceconnectionstatechange', e => onIceStateChange(pc2, e));
|
|
pc2.addEventListener('track', gotRemoteStream);
|
|
|
|
localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));
|
|
console.log('Added local stream to pc1');
|
|
|
|
try {
|
|
console.log('pc1 createOffer start');
|
|
const offer = await pc1.createOffer(offerOptions);
|
|
await onCreateOfferSuccess(offer);
|
|
} catch (e) {
|
|
onCreateSessionDescriptionError(e);
|
|
}
|
|
}
|
|
|
|
function onCreateSessionDescriptionError(error) {
|
|
console.log(`Failed to create session description: ${error.toString()}`);
|
|
}
|
|
|
|
async function onCreateOfferSuccess(desc) {
|
|
console.log(`Offer from pc1\n${desc.sdp}`);
|
|
console.log('pc1 setLocalDescription start');
|
|
try {
|
|
await pc1.setLocalDescription(desc);
|
|
onSetLocalSuccess(pc1);
|
|
} catch (e) {
|
|
onSetSessionDescriptionError();
|
|
}
|
|
|
|
console.log('pc2 setRemoteDescription start');
|
|
try {
|
|
await pc2.setRemoteDescription(desc);
|
|
onSetRemoteSuccess(pc2);
|
|
} catch (e) {
|
|
onSetSessionDescriptionError();
|
|
}
|
|
|
|
console.log('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.
|
|
try {
|
|
const answer = await pc2.createAnswer();
|
|
await onCreateAnswerSuccess(answer);
|
|
} catch (e) {
|
|
onCreateSessionDescriptionError(e);
|
|
}
|
|
}
|
|
|
|
function onSetLocalSuccess(pc) {
|
|
console.log(`${getName(pc)} setLocalDescription complete`);
|
|
}
|
|
|
|
function onSetRemoteSuccess(pc) {
|
|
console.log(`${getName(pc)} setRemoteDescription complete`);
|
|
}
|
|
|
|
function onSetSessionDescriptionError(error) {
|
|
console.log(`Failed to set session description: ${error.toString()}`);
|
|
}
|
|
|
|
function gotRemoteStream(e) {
|
|
if (remoteVideo.srcObject !== e.streams[0]) {
|
|
remoteVideo.srcObject = e.streams[0];
|
|
console.log('pc2 received remote stream');
|
|
}
|
|
}
|
|
|
|
async function onCreateAnswerSuccess(desc) {
|
|
console.log(`Answer from pc2:\n${desc.sdp}`);
|
|
console.log('pc2 setLocalDescription start');
|
|
try {
|
|
await pc2.setLocalDescription(desc);
|
|
onSetLocalSuccess(pc2);
|
|
} catch (e) {
|
|
onSetSessionDescriptionError(e);
|
|
}
|
|
console.log('pc1 setRemoteDescription start');
|
|
try {
|
|
await pc1.setRemoteDescription(desc);
|
|
onSetRemoteSuccess(pc1);
|
|
} catch (e) {
|
|
onSetSessionDescriptionError(e);
|
|
}
|
|
}
|
|
|
|
async function onIceCandidate(pc, event) {
|
|
try {
|
|
await (getOtherPc(pc).addIceCandidate(event.candidate));
|
|
onAddIceCandidateSuccess(pc);
|
|
} catch (e) {
|
|
onAddIceCandidateError(pc, e);
|
|
}
|
|
console.log(`${getName(pc)} ICE candidate:\n${event.candidate ? event.candidate.candidate : '(null)'}`);
|
|
}
|
|
|
|
function onAddIceCandidateSuccess(pc) {
|
|
console.log(`${getName(pc)} addIceCandidate success`);
|
|
}
|
|
|
|
function onAddIceCandidateError(pc, error) {
|
|
console.log(`${getName(pc)} failed to add ICE Candidate: ${error.toString()}`);
|
|
}
|
|
|
|
function onIceStateChange(pc, event) {
|
|
if (pc) {
|
|
console.log(`${getName(pc)} ICE state: ${pc.iceConnectionState}`);
|
|
console.log('ICE state change event: ', event);
|
|
}
|
|
}
|
|
|
|
function hangup() {
|
|
console.log('Ending call');
|
|
pc1.close();
|
|
pc2.close();
|
|
pc1 = null;
|
|
pc2 = null;
|
|
hangupButton.disabled = true;
|
|
callButton.disabled = false;
|
|
} |