forked from webrtc/samples
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
426 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* 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. | ||
*/ | ||
button { | ||
margin: 0 20px 0 0; | ||
width: 83px; | ||
} | ||
|
||
button#hangupButton { | ||
margin: 0; | ||
} | ||
|
||
video { | ||
--width: 45%; | ||
width: var(--width); | ||
height: calc(var(--width) * 0.75); | ||
margin: 0 0 20px 0; | ||
vertical-align: top; | ||
} | ||
|
||
video#localVideo { | ||
margin: 0 20px 20px 0; | ||
} | ||
|
||
div.box { | ||
margin: 1em; | ||
} | ||
|
||
@media screen and (max-width: 400px) { | ||
button { | ||
width: 83px; | ||
margin: 0 11px 10px 0; | ||
} | ||
|
||
video { | ||
height: 90px; | ||
margin: 0 0 10px 0; | ||
width: calc(50% - 7px); | ||
} | ||
video#localVideo { | ||
margin: 0 10px 20px 0; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<!DOCTYPE html> | ||
<!-- | ||
* 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. | ||
--> | ||
<html> | ||
<head> | ||
|
||
<meta charset="utf-8"> | ||
<meta name="description" content="WebRTC code samples"> | ||
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1"> | ||
<meta itemprop="description" content="Client-side WebRTC code samples"> | ||
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png"> | ||
<meta itemprop="name" content="WebRTC code samples"> | ||
<meta name="mobile-web-app-capable" content="yes"> | ||
<meta id="theme-color" name="theme-color" content="#ffffff"> | ||
|
||
<base target="_blank"> | ||
|
||
<title>Peer connection</title> | ||
|
||
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png"> | ||
<link href="//fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css"> | ||
<link rel="stylesheet" href="../../../css/main.css"/> | ||
<link rel="stylesheet" href="css/main.css"/> | ||
|
||
</head> | ||
|
||
<body> | ||
|
||
<div id="container"> | ||
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> | ||
<span>Peer connection</span></h1> | ||
|
||
<p>This sample shows how to setup a connection between two peers using | ||
<a href="https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection">RTCPeerConnection</a>. | ||
An option to specify the <a href="https://webrtc.org/web-apis/chrome/unified-plan/">SDP semantics</a> for | ||
the connection is also available (unified-plan, plan-b or default). | ||
</p> | ||
|
||
<video id="localVideo" playsinline autoplay muted></video> | ||
<video id="remoteVideo" playsinline autoplay></video> | ||
<div class="graph-container" id="bitrateGraph"> | ||
<div>Bitrate</div> | ||
<canvas id="bitrateCanvas"></canvas> | ||
</div> | ||
|
||
<div class="box"> | ||
<button id="startButton">Start</button> | ||
<button id="callButton">Call</button> | ||
<button id="hangupButton">Hang Up</button> | ||
</div> | ||
|
||
<div class="box"> | ||
<span>SDP Semantics:</span> | ||
<select id="sdpSemantics"> | ||
<option selected value="">Default</option> | ||
<option value="unified-plan">Unified Plan</option> | ||
<option value="plan-b">Plan B</option> | ||
</select> | ||
</div> | ||
|
||
<p>View the console to see logging. The <code>MediaStream</code> object <code>localStream</code>, and the <code>RTCPeerConnection</code> | ||
objects <code>pc1</code> and <code>pc2</code> are in global scope, so you can inspect them in the console as | ||
well.</p> | ||
|
||
<p>For more information about RTCPeerConnection, see <a href="http://www.html5rocks.com/en/tutorials/webrtc/basics/" | ||
title="HTML5 Rocks article about WebRTC by Sam Dutton">Getting | ||
Started With WebRTC</a>.</p> | ||
|
||
|
||
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/peerconnection/pc1" | ||
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a> | ||
|
||
</div> | ||
|
||
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script> | ||
<script src="js/main.js" async></script> | ||
<script src="../../../js/third_party/graph.js"></script> | ||
|
||
<script src="../../../js/lib/ga.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
/* | ||
* 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}`); | ||
// 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}`); | ||
} | ||
} | ||
|
||
function getSelectedSdpSemantics() { | ||
const sdpSemanticsSelect = document.querySelector('#sdpSemantics'); | ||
const option = sdpSemanticsSelect.options[sdpSemanticsSelect.selectedIndex]; | ||
return option.value === '' ? {} : {sdpSemantics: option.value}; | ||
} | ||
|
||
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 = getSelectedSdpSemantics(); | ||
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; | ||
} | ||
|
||
// query getStats every second | ||
let lastResult = null; | ||
let bitrateSeries = new TimelineDataSeries(); | ||
let bitrateGraph = new TimelineGraphView('bitrateGraph', 'bitrateCanvas'); | ||
bitrateGraph.updateEndDate(); | ||
window.setInterval(() => { | ||
if (!pc2) { | ||
return; | ||
} | ||
const receiver = pc2.getReceivers()[1]; | ||
receiver.getStats().then(res => { | ||
res.forEach(report => { | ||
if (report.type === 'track') { | ||
if (report.isRemote) { | ||
return; | ||
} | ||
const now = report.timestamp; | ||
const delay = report.jitterBufferDelay; | ||
const count = report.jitterBufferEmittedCount; | ||
console.log(report, 'DELAY', delay, 'count', count); | ||
|
||
if (lastResult && lastResult.has(report.id)) { | ||
// calculate bitrate | ||
const rate = (delay - lastResult.get(report.id).jitterBufferDelay) / (count - lastResult.get(report.id).jitterBufferEmittedCount); | ||
|
||
// append to chart | ||
bitrateSeries.addPoint(now, rate); | ||
bitrateGraph.setDataSeries([bitrateSeries]); | ||
bitrateGraph.updateEndDate(); | ||
} | ||
} | ||
}); | ||
lastResult = res; | ||
}); | ||
}, 1000); |
Oops, something went wrong.