video attachments from camera

version-1
Marc Wäckerlin 8 years ago
parent 7a17c7e3ca
commit 343bdd8c7c
  1. 176
      nodejs/public/javascripts/mediarecorder.js
  2. 82
      nodejs/public/javascripts/safechat.js
  3. 17
      nodejs/public/stylesheets/safechat.css
  4. 6
      nodejs/views/index.ejs

@ -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) {
if (files) files.forEach(function(file) {
if (!file.name) file.name = guessfilename(file.type, from, date);
var img = document.createElement('img');
img.title = file.name;
var a = document.createElement('a');
a.href = file.content;
a.download = file.name;
a.target = '_blank';
if (file.type.match('^image/')) {
var img = document.createElement('img');
img.title = file.name;
img.src = file.content;
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);
}
var a = document.createElement('a');
a.href = file.content;
a.download = file.name;
a.target = '_blank';
a.appendChild(img);
$(id).append(a);
});
}
// function getUserMedia() {
// return navigator.mediaDevices.getUserMedia || navigator.getUserMedia
// || navigator.webkitGetUserMedia || navigator.mozGetUserMedia
// || navigator.msGetUserMedia;
// }
var recorder;
var mediarecorder = null;
var mediastream = null;
function record() {
mediarecorder = mediastream.record();
}
function done() {
mediarecorder.getRecordedData(function(videoblob) {
previewfile(videoblob, "video/webm");
});
mediarecorder = null;
if (recorder) {
recorder.stop();
previewfile(recorder.recording(), "video/webm");
abort();
}
}
function abort() {
mediarecorder = null;
mediastream.getTracks().forEach(function(track) {track.stop()});
mediastream = null;
if (recorder) {
$("#videorecorder").hide();
recorder.release();
delete recorder; recorder = null;
}
}
/// Record Video from builtin camera
function recordvideo() {
$("#videorecorder").show();
if (mediastream) {
mediarecorder = mediastream.record();
}
try {
navigator.mediaDevices.getUserMedia({audio: true, video: true})
.then(function(stream) {
console.log(stream);
mediastream = stream;
var video = $("#videorecorder video");
video.attr("src", window.URL.createObjectURL(mediastream));
}).catch(function() {
error("capture video failed", false);
});
abort();
$("#videorecorder").show();
recorder = new MediaStreamRecorder({
video: {
mandatory: {
maxWidth: 400,
maxHeight: 400
}
},
audio: true
});
recorder.on("ready", function() {
$("#videorecorder video").attr("src", recorder.preview());
recorder.start();
});
recorder.on('')
} catch (e) {
console.log(e);
error("cannot access camera", true);
}
}
function previewfile(content, type, name) {
@ -440,6 +445,7 @@ function previewfile(content, type, name) {
filecontent.push({name: name, type: type, content: content});
var video = document.createElement("video");
video.setAttribute("controls", "controls");
video.setAttribute("loop", "loop");
video.setAttribute("src", content);
video.setAttribute("title", name);
$("#preview").append(video);

@ -255,6 +255,21 @@ label[for=send] img {
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;
}

@ -7,6 +7,7 @@
<script type="text/javascript" src="javascripts/jquery.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="javascripts/mediarecorder.js"></script>
<script type="text/javascript" src="javascripts/safechat.js"></script>
<link href="stylesheets/jquery.cssemoticons.css" media="screen" rel="stylesheet" type="text/css" />
<script src="javascripts/jquery.cssemoticons.js" type="text/javascript"></script>
@ -143,13 +144,12 @@
</span>
</div>
</form>
<div id="preview"></div>
<div id="videorecorder" style="display: none">
<video autoplay muted></video>
<button id="record" onclick="record()">record</button>
<video autoplay muted></video><br/>
<button id="done" onclick="done()">done</button>
<button id="abort" onclick="abort()">abort</button>
</div>
<div id="preview"></div>
</div>
<div id="msgs"></div>
<script>

Loading…
Cancel
Save