Skip to content

Instantly share code, notes, and snippets.

@damienstanton
Created November 28, 2017 12:44
Show Gist options
  • Save damienstanton/32892f47751a39aeb2f98a763f293a30 to your computer and use it in GitHub Desktop.
Save damienstanton/32892f47751a39aeb2f98a763f293a30 to your computer and use it in GitHub Desktop.
React webcam raw
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { findDOMNode } from 'react-dom';
function hasGetUserMedia() {
return !!(navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia || navigator.msGetUserMedia);
}
export default class Webcam extends Component {
static defaultProps = {
audio: true,
className: '',
height: 480,
muted: false,
onUserMedia: () => {},
screenshotFormat: 'image/webp',
width: 640,
};
static propTypes = {
audio: PropTypes.bool,
muted: PropTypes.bool,
onUserMedia: PropTypes.func,
height: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string,
]),
width: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string,
]),
screenshotFormat: PropTypes.oneOf([
'image/webp',
'image/png',
'image/jpeg',
]),
style: PropTypes.object,
className: PropTypes.string,
audioSource: PropTypes.string,
videoSource: PropTypes.string,
};
static mountedInstances = [];
static userMediaRequested = false;
constructor() {
super();
this.state = {
hasUserMedia: false,
};
}
componentDidMount() {
if (!hasGetUserMedia()) return;
Webcam.mountedInstances.push(this);
if (!this.state.hasUserMedia && !Webcam.userMediaRequested) {
this.requestUserMedia();
}
}
componentWillUnmount() {
const index = Webcam.mountedInstances.indexOf(this);
Webcam.mountedInstances.splice(index, 1);
if (Webcam.mountedInstances.length === 0 && this.state.hasUserMedia) {
if (this.stream.stop) {
this.stream.stop();
} else {
if (this.stream.getVideoTracks) {
this.stream.getVideoTracks().map(track => track.stop());
}
if (this.stream.getAudioTracks) {
this.stream.getAudioTracks().map(track => track.stop());
}
}
Webcam.userMediaRequested = false;
window.URL.revokeObjectURL(this.state.src);
}
}
getScreenshot() {
if (!this.state.hasUserMedia) return null;
const canvas = this.getCanvas();
return canvas && canvas.toDataURL(this.props.screenshotFormat);
}
getCanvas() {
const video = findDOMNode(this);
if (!this.state.hasUserMedia || !video.videoHeight) return null;
if (!this.ctx) {
const canvas = document.createElement('canvas');
const aspectRatio = video.videoWidth / video.videoHeight;
canvas.width = video.clientWidth;
canvas.height = video.clientWidth / aspectRatio;
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
}
const { ctx, canvas } = this;
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
return canvas;
}
requestUserMedia() {
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
const sourceSelected = (audioSource, videoSource) => {
const constraints = {
video: {
optional: [{ sourceId: videoSource }],
},
};
if (this.props.audio) {
constraints.audio = {
optional: [{ sourceId: audioSource }],
};
}
navigator.getUserMedia(constraints, (stream) => {
Webcam.mountedInstances.forEach(instance => instance.handleUserMedia(null, stream));
}, (e) => {
Webcam.mountedInstances.forEach(instance => instance.handleUserMedia(e));
});
};
if (this.props.audioSource && this.props.videoSource) {
sourceSelected(this.props.audioSource, this.props.videoSource);
} else if ('mediaDevices' in navigator) {
navigator.mediaDevices.enumerateDevices().then((devices) => {
let audioSource = null;
let videoSource = null;
devices.forEach((device) => {
if (device.kind === 'audio') {
audioSource = device.id;
} else if (device.kind === 'video') {
videoSource = device.id;
}
});
sourceSelected(audioSource, videoSource);
})
.catch((error) => {
console.log(`${error.name}: ${error.message}`); // eslint-disable-line no-console
});
} else {
MediaStreamTrack.getSources((sources) => {
let audioSource = null;
let videoSource = null;
sources.forEach((source) => {
if (source.kind === 'audio') {
audioSource = source.id;
} else if (source.kind === 'video') {
videoSource = source.id;
}
});
sourceSelected(audioSource, videoSource);
});
}
Webcam.userMediaRequested = true;
}
handleUserMedia(error, stream) {
if (error) {
this.setState({
hasUserMedia: false,
});
return;
}
try {
const src = window.URL.createObjectURL(stream);
this.stream = stream;
this.setState({
hasUserMedia: true,
src,
});
this.props.onUserMedia();
} catch(error) {
this.stream = stream;
this.video.srcObject = stream;
this.setState({
hasUserMedia: true
});
}
}
render() {
return (
<video
autoPlay
width={this.props.width}
height={this.props.height}
src={this.state.src}
muted={this.props.muted}
className={this.props.className}
style={this.props.style}
ref={ref => this.video = ref}
/>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment