parent
7a17c7e3ca
commit
343bdd8c7c
4 changed files with 239 additions and 42 deletions
@ -0,0 +1,176 @@ |
|||||||
|
/// @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 |
||||||
|
}; |
||||||
|
///@}
|
||||||
|
|
||||||
|
/// @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
|
||||||
|
/** @return Stream prepared to be used in a HTML @c src attribute |
||||||
|
within a @c audio or @c video tag. */ |
||||||
|
this.preview = function() { |
||||||
|
return window.URL ? window.URL.createObjectURL(stream) : stream; |
||||||
|
} |
||||||
|
|
||||||
|
/// Get Stream to the Recording
|
||||||
|
/** @return Stream prepared 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() { |
||||||
|
var buff = new Blob(recordedBlobs, {type: 'video/webm'}); |
||||||
|
return window.URL ? window.URL.createObjectURL(buff) : buff; |
||||||
|
} |
||||||
|
|
||||||
|
/// 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; |
||||||
|
}); |
||||||
|
///@}
|
||||||
|
} |
Loading…
Reference in new issue