Skip to content

Commit c579c77

Browse files
JSDK-1280 Local video snapshot example. (#7)
1 parent f61bc7a commit c579c77

File tree

10 files changed

+341
-5
lines changed

10 files changed

+341
-5
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ jspm_packages
4545
# The generated index.js
4646
quickstart/public/index.js
4747
examples/mediadevices/public/index.js
48+
examples/localvideosnapshot/public/index.js
4849

4950
# The generated helpers.js
5051
examples/mediadevices/public/helpers.js
52+
examples/localvideosnapshot/public/helpers.js

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# 1.0.0
22
=======
33

4+
* Added an example app to demonstrate Local Video Snapshot.
45
* Added an example app to demonstrate Media Device Selection.
56
* Updated twilio-video to 1.0.0
67
* Removed configuration profile

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ video in both the tabs!
4848
The project contains some common use-case examples for the Twilio Video JS SDK.
4949

5050
* [Media Device Selection](http://localhost:3000/mediadevices)
51+
* [Local Video Snapshot](http://localhost:3000/localvideosnapshot)
5152

5253
## License
5354

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
@import url('https://fonts.googleapis.com/css?family=Roboto+Mono:300');
2+
3+
html {
4+
height: 100%;
5+
}
6+
7+
body {
8+
height: 100%;
9+
}
10+
11+
div.container-fluid {
12+
height: 100%;
13+
}
14+
15+
div.row {
16+
height: 100%;
17+
}
18+
19+
div.row.thin-gutters {
20+
margin: 0 2px 0 2px;
21+
}
22+
23+
div.row.thin-gutters > .col,
24+
div.row.thin-gutters > [class*="col-"] {
25+
padding: 0 2px;
26+
}
27+
28+
div.col-sm-8, div.col-sm-4 {
29+
height: 100%;
30+
}
31+
32+
pre.language-javascript {
33+
font-family: 'Roboto Mono', monospace;
34+
font-size: 13px;
35+
}
36+
37+
pre.language-javascript a {
38+
color: aquamarine;
39+
text-decoration: underline;
40+
}
41+
42+
pre.language-javascript a:hover {
43+
text-decoration: none;
44+
}
45+
46+
div.card {
47+
border: none;
48+
overflow-y: auto;
49+
}
50+
51+
div.col-sm-8 > .card {
52+
height: 100%;
53+
}
54+
55+
div.col-sm-4 > .card {
56+
height: 50%;
57+
}
58+
59+
canvas#snapshot,
60+
video#videoinputpreview {
61+
width: 100%;
62+
background-color: lightgrey !important;
63+
background-image: url('https://static0.twilio.com/marketing/bundles/archetype/img/logo-wordmark.svg');
64+
background-position: 50%;
65+
background-repeat: no-repeat;
66+
}
67+
68+
button#takesnapshot {
69+
margin-top: 5px;
70+
}
71+
72+
@media (max-width: 900px) {
73+
div.col-sm-8, div.col-sm-4 {
74+
max-width: 100%;
75+
flex: 100%;
76+
}
77+
78+
div.col-sm-8 {
79+
height: 40%;
80+
}
81+
82+
div.col-sm-4 {
83+
height: 60%;
84+
}
85+
86+
pre.language-javascript {
87+
font-size: 12px;
88+
}
89+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<html lang="en">
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7+
<title>Local Video Snapshot</title>
8+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
9+
<link rel="stylesheet" href="prism.css">
10+
<link rel="stylesheet" href="index.css">
11+
</head>
12+
<body>
13+
<div class="container-fluid">
14+
<div class="row thin-gutters">
15+
<div class="col-sm-8">
16+
<div class="card">
17+
<div class="card-block">
18+
<h4 class="card-title">
19+
Local Video Snapshot
20+
</h4>
21+
<pre class="language-javascript"></pre>
22+
</div>
23+
</div>
24+
</div>
25+
<div class="col-sm-4">
26+
<div class="card">
27+
<div class="card-block">
28+
<h4 class="card-title">Local Video</h4>
29+
<video id="videoinputpreview" autoplay></video>
30+
<button id="takesnapshot" class="btn btn-primary btn-block">Take Snapshot</button>
31+
</div>
32+
</div>
33+
<div class="card">
34+
<div class="card-block">
35+
<h4 class="card-title">Snapshot</h4>
36+
<canvas id="snapshot" width="320" height="240"></canvas>
37+
</div>
38+
</div>
39+
</div>
40+
</div>
41+
</div>
42+
<script src="index.js"></script>
43+
</body>
44+
</html>
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript */
2+
/**
3+
* okaidia theme for JavaScript, CSS and HTML
4+
* Loosely based on Monokai textmate theme by http://www.monokai.nl/
5+
* @author ocodia
6+
*/
7+
8+
code[class*="language-"],
9+
pre[class*="language-"] {
10+
color: #f8f8f2;
11+
background: none;
12+
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
13+
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
14+
text-align: left;
15+
white-space: pre;
16+
word-spacing: normal;
17+
word-break: normal;
18+
word-wrap: normal;
19+
line-height: 1.5;
20+
21+
-moz-tab-size: 4;
22+
-o-tab-size: 4;
23+
tab-size: 4;
24+
25+
-webkit-hyphens: none;
26+
-moz-hyphens: none;
27+
-ms-hyphens: none;
28+
hyphens: none;
29+
}
30+
31+
/* Code blocks */
32+
pre[class*="language-"] {
33+
padding: 1em;
34+
margin: .5em 0;
35+
overflow: auto;
36+
border-radius: 0.3em;
37+
}
38+
39+
:not(pre) > code[class*="language-"],
40+
pre[class*="language-"] {
41+
background: #272822;
42+
}
43+
44+
/* Inline code */
45+
:not(pre) > code[class*="language-"] {
46+
padding: .1em;
47+
border-radius: .3em;
48+
white-space: normal;
49+
}
50+
51+
.token.comment,
52+
.token.prolog,
53+
.token.doctype,
54+
.token.cdata {
55+
color: #87ceeb;
56+
}
57+
58+
.token.operator,
59+
.token.punctuation {
60+
color: #ff5555;
61+
}
62+
63+
.namespace {
64+
opacity: .7;
65+
}
66+
67+
.token.property,
68+
.token.tag,
69+
.token.constant,
70+
.token.symbol,
71+
.token.deleted {
72+
color: #f92672;
73+
}
74+
75+
.token.boolean {
76+
color: #55ff55;
77+
}
78+
79+
.token.number {
80+
color: #cd5c5c;
81+
}
82+
83+
.token.selector,
84+
.token.attr-name,
85+
.token.string,
86+
.token.char,
87+
.token.builtin,
88+
.token.inserted {
89+
color: #ff55ff;
90+
}
91+
92+
.token.entity,
93+
.token.url,
94+
.language-css .token.string,
95+
.style .token.string,
96+
.token.variable {
97+
color: #ff55ff;
98+
}
99+
100+
.token.function {
101+
color: #ccc;
102+
}
103+
104+
.token.keyword {
105+
color: #55ff55;
106+
}
107+
108+
.token.regex,
109+
.token.important {
110+
color: #fd971f;
111+
}
112+
113+
.token.important,
114+
.token.bold {
115+
font-weight: bold;
116+
}
117+
.token.italic {
118+
font-style: italic;
119+
}
120+
121+
.token.entity {
122+
cursor: help;
123+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
3+
var Video = require('twilio-video');
4+
5+
/**
6+
* Display local video in the given HTMLVideoElement.
7+
* @param {HTMLVideoElement} video
8+
* @returns {Promise<void>}
9+
*/
10+
function displayLocalVideo(video) {
11+
return Video.createLocalVideoTrack().then(function(localTrack) {
12+
localTrack.attach(video);
13+
});
14+
}
15+
16+
/**
17+
* Take snapshot of the local video from the HTMLVideoElement and render it
18+
* in the HTMLCanvasElement.
19+
* @param {HTMLVideoElement} video
20+
* @param {HTMLCanvasElement} canvas
21+
*/
22+
function takeLocalVideoSnapshot(video, canvas) {
23+
var context = canvas.getContext('2d');
24+
context.clearRect(0, 0, canvas.width, canvas.height);
25+
context.drawImage(video, 0, 0, canvas.width, canvas.height);
26+
}
27+
28+
module.exports.displayLocalVideo = displayLocalVideo;
29+
module.exports.takeLocalVideoSnapshot = takeLocalVideoSnapshot;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
3+
var Prism = require('prismjs');
4+
var getSnippet = require('../../util/getsnippet');
5+
var helpers = require('./helpers');
6+
var displayLocalVideo = helpers.displayLocalVideo;
7+
var takeLocalVideoSnapshot = helpers.takeLocalVideoSnapshot;
8+
9+
var canvas = document.querySelector('canvas#snapshot');
10+
var takeSnapshot = document.querySelector('button#takesnapshot');
11+
var video = document.querySelector('video#videoinputpreview');
12+
13+
// Set the canvas size to the video size.
14+
function setCanvasSizeToVideo(canvas, video) {
15+
canvas.style.height = video.clientHeight + 'px';
16+
}
17+
18+
// Load the code snippet.
19+
getSnippet('./helpers.js').then(function(snippet) {
20+
var pre = document.querySelector('pre.language-javascript');
21+
pre.innerHTML = Prism.highlight(snippet, Prism.languages.javascript);
22+
});
23+
24+
// Request the default LocalVideoTrack and display it.
25+
displayLocalVideo(video).then(function() {
26+
// Display a snapshot of the LocalVideoTrack on the canvas.
27+
takeSnapshot.onclick = function() {
28+
setCanvasSizeToVideo(canvas, video);
29+
takeLocalVideoSnapshot(video, canvas);
30+
};
31+
});
32+
33+
// Resize the canvas to the video size whenever window is resized.
34+
window.onresize = function() {
35+
setCanvasSizeToVideo(canvas, video);
36+
};

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
"scripts": {
77
"clean": "npm run clean:quickstart && npm run clean:examples",
88
"clean:quickstart": "rimraf quickstart/public/index.js",
9-
"clean:examples": "npm run clean:examples:mediadevices",
9+
"clean:examples": "npm run clean:examples:mediadevices && npm run clean:examples:localvideosnapshot",
1010
"clean:examples:mediadevices": "rimraf examples/mediadevices/public/index.js examples/mediadevices/public/helpers.js",
11+
"clean:examples:localvideosnapshot": "rimraf examples/localvideosnapshot/public/index.js examples/localvideosnapshot/public/helpers.js",
1112
"build": "npm run build:quickstart && npm run build:examples",
12-
"build:examples": "npm run build:example:mediadevices",
13-
"build:example:mediadevices": "copyfiles -f examples/mediadevices/src/helpers.js examples/mediadevices/public && browserify examples/mediadevices/src/index.js > examples/mediadevices/public/index.js",
13+
"build:examples": "npm run build:examples:mediadevices && npm run build:examples:localvideosnapshot",
14+
"build:examples:mediadevices": "copyfiles -f examples/mediadevices/src/helpers.js examples/mediadevices/public && browserify examples/mediadevices/src/index.js > examples/mediadevices/public/index.js",
15+
"build:examples:localvideosnapshot": "copyfiles -f examples/localvideosnapshot/src/helpers.js examples/localvideosnapshot/public && browserify examples/localvideosnapshot/src/index.js > examples/localvideosnapshot/public/index.js",
1416
"build:quickstart": "browserify quickstart/src/index.js > quickstart/public/index.js",
1517
"start": "npm run clean && npm run build && node server"
1618
},

server/index.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,19 @@ var randomName = require('./randomname');
1919

2020
// Create Express webapp.
2121
var app = express();
22-
var mediadevicesPath = path.join(__dirname, '../examples/mediadevices/public');
22+
23+
// Set up the paths for the examples.
24+
[
25+
'mediadevices',
26+
'localvideosnapshot'
27+
].forEach(function(example) {
28+
var examplePath = path.join(__dirname, `../examples/${example}/public`);
29+
app.use(`/${example}`, express.static(examplePath));
30+
});
31+
32+
// Set up the path for the quickstart.
2333
var quickstartPath = path.join(__dirname, '../quickstart/public');
2434
app.use('/quickstart', express.static(quickstartPath));
25-
app.use('/mediadevices', express.static(mediadevicesPath));
2635

2736
/**
2837
* Default to the Quick Start application.

0 commit comments

Comments
 (0)