video attachments from camera
This commit is contained in:
176
nodejs/public/javascripts/mediarecorder.js
Normal file
176
nodejs/public/javascripts/mediarecorder.js
Normal file
@@ -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;
|
||||||
|
});
|
||||||
|
///@}
|
||||||
|
}
|
@@ -343,67 +343,72 @@ function guessfilename(mimetype, user, date) {
|
|||||||
function attachments(files, id, from, date) {
|
function attachments(files, id, from, date) {
|
||||||
if (files) files.forEach(function(file) {
|
if (files) files.forEach(function(file) {
|
||||||
if (!file.name) file.name = guessfilename(file.type, from, date);
|
if (!file.name) file.name = guessfilename(file.type, from, date);
|
||||||
var img = document.createElement('img');
|
|
||||||
img.title = file.name;
|
|
||||||
if (file.type.match('^image/')) {
|
|
||||||
img.src = file.content;
|
|
||||||
} else {
|
|
||||||
img.src = "images/Document_sans_PICOL-PIctorial-COmmunication-Language.svg";
|
|
||||||
}
|
|
||||||
var a = document.createElement('a');
|
var a = document.createElement('a');
|
||||||
a.href = file.content;
|
a.href = file.content;
|
||||||
a.download = file.name;
|
a.download = file.name;
|
||||||
a.target = '_blank';
|
a.target = '_blank';
|
||||||
|
if (file.type.match('^image/')) {
|
||||||
|
var img = document.createElement('img');
|
||||||
|
img.title = file.name;
|
||||||
|
img.src = file.content;
|
||||||
a.appendChild(img);
|
a.appendChild(img);
|
||||||
|
} else if (file.type.match('^video/')) {
|
||||||
|
var video = document.createElement('video');
|
||||||
|
video.controls = true;
|
||||||
|
video.title = file.name;
|
||||||
|
video.src = file.content;
|
||||||
|
a.appendChild(video);
|
||||||
|
} else {
|
||||||
|
var img = document.createElement('img');
|
||||||
|
img.title = file.name;
|
||||||
|
img.src = "images/Document_sans_PICOL-PIctorial-COmmunication-Language.svg";
|
||||||
|
a.appendChild(img);
|
||||||
|
}
|
||||||
$(id).append(a);
|
$(id).append(a);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// function getUserMedia() {
|
var recorder;
|
||||||
// return navigator.mediaDevices.getUserMedia || navigator.getUserMedia
|
|
||||||
// || navigator.webkitGetUserMedia || navigator.mozGetUserMedia
|
|
||||||
// || navigator.msGetUserMedia;
|
|
||||||
// }
|
|
||||||
|
|
||||||
var mediarecorder = null;
|
|
||||||
var mediastream = null;
|
|
||||||
function record() {
|
|
||||||
mediarecorder = mediastream.record();
|
|
||||||
}
|
|
||||||
function done() {
|
function done() {
|
||||||
mediarecorder.getRecordedData(function(videoblob) {
|
if (recorder) {
|
||||||
previewfile(videoblob, "video/webm");
|
recorder.stop();
|
||||||
});
|
previewfile(recorder.recording(), "video/webm");
|
||||||
mediarecorder = null;
|
abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function abort() {
|
function abort() {
|
||||||
mediarecorder = null;
|
if (recorder) {
|
||||||
mediastream.getTracks().forEach(function(track) {track.stop()});
|
$("#videorecorder").hide();
|
||||||
mediastream = null;
|
recorder.release();
|
||||||
|
delete recorder; recorder = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Record Video from builtin camera
|
/// Record Video from builtin camera
|
||||||
function recordvideo() {
|
function recordvideo() {
|
||||||
$("#videorecorder").show();
|
|
||||||
if (mediastream) {
|
|
||||||
mediarecorder = mediastream.record();
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
navigator.mediaDevices.getUserMedia({audio: true, video: true})
|
abort();
|
||||||
.then(function(stream) {
|
$("#videorecorder").show();
|
||||||
console.log(stream);
|
recorder = new MediaStreamRecorder({
|
||||||
mediastream = stream;
|
video: {
|
||||||
var video = $("#videorecorder video");
|
mandatory: {
|
||||||
video.attr("src", window.URL.createObjectURL(mediastream));
|
maxWidth: 400,
|
||||||
}).catch(function() {
|
maxHeight: 400
|
||||||
error("capture video failed", false);
|
}
|
||||||
|
},
|
||||||
|
audio: true
|
||||||
});
|
});
|
||||||
|
recorder.on("ready", function() {
|
||||||
|
$("#videorecorder video").attr("src", recorder.preview());
|
||||||
|
recorder.start();
|
||||||
|
});
|
||||||
|
recorder.on('')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
error("cannot access camera", true);
|
error("cannot access camera", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function previewfile(content, type, name) {
|
function previewfile(content, type, name) {
|
||||||
@@ -440,6 +445,7 @@ function previewfile(content, type, name) {
|
|||||||
filecontent.push({name: name, type: type, content: content});
|
filecontent.push({name: name, type: type, content: content});
|
||||||
var video = document.createElement("video");
|
var video = document.createElement("video");
|
||||||
video.setAttribute("controls", "controls");
|
video.setAttribute("controls", "controls");
|
||||||
|
video.setAttribute("loop", "loop");
|
||||||
video.setAttribute("src", content);
|
video.setAttribute("src", content);
|
||||||
video.setAttribute("title", name);
|
video.setAttribute("title", name);
|
||||||
$("#preview").append(video);
|
$("#preview").append(video);
|
||||||
|
@@ -255,6 +255,21 @@ label[for=send] img {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#preview img {
|
#msgs .msg .text video {
|
||||||
|
border-radius: 2ex;
|
||||||
|
-moz-border-radius: 2ex;
|
||||||
|
-webkit-border-radius: 2ex;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#videorecorder {
|
||||||
|
max-width: 100%;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview img, #preview video {
|
||||||
height: 4em;
|
height: 4em;
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
<script type="text/javascript" src="javascripts/jquery.js"></script>
|
<script type="text/javascript" src="javascripts/jquery.js"></script>
|
||||||
<script type="text/javascript" src="javascripts/openpgp.js"></script>
|
<script type="text/javascript" src="javascripts/openpgp.js"></script>
|
||||||
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
|
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
|
||||||
|
<script type="text/javascript" src="javascripts/mediarecorder.js"></script>
|
||||||
<script type="text/javascript" src="javascripts/safechat.js"></script>
|
<script type="text/javascript" src="javascripts/safechat.js"></script>
|
||||||
<link href="stylesheets/jquery.cssemoticons.css" media="screen" rel="stylesheet" type="text/css" />
|
<link href="stylesheets/jquery.cssemoticons.css" media="screen" rel="stylesheet" type="text/css" />
|
||||||
<script src="javascripts/jquery.cssemoticons.js" type="text/javascript"></script>
|
<script src="javascripts/jquery.cssemoticons.js" type="text/javascript"></script>
|
||||||
@@ -143,13 +144,12 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div id="preview"></div>
|
|
||||||
<div id="videorecorder" style="display: none">
|
<div id="videorecorder" style="display: none">
|
||||||
<video autoplay muted></video>
|
<video autoplay muted></video><br/>
|
||||||
<button id="record" onclick="record()">record</button>
|
|
||||||
<button id="done" onclick="done()">done</button>
|
<button id="done" onclick="done()">done</button>
|
||||||
<button id="abort" onclick="abort()">abort</button>
|
<button id="abort" onclick="abort()">abort</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="preview"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="msgs"></div>
|
<div id="msgs"></div>
|
||||||
<script>
|
<script>
|
||||||
|
Reference in New Issue
Block a user