2016-01-13 14:41:46 +00:00
|
|
|
/// @class MediaRecorder
|
|
|
|
/** Provides a recorder to record fotos, audio and video from a web
|
|
|
|
browser client using the JavaScript getUserMedia feature.
|
|
|
|
|
|
|
|
@param constraints The constraints as JSON data:
|
|
|
|
@code
|
|
|
|
{
|
|
|
|
video: true, // true if you want to record video
|
|
|
|
audio: true // true if you want to record audio
|
|
|
|
}
|
|
|
|
@endcode
|
|
|
|
You can record video or audio only or both together.
|
|
|
|
if not specified, defaults to @ref defaultconstraints.
|
|
|
|
|
|
|
|
@note Special thanks to the following projects:
|
|
|
|
- For the basics regarding getUserMedia:
|
|
|
|
http://www.html5rocks.com/en/tutorials/getusermedia/intro/
|
|
|
|
- For a simple example of a media recorder:
|
|
|
|
https://github.com/samdutton/simpl/blob/gh-pages/mediarecorder
|
|
|
|
|
|
|
|
@note Supported browsers are:
|
|
|
|
- Firefox 29 or later
|
|
|
|
- Chrome 47 or later, with @c "Enable experimental Web Platform
|
|
|
|
features" enabled from @c "chrome://flags"
|
|
|
|
|
|
|
|
@note This class must be used from within a secure context. A
|
|
|
|
secure context is an encrypted SSL connection through HTTPS, or
|
|
|
|
the special address @c localhost.
|
|
|
|
|
|
|
|
@todo Why are mediaSource and sourceBuffer needed? They are not
|
|
|
|
further referenced.
|
|
|
|
*/
|
|
|
|
function MediaStreamRecorder(constraints) {
|
|
|
|
|
|
|
|
/// @name private variables
|
|
|
|
///@{
|
|
|
|
var events = [];
|
|
|
|
var mediaSource = new MediaSource();
|
|
|
|
var stream;
|
|
|
|
var mediaRecorder;
|
|
|
|
var recordedBlobs = [];
|
|
|
|
var sourceBuffer;
|
|
|
|
var defaultconstraints = {
|
|
|
|
audio: true,
|
|
|
|
video: true
|
|
|
|
};
|
|
|
|
///@}
|
|
|
|
|
2016-01-13 15:54:50 +00:00
|
|
|
/// @name private methods
|
|
|
|
///@{
|
|
|
|
function createURL(data) {
|
|
|
|
var urlCreator = window.URL || window.webkitURL;
|
|
|
|
return urlCreator ? urlCreator.createObjectURL(data) : data;
|
|
|
|
}
|
|
|
|
///@}
|
|
|
|
|
2016-01-13 14:41:46 +00:00
|
|
|
/// @name internal event handlers
|
|
|
|
///@{
|
|
|
|
|
|
|
|
/// Create Source Buffer on Event Source Open
|
|
|
|
function handleSourceOpen(event) {
|
|
|
|
sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8"');
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Store Data in local Binary Large Object
|
|
|
|
function handleDataAvailable(event) {
|
|
|
|
if (event.data && event.data.size > 0) {
|
|
|
|
recordedBlobs.push(event.data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Propagate @c ready Event to Registered Handler
|
|
|
|
function handleReady() {
|
|
|
|
if (events['ready']) events['ready']();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Propagate @c start Event to Registered Handler
|
|
|
|
function handleStart() {
|
|
|
|
if (events['stop']) events['start']();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Propagate @c stop Event to Registered Handler
|
|
|
|
function handleStop(event) {
|
|
|
|
if (events['stop']) events['stop'](event);
|
|
|
|
}
|
|
|
|
|
|
|
|
///@}
|
|
|
|
|
|
|
|
/// @name public methods
|
|
|
|
///@{
|
|
|
|
|
|
|
|
/// Register Eventhandler
|
|
|
|
/** @param eventname The following events are available:
|
|
|
|
- @c ready Video preview stream is ready for use
|
|
|
|
- @c stop Recording has stopped, parameter: @event
|
|
|
|
- @c start Recording has started
|
|
|
|
|
|
|
|
@param eventhandler callback function to be called when the
|
|
|
|
event occurs.
|
|
|
|
*/
|
|
|
|
this.on = function(eventname, eventhandler) {
|
|
|
|
events[eventname] = eventhandler;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get Stream to the Preview
|
2016-01-13 15:54:50 +00:00
|
|
|
/** @return Data URL prepared to be used in a HTML @c src
|
|
|
|
attribute within a @c audio or @c video tag. */
|
2016-01-13 14:41:46 +00:00
|
|
|
this.preview = function() {
|
2016-01-13 15:54:50 +00:00
|
|
|
return createURL(stream);
|
2016-01-13 14:41:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get Stream to the Recording
|
2016-01-13 15:54:50 +00:00
|
|
|
/** @param callback Callback function that will be called with a
|
|
|
|
data url to be used in a HTML @c src attribute within a
|
|
|
|
@c audio or @c video tag, or to be used in a HTML @c
|
|
|
|
href attribute in a @c a tag for downloading the
|
|
|
|
recording. */
|
|
|
|
this.recording = function(callback) {
|
|
|
|
var reader = new FileReader();
|
|
|
|
reader.onload = function(e) {
|
|
|
|
callback(e.target.result);
|
|
|
|
}
|
|
|
|
reader.readAsDataURL(new Blob(recordedBlobs, {type: 'video/webm'}));
|
2016-01-13 14:41:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Start Stream Recording
|
|
|
|
/** @throws Exception if browser is not supported */
|
|
|
|
this.start = function() {
|
|
|
|
// The nested try blocks will be simplified when Chrome 47 moves to Stable
|
|
|
|
var options = {mimeType: 'video/webm'};
|
|
|
|
recordedBlobs = [];
|
|
|
|
try {
|
|
|
|
mediaRecorder = new MediaRecorder(stream, options);
|
|
|
|
} catch (e0) {
|
|
|
|
try {
|
|
|
|
options = {mimeType: 'video/webm,codecs=vp9'};
|
|
|
|
mediaRecorder = new MediaRecorder(stream, options);
|
|
|
|
} catch (e1) {
|
|
|
|
try {
|
|
|
|
options = 'video/vp8'; // Chrome 47
|
|
|
|
mediaRecorder = new MediaRecorder(stream, options);
|
|
|
|
} catch (e2) {
|
|
|
|
throw {
|
|
|
|
text: 'MediaRecorder is not supported by browser',
|
|
|
|
e0: e0,
|
|
|
|
e1: e1,
|
|
|
|
e2: e2
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mediaRecorder.onstop = handleStop;
|
|
|
|
mediaRecorder.ondataavailable = handleDataAvailable;
|
|
|
|
mediaRecorder.start(10); // collect 10ms of data
|
|
|
|
handleStart();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Stop Stream Recording
|
|
|
|
/** Use recording() to get access to the result. */
|
|
|
|
this.stop = function () {
|
|
|
|
if (mediaRecorder) mediaRecorder.stop();
|
|
|
|
delete mediaRecorder; mediaRecorder = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Close Preview Stream
|
|
|
|
/** Closes the stream and releases the camera. This should always
|
|
|
|
be called to cleanup, when the camera is no more needed. */
|
|
|
|
this.release = function() {
|
|
|
|
stop();
|
|
|
|
stream.getTracks().forEach(function(track) {track.stop()});
|
|
|
|
delete stream; stream = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
///@}
|
|
|
|
|
|
|
|
/// @name initialization
|
|
|
|
///@{
|
|
|
|
mediaSource.addEventListener('sourceopen', handleSourceOpen, false);
|
|
|
|
if (!constraints) constraints = defaultconstraints;
|
|
|
|
navigator.mediaDevices.getUserMedia(constraints)
|
|
|
|
.then(function(s) {
|
|
|
|
stream = s;
|
|
|
|
handleReady();
|
|
|
|
}).catch(function(error) {
|
|
|
|
throw error;
|
|
|
|
});
|
|
|
|
///@}
|
|
|
|
}
|