mighty-webcamjs
Version:
HTML5 Webcam Image Capture Library with Flash Fallback
217 lines (182 loc) • 6.09 kB
JavaScript
/* global MediaRecorder, Blob, FileReader */
let Webcam;
let recordedBlobs;
let mediaRecorder;
let videoMimeType;
let maxVideoLengthTimeout;
const isMobile = global.navigator ? /(mobile)/i.test(global.navigator.userAgent) : false;
const videoFormats = [
'video/webm;codecs=h264',
'video/x-matroska;codecs=h264',
'video/mp4;codecs=h264',
'video/webm;codecs=vp9',
'video/webm;codecs=vp8',
'video/webm;codecs=daala',
'video/x-matroska;codecs=avc1',
'video/mp4;codecs=avc1',
'video/mp4',
'video/mpeg',
'video/hevc',
''
];
const RECORD_TIMESCALE = 20; // collect 20ms of data in each blob.
const VideoRecorder = {
init(webcamObj) {
Webcam = webcamObj;
return VideoRecorder;
},
checkIfCanRecord() {
// No Webcam.stream.active means camera not started or not available.
// No MediaRecorder means - file fallabck video recording will be used.
return !!(Webcam.stream && Webcam.stream.active) || !global.MediaRecorder;
},
resetWebrtcCamera() {
const stream = this.stream;
if (stream) {
if (stream.getVideoTracks) {
// get video track to call stop on it
const tracks = stream.getVideoTracks();
if (tracks && tracks[0] && tracks[0].stop) tracks[0].stop();
} else if (stream.stop) {
// deprecated, may be removed in future
stream.stop();
}
}
VideoRecorder.stopRecording();
},
stopRecording(dispatch) {
clearTimeout(maxVideoLengthTimeout);
// HACK:
// mediaRecorder can be inactive in Incognito mode Chrome@Windows 7, even while recording.
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
}
Webcam.params.set('videoRecording', false);
if (dispatch && recordedBlobs.length) {
Webcam.dispatch('videoTaken', {
blob: new Blob(recordedBlobs, { type: videoMimeType.split(';')[0] })
});
}
},
startRecording() {
if (!Webcam.video) {
Webcam.dispatch('error', new Webcam.errors.WebcamError('Video is not loaded yet.'));
return;
}
// phones have trouble with high bitrate for high resolutions
let bitRateLimiter = 1;
if (isMobile) {
bitRateLimiter = Math.min(1024 * 1024, Webcam.video.videoWidth * Webcam.video.videoHeight) / (Webcam.video.videoWidth * Webcam.video.videoHeight);
bitRateLimiter = Math.min(1, bitRateLimiter + (bitRateLimiter / 2));
}
recordedBlobs = [];
const autoDetectedBitrate = Webcam.params.get('video_bitrate_per_pixel')
* Webcam.video.videoWidth
* Webcam.video.videoHeight
* bitRateLimiter;
const options = {
videoBitsPerSecond: autoDetectedBitrate
};
for (let i = 0; i < videoFormats.length; i++) {
if (MediaRecorder.isTypeSupported(videoFormats[i])) {
options.mimeType = videoFormats[i];
break;
}
}
videoMimeType = options.mimeType;
try {
mediaRecorder = new MediaRecorder(Webcam.stream, options);
// var maxWidth = 9999;
// // android-version-based optimisation so video is not lagging
// if (parseFloat(getAndroidVersion()) < 6) {
// maxWidth = 1280;
// }
// if (parseFloat(getAndroidVersion()) === 6) {
// maxWidth = 1440;
// }
// if (parseFloat(getAndroidVersion()) >= 7) {
// maxWidth = 1600;
// }
// const longerSide = Math.max( Webcam.video.videoWidth, Webcam.video.videoHeight );
// const shorterSide = Math.min( Webcam.video.videoWidth, Webcam.video.videoHeight );
// if (longerSide > maxWidth) {
// if (longerSide === Webcam.video.videoWidth) {
// mediaRecorder.videoWidth = maxWidth;
// mediaRecorder.videoHeight = shorterSide * (Webcam.video.videoWidth / maxWidth);
// } else {
// mediaRecorder.videoHeight = maxWidth;
// mediaRecorder.videoWidth = shorterSide * (Webcam.video.videoHeight / maxWidth);
// }
// }
} catch (e) {
console.error('Exception while creating MediaRecorder: ' + e); // eslint-disable-line
const alertMsg = 'Exception while creating MediaRecorder: ' // eslint-disable-line
+ e + '. mimeType: ' + options.mimeType;
Webcam.dispatch('error', new Webcam.errors.WebcamError(alertMsg));
return;
}
if (process.env.NODE_ENV !== 'production' && Webcam.params.get('verbose')) {
/* eslint-disable no-console */
console.log('videoBitsPerSecond', options.videoBitsPerSecond);
console.log('detectedMimeType', options.mimeType);
console.log('Created MediaRecorder', mediaRecorder, 'with options', options);
/* eslint-enable */
}
// mediaRecorder.onstop = handleStop;
mediaRecorder.ondataavailable = (event) => {
if (event.data && event.data.size > 0) {
recordedBlobs.push(event.data);
}
};
mediaRecorder.start(RECORD_TIMESCALE); // collect 20ms of data
maxVideoLengthTimeout = setTimeout(() => {
VideoRecorder.stopRecording(true);
}, Webcam.params.get('video_length'));
Webcam.params.set('videoRecording', true);
},
handleFileFallbackVideoInput(e) {
if (!e.target.files || !e.target.files.length) {
return;
}
const rawFile = e.target.files[0];
const rawFileType = rawFile.type;
if (!/^video\//.test(rawFileType)) {
return Webcam.dispatch('error', new Webcam.errors.WebcamError('Invalid file type. Please provide video.'));
}
const reader = new FileReader();
reader.onload = (readerEvent) => {
const blob = new Blob([readerEvent.target.result], { type: rawFileType });
Webcam.dispatch('videoTaken', {
blob: blob,
rawFile: rawFile
});
};
reader.readAsArrayBuffer(rawFile);
},
videoFormatToExt(mimeType) {
const videoFormat = mimeType.substr(6).split(';')[0];
switch (videoFormat) {
case 'x-matroska':
return 'mkv';
case 'quicktime':
return 'mov';
case 'webm':
default:
return videoFormat;
}
},
download(blob, fileName = Date.now()) {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = `${fileName}.${VideoRecorder.videoFormatToExt(blob.type)}`;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
}
};
module.exports = VideoRecorder;