JSFiddle - React, Tailwind, and code Playground
HTML
<!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="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>MediaStream Recording</title>
<link href="//fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a>
<span>MediaRecorder</span></h1>
<p>For more information see the MediaStream Recording API <a
href="http://w3c.github.io/mediacapture-record/MediaRecorder.html"
title="W3C MediaStream Recording API Editor's Draft">Editor's Draft</a>.</p>
<video id="gum" playsinline autoplay muted></video>
<div>
<button id="start">Start camera</button>
<button id="record" disabled>Start Recording</button>
<button id="play" disabled>Play</button>
<button id="download" disabled>Download</button>
</div>
<div>
<span id="errorMsg"></span>
</div>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/record"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<!-- include adapter for srcObject shim -->
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
</body>
</html>
CSS
/*
* 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.
*/
.hidden {
display: none;
}
.highlight {
background-color: #eee;
font-size: 1.2em;
margin: 0 0 30px 0;
padding: 0.2em 1.5em;
}
.warning {
color: red;
font-weight: 400;
}
@media screen and (min-width: 1000px) {
/* hack! to detect non-touch devices */
div#links a {
line-height: 0.8em;
}
}
audio {
max-width: 100%;
}
body {
font-family: 'Roboto', sans-serif;
font-weight: 300;
margin: 0;
padding: 1em;
word-break: break-word;
}
button {
background-color: #d84a38;
border: none;
border-radius: 2px;
color: white;
font-family: 'Roboto', sans-serif;
font-size: 0.8em;
margin: 0 0 1em 0;
padding: 0.5em 0.7em 0.6em 0.7em;
}
button:active {
background-color: #cf402f;
}
button:hover {
background-color: #cf402f;
}
button[disabled] {
color: #ccc;
}
button[disabled]:hover {
background-color: #d84a38;
}
canvas {
background-color: #ccc;
max-width: 100%;
width: 100%;
}
code {
font-family: 'Roboto', sans-serif;
font-weight: 400;
}
div#container {
margin: 0 auto 0 auto;
max-width: 60em;
padding: 1em 1.5em 1.3em 1.5em;
}
div#links {
padding: 0.5em 0 0 0;
}
h1 {
border-bottom: 1px solid #ccc;
font-family: 'Roboto', sans-serif;
font-weight: 500;
margin: 0 0 0.8em 0;
padding: 0 0 0.2em 0;
}
h2 {
color: #444;
font-weight: 500;
}
h3 {
border-top: 1px solid #eee;
color: #666;
font-weight: 500;
margin: 10px 0 10px 0;
white-space: nowrap;
}
li {
margin: 0 0 0.4em 0;
}
html {
/* avoid annoying page width change
when moving from the home page */
overflow-y: scroll;
}
img {
border: none;
max-width: 100%;
}
input[type=radio] {
position: relative;
top: -1px;
}
p {
color: #444;
font-weight: 300;
}
p#data {
border-top: 1px dotted #666;
...
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.
*/
// This code is adapted from
// https://rawgit.com/Miguelao/demos/master/mediarecorder.html
'use strict';
/* globals MediaRecorder */
let mediaRecorder;
let recordedBlobs;
const errorMsgElement = document.querySelector('span#errorMsg');
const gumVideo = document.querySelector('video#gum');
const recordButton = document.querySelector('button#record');
recordButton.addEventListener('click', () => {
if (recordButton.textContent === 'Start Recording') {
startRecording();
} else {
stopRecording();
recordButton.textContent = 'Start Recording';
playButton.disabled = false;
downloadButton.disabled = false;
}
});
const playButton = document.querySelector('button#play');
playButton.addEventListener('click', () => {
const superBuffer = new Blob(recordedBlobs, {type: mediaRecorder.mimeType});
gumVideo.src = null;
gumVideo.srcObject = null;
gumVideo.src = window.URL.createObjectURL(superBuffer);
gumVideo.controls = true;
gumVideo.play();
});
const downloadButton = document.querySelector('button#download');
downloadButton.addEventListener('click', () => {
const blob = new Blob(recordedBlobs, {type: mediaRecorder.mimeType});
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'test.webm';
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
});
function handleDataAvailable(event) {
console.log('handleDataAvailable', event);
if (event.data && event.data.size > 0) {
recordedBlobs.push(event.data);
}
}
function startRecording() {
recordedBlobs = [];
let options = {mimeType: 'video/webm;codecs=vp9,opus'};
if...