Last active
November 4, 2021 16:24
-
-
Save dzeitman/b5cdcd04185d03705cf7d5c98ffa91f6 to your computer and use it in GitHub Desktop.
Dolby.io-CascadiaJs Workshop - Fiddle Demo
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
/** Create a list of Cities for our Money Heist Characters */ | |
const cities = ['Seattle', 'Vancouver', 'Portland', 'Tokyo', 'Berlin', 'Palermo', 'Nairobi', 'Denver', 'Helsinki', 'Rio', 'Moscow', 'Oslo']; | |
let randomName = cities[Math.floor(Math.random() * cities.length)]; | |
// We'll pulls some images that have been named after cities... {Money Heist} and replace our city name in URL and add some cloudinary transformations. | |
let imagePlaceholder = `https://res.cloudinary.com/dolby-io/image/upload/e_art:red_rock/ar_1:1,c_fill,g_auto,r_max,w_50/v1634690310/dolby-hackathon/cities/${randomName}.png` | |
/** Update varibles when form input changes */ | |
function updateNameValue(e) { | |
randomName = e.target.value; | |
} | |
// URL to our Token Server | |
const tokenServerURL = 'Enter the url to your token server here'; | |
/** initializeToken authorization flow on script load **/ | |
(function () { | |
try { | |
getTokenAndInitalize() | |
} catch (e) { | |
alert('Something went wrong initalizaton : ' + e); | |
} | |
})(); | |
/** Fetch our token and start initialization of SDK, update UI sources */ | |
async function getTokenAndInitalize() { | |
return fetch(tokenServerURL) | |
.then((res) => { | |
return res.json(); | |
}) | |
.then((result) => { | |
VoxeetSDK.initializeToken(result.access_token, refreshToken); | |
return result.access_token | |
}) | |
.then((token) => { | |
console.info('token received', token); | |
initializeConferenceSession() | |
}) | |
.catch((error) => { | |
console.error(error); | |
}); | |
} | |
/** Refresh Token is called when token expiration is 50% completed, this keeps the app initialized */ | |
async function refreshToken() { | |
return fetch(tokenServerURL) | |
.then((res) => { | |
return res.json(); | |
}) | |
.then((json) => json.access_token) | |
.catch((error) => { | |
console.error(error); | |
}); | |
} | |
/** Create the participantInfo object and open the session with the object */ | |
async function initializeConferenceSession() { | |
let participantInfo = { name: randomName, avatarUrl: imagePlaceholder, externalId: randomName } | |
try { | |
// Open a session for the user | |
await VoxeetSDK.session.open(participantInfo); | |
// Initialize the UI | |
initUI(); | |
console.log('session initialized!'); | |
} catch (e) { | |
alert('Something went wrong: ' + e); | |
} | |
} | |
/* Dolby.io Event handlers */ | |
// When a stream is added to the conference | |
VoxeetSDK.conference.on('streamAdded', (participant, stream) => { | |
if (stream.type === 'ScreenShare') { | |
return addScreenShareNode(stream); | |
} | |
if (stream.getVideoTracks().length) { | |
// Only add the video node if there is a video track | |
addVideoNode(participant, stream); | |
} | |
addParticipantNode(participant); | |
}); | |
// When a stream is updated | |
VoxeetSDK.conference.on('streamUpdated', (participant, stream) => { | |
if (stream.type === 'ScreenShare') return; | |
if (stream.getVideoTracks().length) { | |
// Only add the video node if there is a video track | |
addVideoNode(participant, stream); | |
} else { | |
removeVideoNode(participant); | |
} | |
}); | |
// When a stream is removed from the conference | |
VoxeetSDK.conference.on('streamRemoved', (participant, stream) => { | |
if (stream.type === 'ScreenShare') { | |
return removeScreenShareNode(); | |
} | |
removeVideoNode(participant); | |
removeParticipantNode(participant); | |
}); |
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
//module.js: | |
var configObject = () => { | |
return { | |
"customerKey": "ckOi1Ea7j39ItpKQTDWVCQ==", | |
"customerSecret": "kxp5VT6TuKqae5tubdXgmr5ARkqnZY6GPuD23u0Ilk0=" | |
}; | |
} |
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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title>Simple Web Video Conference Application</title> | |
<script type="text/javascript" src="https://unpkg.com/@voxeet/voxeet-web-sdk@latest/dist/voxeet-sdk.js"> | |
</script> | |
<script type="text/javascript" src="ui.js"></script> | |
<meta http-equiv="Content-Security-Policy" | |
content="default-src * self blob: data: gap:; style-src * self 'unsafe-inline' blob: data: gap:; script-src * 'self' 'unsafe-eval' 'unsafe-inline' blob: data: gap:; object-src * 'self' blob: data: gap:; img-src * self 'unsafe-inline' blob: data: gap:; connect-src self * 'unsafe-inline' blob: data: gap:; frame-src * self blob: data: gap:;"> | |
<!-- CSS only --> | |
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" | |
integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous"> | |
<!-- JavaScript Bundle with Popper --> | |
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" | |
integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"> | |
</script> | |
<script src="https://kit.fontawesome.com/d8d772b4af.js" crossorigin="anonymous"></script> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.5.0/font/bootstrap-icons.min.css" | |
rel="stylesheet" crossorigin="anonymous"> | |
<link href="./styles.css" rel="stylesheet"> | |
</head> | |
<body style="background-color: white;"> | |
<div id="video-container" class="videos-container"> | |
</div> | |
<div id="clip-container" class="clip-container1"></div> | |
<div id="screenshare-container" class="screenshare-container"></div> | |
<div class="main-controls-container"> | |
<div class="message-item"> | |
<div class="mb-3"> | |
<div for="alias-input" class="form-text">Conference alias:</div> | |
<input id="alias-input" value="dev-portal" class="form-control-lg" aria-describedby="aliasHelp" /> | |
<div id="aliasHelp" class="form-text">Enter a unique conference name</div> | |
</div> | |
<div class="btn-group"> | |
<button id="join-btn" class="btn btn-primary" disabled>Join</button> | |
<button id="leave-btn" class="btn btn-primary" disabled>Leave</button> | |
</div> | |
<div class="mb-3"> | |
<div id="name-message" class="form-text">You are logged out.</div> | |
<div id="record-status" class="form-text" style="color: red;">Not Recording</div> | |
<div id="label-dolby-voice" class="form-text">Dolby Voice Off</div> | |
</div> | |
<div class="btn-group-sm"> | |
<button type="button" class="btn btn-primary" id="start-video-btn" disabled> Start | |
Video</button> | |
<button type="button" class="btn btn-primary" id="stop-video-btn" disabled> Stop | |
Video</button> | |
<button type="button" class="btn btn-primary" id="start-audio-btn" disabled> Start | |
Audio</button> | |
<button type="button" class="btn btn-primary" id="stop-audio-btn" disabled> Stop | |
Audio</button> | |
</div> | |
<div class="btn-group-sm"> | |
<button type="button" class="btn btn-warning" id="start-screenshare-btn" disabled> Start | |
Screenshare</button> | |
<button type="button" class="btn btn-warning" id="stop-screenshare-btn" disabled> Stop | |
Screenshare</button> | |
</div> | |
<div class="btn-group-sm"> | |
<button type="button" class="btn btn-danger" id="start-recording-btn" disabled> Start | |
Recording</button> | |
<button type="button" class="btn btn-danger" id="stop-recording-btn" disabled> Stop | |
Recording</button> | |
</div> | |
<span id="clip-player-controls" style="visibility:hidden;"> | |
<label for="clip-url-input" class="form-text">Enter a video URL</label> | |
<input id="clip-url-input" | |
value="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" | |
class="form-control" aria-describedby="clipURLHelp" /> | |
<div class="btn-group-sm p-3"> | |
<button type="button" class="btn btn-success" id="start-clip-btn" disabled> Begin</button> | |
<button type="button" class="btn btn-success" id="play-clip-btn" disabled> Play </button> | |
<button type="button" class="btn btn-success" id="pause-clip-btn" disabled> Pause </button> | |
<button type="button" class="btn btn-success" id="stop-clip-btn" disabled> End</button> | |
</div> | |
</span> | |
<h3 class="form-text">Participants</h3> | |
<ul id="participants-list" class="list-group"> | |
</ul> | |
</div> | |
</div> | |
<script type="text/javascript" src="client.js"></script> | |
<script type="text/javascript" src="video-controller.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
// Modules to control application life and create native browser window | |
const { app, BrowserWindow } = require('electron') | |
const path = require('path') | |
function createWindow() { | |
// Create the browser window. | |
const mainWindow = new BrowserWindow({ | |
width: 1920, | |
height: 1080, | |
webPreferences: { | |
nodeIntegration: true, | |
preload: path.join(__dirname, 'preload.js'), | |
}, | |
}) | |
// and load the index.html of the app. | |
mainWindow.loadFile('index.html') | |
// Open the DevTools. | |
// mainWindow.webContents.openDevTools() | |
} | |
// This method will be called when Electron has finished | |
// initialization and is ready to create browser windows. | |
// Some APIs can only be used after this event occurs. | |
app.whenReady().then(() => { | |
createWindow() | |
app.on('activate', function () { | |
// On macOS it's common to re-create a window in the app when the | |
// dock icon is clicked and there are no other windows open. | |
if (BrowserWindow.getAllWindows().length === 0) createWindow() | |
}) | |
}) | |
// Quit when all windows are closed, except on macOS. There, it's common | |
// for applications and their menu bar to stay active until the user quits | |
// explicitly with Cmd + Q. | |
app.on('window-all-closed', function () { | |
if (process.platform !== 'darwin') app.quit() | |
}) | |
// In this file you can include the rest of your app's specific main process | |
// code. You can also put them in separate files and require them here. |
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
{ | |
"name": "Watch Party v3", | |
"productName": "Watch Party v3", | |
"description": "My Electron application description", | |
"keywords": [], | |
"main": "./main.js", | |
"version": "1.0.0", | |
"author": "dzeit", | |
"scripts": { | |
"start": "electron ." | |
}, | |
"dependencies": {}, | |
"devDependencies": { | |
"electron": "13.1.7" | |
} | |
} |
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
// All of the Node.js APIs are available in the preload process. | |
// It has the same sandbox as a Chrome extension. | |
window.addEventListener('DOMContentLoaded', () => { | |
const replaceText = (selector, text) => { | |
const element = document.getElementById(selector) | |
if (element) element.innerText = text | |
} | |
for (const type of ['chrome', 'node', 'electron']) { | |
replaceText(`${type}-version`, process.versions[type]) | |
} | |
}) |
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
// This file is required by the index.html file and will | |
// be executed in the renderer process for that window. | |
// No Node.js APIs are available in this process because | |
// `nodeIntegration` is turned off. Use `preload.js` to | |
// selectively enable features needed in the rendering | |
// process. | |
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
/* app Styles */ | |
.flipped-video { | |
transform: rotateY(180deg); | |
-webkit-transform: rotateY(180deg); /* Safari and Chrome */ | |
-moz-transform: rotateY(180deg); /* Firefox */ | |
} | |
.videos-container { | |
display: flex; | |
flex-wrap: wrap; | |
flex-direction: row; | |
justify-content: flex-start; | |
align-items:flex-start; | |
position:absolute; | |
left:30px; | |
top: 30px; | |
bottom:30px; | |
right: 420px; | |
background-color: ghostwhite; | |
padding: 20px; | |
} | |
.screenshare-container { | |
display: flex; | |
flex-wrap: wrap; | |
justify-content: flex-end; | |
align-items:flex-end; | |
position:absolute; | |
left:30px; | |
top: 30px; | |
bottom:30px; | |
right: 420px; | |
} | |
.screenshare { | |
z-index: 25; | |
max-width: 60%; | |
max-height: 60%; | |
margin: 10px; | |
border: 3px solid #333; | |
background-color: ghostwhite; | |
} | |
.video-item { | |
max-width: 240px; | |
max-height: 240px; | |
/* width: 320px; | |
height: 240px; */ | |
margin: 10px; | |
border: 2px solid #333; | |
box-shadow: 12px 12px 2px 1px rgba(152, 152, 154, 0.2); | |
} | |
.button-style { | |
border-radius: 1em; | |
margin: 0 0.3em 0.3em 0; | |
min-width: 80px; | |
} | |
.button-style-old { | |
display: inline-block; | |
padding: 0.3em 1.2em; | |
margin: 0 0.3em 0.3em 0; | |
border-radius: 2em; | |
box-sizing: border-box; | |
text-decoration: none; | |
font-family: 'Roboto', sans-serif; | |
font-weight: 300; | |
color: #FFFFFF; | |
background-color: #4eb5f1; | |
text-align: center; | |
transition: all 0.2s; | |
min-width: 150px; | |
} | |
.main-controls-container { | |
display: flex; | |
/* flex-wrap:wrap; */ | |
flex-direction: column; | |
justify-content:right; | |
align-items:center; | |
position:absolute; | |
right:30px; | |
top: 30px; | |
width: 400px; | |
padding: 5px; | |
} | |
.list-item{ | |
text-decoration: none; | |
} | |
.button-controls-container { | |
} | |
.message-item { | |
display: inline-block; | |
padding: 0.5em; | |
margin: 0.3em 0.3em 0.3em 0; | |
box-sizing: border-box; | |
text-decoration: none; | |
font-family: 'Roboto', sans-serif; | |
font-weight: 300; | |
color: #FFFFFF; | |
background-color: #b5c6d0; | |
text-align: center; | |
transition: all 0.2s; | |
width: 100%; | |
height: 100%; | |
border-radius: 1em; | |
} | |
.flexbox-item { | |
/* width: 200px; */ | |
margin: 10px; | |
border: 1px solid #eee; | |
background-color: #dfdfdf; | |
} | |
.flexbox-item-1 { | |
width: 200px; | |
margin: 10px; | |
border: 1px solid #eee; | |
background-color: #dfdfdf; | |
min-height: 44px; | |
} | |
.controls { | |
width: 100%; | |
border: 1px solid #eee; | |
background-color: #dfdfdf; | |
} | |
html { | |
/* font-size: 10px; | |
background-color: #ffc600; */ | |
} | |
body { | |
background: #eee; | |
font-family: 'helvetica neue'; | |
font-weight: 200; | |
font-size: 20px; | |
} | |
p { | |
/* margin: 0 0 3rem; */ | |
} | |
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
const initUI = () => { | |
const nameMessage = document.getElementById('name-message'); | |
const conferenceAliasInput = document.getElementById('alias-input'); | |
const joinButton = document.getElementById('join-btn'); | |
const leaveButton = document.getElementById('leave-btn'); | |
const lblDolbyVoice = document.getElementById('label-dolby-voice'); | |
const startVideoBtn = document.getElementById('start-video-btn'); | |
const stopVideoBtn = document.getElementById('stop-video-btn'); | |
const startAudioBtn = document.getElementById('start-audio-btn'); | |
const stopAudioBtn = document.getElementById('stop-audio-btn'); | |
const startScreenShareBtn = document.getElementById('start-screenshare-btn'); | |
const stopScreenShareBtn = document.getElementById('stop-screenshare-btn'); | |
const startRecordingBtn = document.getElementById('start-recording-btn'); | |
const stopRecordingBtn = document.getElementById('stop-recording-btn'); | |
// Workshop Part three hide html elements / uncomment // showPlayer.style = ""; to show clip video controller | |
const showPlayer = document.getElementById('clip-player-controls'); | |
showPlayer.style = ""; | |
// Update the login message with the name of the user | |
nameMessage.innerHTML = `You are logged in as ${randomName}`; | |
// nameInput.value = randomName; | |
joinButton.disabled = false; | |
joinButton.onclick = () => { | |
// Default conference parameters | |
// See: https://docs.dolby.io/interactivity/docs/js-client-sdk-model-conferenceparameters | |
let conferenceParams = { | |
liveRecording: true, | |
rtcpMode: "average", // worst, average, max | |
ttl: 0, | |
videoCodec: "H264", // H264, VP8 | |
dolbyVoice: true | |
}; | |
// See: https://docs.dolby.io/interactivity/docs/js-client-sdk-model-conferenceoptions | |
let conferenceOptions = { | |
alias: conferenceAliasInput.value, | |
params: conferenceParams, | |
}; | |
// 1. Create a conference room with an alias | |
VoxeetSDK.conference.create(conferenceOptions) | |
.then((conference) => { | |
// See: https://docs.dolby.io/interactivity/docs/js-client-sdk-model-joinoptions | |
const joinOptions = { | |
constraints: { | |
audio: false, | |
video: true | |
}, | |
simulcast: false | |
}; | |
// 2. Join the conference | |
VoxeetSDK.conference.join(conference, joinOptions) | |
.then((conf) => { | |
lblDolbyVoice.innerHTML = `Dolby Voice is ${conf.params.dolbyVoice ? 'On' : 'Off'}.`; | |
conferenceAliasInput.disabled = true; | |
joinButton.disabled = true; | |
leaveButton.disabled = false; | |
startVideoBtn.disabled = true; | |
stopVideoBtn.disabled = false; | |
startAudioBtn.disabled = false; | |
stopAudioBtn.disabled = true; | |
startScreenShareBtn.disabled = false; | |
startRecordingBtn.disabled = false; | |
startClipBtn.disabled = false; | |
playClipBtn.disabled = true; | |
pauseClipBtn.disabled = true; | |
stopClipBtn.disabled = true; | |
}) | |
.catch((e) => console.log(e)); | |
}) | |
.catch((e) => console.log(e)); | |
}; | |
leaveButton.onclick = () => { | |
// Leave the conference | |
VoxeetSDK.conference.leave() | |
.then(() => { | |
lblDolbyVoice.innerHTML = ''; | |
conferenceAliasInput.disabled = false; | |
joinButton.disabled = false; | |
leaveButton.disabled = true; | |
startVideoBtn.disabled = true; | |
stopVideoBtn.disabled = true; | |
startAudioBtn.disabled = true; | |
stopAudioBtn.disabled = true; | |
startScreenShareBtn.disabled = true; | |
stopScreenShareBtn.disabled = true; | |
startRecordingBtn.disabled = true; | |
stopRecordingBtn.disabled = true; | |
startClipBtn.disabled = true; | |
playClipBtn.disabled = true; | |
pauseClipBtn.disabled = true; | |
stopClipBtn.disabled = true; | |
}) | |
.catch((e) => console.log(e)); | |
}; | |
startVideoBtn.onclick = () => { | |
// Start sharing the video with the other participants | |
VoxeetSDK.conference.startVideo(VoxeetSDK.session.participant) | |
.then(() => { | |
startVideoBtn.disabled = true; | |
stopVideoBtn.disabled = false; | |
}) | |
.catch((e) => console.log(e)); | |
}; | |
stopVideoBtn.onclick = () => { | |
// Stop sharing the video with the other participants | |
VoxeetSDK.conference.stopVideo(VoxeetSDK.session.participant) | |
.then(() => { | |
stopVideoBtn.disabled = true; | |
startVideoBtn.disabled = false; | |
}) | |
.catch((e) => console.log(e)); | |
}; | |
startAudioBtn.onclick = () => { | |
// Start sharing the Audio with the other participants | |
VoxeetSDK.conference.startAudio(VoxeetSDK.session.participant) | |
.then(() => { | |
startAudioBtn.disabled = true; | |
stopAudioBtn.disabled = false; | |
}) | |
.catch((e) => console.log(e)); | |
}; | |
stopAudioBtn.onclick = () => { | |
// Stop sharing the Audio with the other participants | |
VoxeetSDK.conference.stopAudio(VoxeetSDK.session.participant) | |
.then(() => { | |
stopAudioBtn.disabled = true; | |
startAudioBtn.disabled = false; | |
}) | |
.catch((e) => console.log(e)); | |
}; | |
startScreenShareBtn.onclick = () => { | |
// Start the Screen Sharing with the other participants | |
VoxeetSDK.conference.startScreenShare() | |
.then(() => { | |
startScreenShareBtn.disabled = true; | |
stopScreenShareBtn.disabled = false; | |
}) | |
.catch((e) => console.log(e)); | |
}; | |
stopScreenShareBtn.onclick = () => { | |
// Stop the Screen Sharing | |
VoxeetSDK.conference.stopScreenShare() | |
.catch((e) => console.log(e)); | |
}; | |
startRecordingBtn.onclick = () => { | |
let recordStatus = document.getElementById('record-status'); | |
// Start recording the conference | |
VoxeetSDK.recording.start() | |
.then(() => { | |
recordStatus.innerText = 'Recording...'; | |
startRecordingBtn.disabled = true; | |
stopRecordingBtn.disabled = false; | |
}) | |
.catch((e) => console.log(e)); | |
}; | |
stopRecordingBtn.onclick = () => { | |
let recordStatus = document.getElementById('record-status'); | |
// Stop recording the conference | |
VoxeetSDK.recording.stop() | |
.then(() => { | |
recordStatus.innerText = ''; | |
startRecordingBtn.disabled = false; | |
stopRecordingBtn.disabled = true; | |
}) | |
.catch((e) => console.log(e)); | |
}; | |
// watch party | |
// import { initVideoController } from './video-controller.mjs'; | |
// initVideoController(); // bring in whole script | |
}; // init | |
// Add a video stream to the web page | |
const addVideoNode = (participant, stream) => { | |
let videoNode = document.getElementById('video-' + participant.id); | |
if (!videoNode) { | |
videoNode = document.createElement('video'); | |
// add css class to mirror current user's video | |
if (participant.id === VoxeetSDK.session.participant.id) { | |
videoNode.setAttribute('class', 'video-item flipped-video'); | |
} else { | |
videoNode.setAttribute('class', 'video-item'); | |
} | |
videoNode.setAttribute('id', 'video-' + participant.id); | |
videoNode.setAttribute("playsinline", true); | |
videoNode.muted = true; | |
// videoNode.setAttribute("autoplay", 'autoplay'); | |
videoNode.autoplay = true; | |
// videoNode.controls = true; | |
const videoContainer = document.getElementById('video-container'); | |
videoContainer.appendChild(videoNode); | |
} | |
navigator.attachMediaStream(videoNode, stream); | |
}; | |
// Remove the video streem from the web page | |
const removeVideoNode = (participant) => { | |
let videoNode = document.getElementById('video-' + participant.id); | |
if (videoNode) { | |
videoNode.parentNode.removeChild(videoNode); | |
} | |
}; | |
const createParticpantCard = (participant) => { | |
let newCard = `<li class="list-group-item-primary d-flex justify-content-between align-items-center my-list"> | |
${participant.info.name} | |
<img src="${participant.info.avatarUrl}" class="img-fluid rounded-start my-list" alt="${participant.info.name}"> | |
</li>` | |
return newCard; | |
} | |
// Add a new participant to the list | |
const addParticipantNode = (participant) => { | |
// If the participant is the current session user, don't add himself to the list | |
if (participant.id === VoxeetSDK.session.participant.id) return; | |
let participantNode = document.createElement('p'); | |
participantNode.setAttribute('id', 'participant-' + participant.id); | |
participantNode.innerHTML = createParticpantCard(participant); | |
const participantsList = document.getElementById('participants-list'); | |
participantsList.appendChild(participantNode); | |
}; | |
// Remove a participant from the list | |
const removeParticipantNode = (participant) => { | |
let participantNode = document.getElementById('participant-' + participant.id); | |
if (participantNode) { | |
participantNode.parentNode.removeChild(participantNode); | |
} | |
}; | |
// Add a screen share stream to the web page | |
const addScreenShareNode = (stream) => { | |
let screenShareNode = document.getElementById('screenshare'); | |
if (screenShareNode) { | |
return alert('There is already a participant sharing a screen!'); | |
} | |
screenShareNode = document.createElement('video'); | |
screenShareNode.setAttribute('class', 'screenshare'); | |
screenShareNode.setAttribute('id', 'screenshare'); | |
screenShareNode.autoplay = 'autoplay'; | |
screenShareNode.controls = true; // allows PIP and full screen | |
navigator.attachMediaStream(screenShareNode, stream); | |
const screenShareContainer = document.getElementById('screenshare-container'); | |
screenShareContainer.appendChild(screenShareNode); | |
} | |
// Remove the screen share stream from the web page | |
const removeScreenShareNode = () => { | |
let screenShareNode = document.getElementById('screenshare'); | |
if (screenShareNode) { | |
screenShareNode.parentNode.removeChild(screenShareNode); | |
} | |
const startScreenShareBtn = document.getElementById('start-screenshare-btn'); | |
startScreenShareBtn.disabled = false; | |
const stopScreenShareBtn = document.getElementById('stop-screenshare-btn'); | |
stopScreenShareBtn.disabled = true; | |
} |
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
/** | |
* | |
* 1. add script just after client.js in html file | |
* <script type="text/javascript" src="video-controller.js"></script> | |
* | |
* Add ui to html | |
* | |
* <div id="video-clip-controls"> | |
<label for="clip-url-input" class="form-text">Enter a video URL</label> | |
<input id="clip-url-input" | |
value="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" | |
class="form-control" aria-describedby="clipURLHelp" /> | |
<div class="btn-group-sm p-3"> | |
<button type="button" class="btn btn-success" id="start-clip-btn" disabled> Begin</button> | |
<button type="button" class="btn btn-success" id="play-clip-btn" disabled> Play </button> | |
<button type="button" class="btn btn-success" id="pause-clip-btn" disabled> Pause </button> | |
<button type="button" class="btn btn-success" id="stop-clip-btn" disabled> End</button> | |
</div> | |
</div> | |
* | |
* | |
* | |
* | |
*/ | |
// clip video Controller controls | |
const clipURLInput = document.getElementById('clip-url-input'); | |
const startClipBtn = document.getElementById('start-clip-btn'); | |
const playClipBtn = document.getElementById('play-clip-btn'); | |
const pauseClipBtn = document.getElementById('pause-clip-btn'); | |
const stopClipBtn = document.getElementById('stop-clip-btn'); | |
let timestamp = 0; | |
// start video presentation with a url | |
startClipBtn.onclick = () => { | |
videoURL = clipURLInput.value; | |
VoxeetSDK.videoPresentation.start(videoURL) | |
.then(() => { | |
console.log("video started") | |
}) | |
.catch((error) => { | |
console.log(error); | |
}) | |
} | |
//play | |
playClipBtn.onclick = () => { | |
VoxeetSDK.videoPresentation.play() | |
.then(() => { | |
console.log("video play") | |
}) | |
.catch((error) => { | |
console.log(error); | |
}) | |
} | |
// pause | |
pauseClipBtn.onclick = () => { | |
VoxeetSDK.videoPresentation.pause() | |
.then(() => { | |
console.log("video pause") | |
}) | |
.catch((error) => { | |
console.log(error); | |
}) | |
} | |
// stop | |
stopClipBtn.onclick = () => { | |
VoxeetSDK.videoPresentation.stop() | |
.then(() => { | |
console.log("video Stop") | |
}) | |
.catch((error) => { | |
console.log(error); | |
}) | |
} | |
// Video Presentation listeners | |
VoxeetSDK.videoPresentation.on("started", (participant, stream) => { | |
console.log("started", participant, stream) | |
addClipNode(participant, stream) | |
startClipBtn.disabled = true; | |
playClipBtn.disabled = true; | |
pauseClipBtn.disabled = false; | |
stopClipBtn.disabled = false; | |
}); | |
VoxeetSDK.videoPresentation.on("paused", (participant, stream) => { | |
console.log("paused", participant, stream) | |
let videoNode = document.getElementById('video-clip'); | |
timestamp = Math.round(videoNode.currentTime * 1000); | |
// videoNode.currentTime = timestamp; | |
videoNode.pause() | |
startClipBtn.disabled = true; | |
playClipBtn.disabled = false; | |
pauseClipBtn.disabled = true; | |
stopClipBtn.disabled = false; | |
}); | |
VoxeetSDK.videoPresentation.on("sought", (participant, stream) => { | |
let videoNode = document.getElementById('video-clip'); | |
timestamp = Math.round(videoNode.currentTime * 1000); | |
// videoNode.currentTime = timestamp; | |
console.log("seek", participant, stream) | |
}); | |
VoxeetSDK.videoPresentation.on("played", (participant, stream) => { | |
console.log("play", participant, stream) | |
let videoNode = document.getElementById('video-clip'); | |
videoNode.play() | |
videoNode.muted = false; | |
startClipBtn.disabled = true; | |
playClipBtn.disabled = true; | |
pauseClipBtn.disabled = false; | |
stopClipBtn.disabled = false; | |
}); | |
VoxeetSDK.videoPresentation.on("stopped", (participant, stream) => { | |
console.log("stopped", participant, stream); | |
let videoNode = document.getElementById('video-clip'); | |
videoNode.pause() | |
resetRemoveVideo() | |
}); | |
var resetRemoveVideo = () => { | |
let videoNode = document.getElementById('video-clip'); | |
if (videoNode) { | |
videoNode.pause() | |
removeClipNode() | |
} | |
startClipBtn.disabled = false; | |
playClipBtn.disabled = true; | |
pauseClipBtn.disabled = true; | |
stopClipBtn.disabled = true; | |
} | |
// Add a Clip stream to the web page | |
const addClipNode = (participant, stream) => { | |
let videoNode = document.getElementById('video-clip'); | |
if (!videoNode) { | |
videoNode = document.createElement('video'); | |
videoNode.setAttribute('class', 'clip-item'); // style lager | |
videoNode.setAttribute('id', 'video-clip'); | |
videoNode.setAttribute('height', 480); | |
videoNode.setAttribute('width', 640); | |
videoNode.setAttribute("playsinline", true); | |
videoNode.muted = false; | |
videoNode.setAttribute("autoplay", 'autoplay'); | |
videoNode.setAttribute("src", participant.url);; | |
const videoContainer = document.getElementById('video-container'); | |
videoContainer.prepend(videoNode) | |
} | |
navigator.attachMediaStream(videoNode, stream); | |
playClipBtn.disabled = false; | |
}; | |
// Remove the Clip stream from the web page | |
const removeClipNode = () => { | |
let clipNode = document.getElementById('video-clip'); | |
if (clipNode) { | |
clipNode.parentNode.removeChild(clipNode); | |
} | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment