MediaRecorder not saving properly in Electron - javascript

I am trying to record my screen using media Recorder API using electron like this. I have a start function which starts recording the screen. I use the electron API for this. and a stop function to stop recording using MediaRecorder API from - https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder.
Currently it stops after 7 seconds.
This is how it looks like.
var electron = require('electron');
var SECRET_KEY = 'test.html';
var recorder;
var blobs = [];
console.log("its loading");
function startRecording() {
var title = document.title;
document.title = SECRET_KEY;
console.log("Started Recording");
electron.desktopCapturer.getSources({ types: ['window', 'screen'] }, function(error, sources) {
if (error) throw error;
for (let i = 0; i < sources.length; i++) {
let src = sources[i];
if (src.name === SECRET_KEY) {
console.log("its passing");
document.title = title;
navigator.webkitGetUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: src.id,
minWidth: 800,
maxWidth: 1280,
minHeight: 600,
maxHeight: 720
}
}
}, handleStream, handleUserMediaError);
return;
}
}
});
}
function handleStream(stream) {
recorder = new MediaRecorder(stream);
blobs = [];
recorder.ondataavailable = function(event) {
blobs.push(event.data);
};
recorder.start();
}
function stopRecording() {
recorder.stop();
toArrayBuffer(new Blob(blobs, { type: "video/webm" }), function(ab) {
var buffer = toBuffer(ab);
var file = `./SavedVideos/example.webm`;
fs.writeFile(file, buffer, function(err) {
if (err) {
console.error('Failed to save video ' + err);
} else {
console.log('Saved video: ' + file);
}
});
});
}
function handleUserMediaError(e) {
console.error('handleUserMediaError', e);
}
function toArrayBuffer(blob, cb) {
let fileReader = new FileReader();
fileReader.onload = function() {
let arrayBuffer = this.result;
cb(arrayBuffer);
};
fileReader.readAsArrayBuffer(blob);
}
function toBuffer(ab) {
let buffer = new Buffer(ab.byteLength);
let arr = new Uint8Array(ab);
for (let i = 0; i < arr.byteLength; i++) {
buffer[i] = arr[i];
}
return buffer;
}
var startbutton = document.getElementById('start');
var stopbutton = document.getElementById('stop');
// startbutton.onclick = startRecording();
// stopbutton.onclick = stopRecording();
// Record for 7 seconds and save to disk
startRecording();
setTimeout(function() { stopRecording() }, 7000);
The code is passing without any error. But when I open the video which is saved using VLC, it has 0 bytes and it doesn't start.
How can fix this ?

You didn't wait for value to come in stopReocoring. You need to change your stopRecording function to following:
function stopRecording() {
var save = function() {
console.log(blobs);
toArrayBuffer(new Blob(blobs, {type: 'video/webm'}), function(ab) {
console.log(ab);
var buffer = toBuffer(ab);
var file = `./videos/example.webm`;
fs.writeFile(file, buffer, function(err) {
if (err) {
console.error('Failed to save video ' + err);
} else {
console.log('Saved video: ' + file);
}
});
});
};
recorder.onstop = save;
recorder.stop();
}

Related

How to horizontially flip a image in JavaScript

I am attempting to flip in image captured from the webcam stream. The webcam view in the CSS is flipped, but when I take a snapshot, it takes it directly from the camera stream. I am doing this for OCR recognition. (TesseractJS)
Capture Function:
function captureSnapshot() {
if (null != cameraStream) {
var ctx = capture.getContext("2d");
var img = new Image();
ctx.drawImage(stream, 0, 0, capture.width, capture.height);
img.src = capture.toDataURL("image/png");
img.width = 240;
snapshot.innerHTML = "";
snapshot.appendChild(img);
}
}
Full JavaScript
// The buttons to start & stop stream and to capture the image
var btnStart = document.getElementById("btn-start");
var btnStop = document.getElementById("btn-stop");
var btnCapture = document.getElementById("btn-capture");
// The stream & capture
var stream = document.getElementById("stream");
var capture = document.getElementById("capture");
var snapshot = document.getElementById("snapshot");
// The video stream
var cameraStream = null;
// Attach listeners
btnStart.addEventListener("click", startStreaming);
btnStop.addEventListener("click", stopStreaming);
// Start Streaming
function startStreaming() {
var mediaSupport = "mediaDevices" in navigator;
if (mediaSupport && null == cameraStream) {
navigator.mediaDevices
.getUserMedia({ video: true })
.then(function(mediaStream) {
cameraStream = mediaStream;
stream.srcObject = mediaStream;
stream.play();
})
.catch(function(err) {
console.log("Unable to access camera: " + err);
});
} else {
alert("Your browser does not support media devices.");
return;
}
}
// Stop Streaming
function stopStreaming() {
if (null != cameraStream) {
var track = cameraStream.getTracks()[0];
track.stop();
stream.load();
cameraStream = null;
}
}
btnCapture.addEventListener("click", captureSnapshot);
function captureSnapshot() {
if (null != cameraStream) {
var ctx = capture.getContext("2d");
var img = new Image();
ctx.drawImage(stream, 0, 0, capture.width, capture.height);
img.src = capture.toDataURL("image/png");
img.width = 240;
snapshot.innerHTML = "";
snapshot.appendChild(img);
}
}
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(",")[1]);
var mimeString = dataURI
.split(",")[0]
.split(":")[1]
.split(";")[0];
var buffer = new ArrayBuffer(byteString.length);
var data = new DataView(buffer);
for (var i = 0; i < byteString.length; i++) {
data.setUint8(i, byteString.charCodeAt(i));
}
return new Blob([buffer], { type: mimeString });
}
function recognizeText(img) {
Tesseract.recognize(
"https://tesseract.projectnaptha.com/img/eng_bw.png",
"eng",
{ logger: m => console.log(m) }
).then(({ data: { text } }) => {
console.log(text);
});
}
CSS Flip (I don't know if this matters):
video {
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
This code below you have should work for css. i would recommend just adding a class to the image when you instantiate it. that class in css will use that of below.
Is this something you have tried?
video {
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}

Convert multiple video Blobs into one and save it

I record the screen and push a blob into an array, when it´s avaible. In the end I want to save it to a webm or a mp4 file. I tried converting it to a new blob and then save it as an arraybuffer, but that didn´t worked out.
function recordScreen() {
blobs = [];
recorder = new MediaRecorder(localstream);
recorder.ondataavailable = function(event) {
blobs.push(event.data);
}
setTimeout(() => {
stopRecording();
}, 7000);
}
function stopRecording() {
recorder.stop();
console.log(blobs);
//But how to save it to a mp4 or a webm?
}
//UPDATE
Ok now its written to the file but it is not playable.
function recordScreen() {
recorder = new MediaRecorder(localstream);
recorder.ondataavailable = function(event) {
chunks.push(event.data);
}
recorder.start();
setTimeout(() => {
stopRecording();
}, 7000);
}
function stopRecording() {
recorder.stop();
setTimeout(() => {
toArrayBuffer(new Blob(chunks, {type: 'video/webm'}), function(ab) {
var buffer = toBuffer(ab);
var file = `example.webm`;
fs.writeFile(file, buffer, function(err) {
if (err) {
console.error('Failed to save video ' + err);
} else {
console.log('Saved video: ' + file);
}
});
});
}, 1000);
}
function toArrayBuffer(blob, cb) {
let fileReader = new FileReader();
fileReader.onload = function() {
let arrayBuffer = this.result;
cb(arrayBuffer);
};
fileReader.readAsArrayBuffer(blob);
}
function toBuffer(ab) {
return Buffer.from(ab);
}
this is my first answer ever...It's old but maybe I can save some time to someone. Rigth now I'm working in something like that, and I resolve this situation like that:
1-In the ondataavailable event push the data in the result array.
2-In the onstop event create a new blob with the array.
Example code:
videoRecorder.ondataavailable = (event) => {
event.data.size > 0 && videoChunksArray.push(event.data);
};
videoRecorder.onstop = () => {
let blob = new Blob( videoChunksArray, {
type: "video/webm",//your own type here
});
let url = URL.createObjectURL(blob);
let a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = url;
a.download = "PRUEBA.webm";//your own name here
a.click();
window.URL.revokeObjectURL(url);
};

replace() in cropper js

My project is about saving the cropped image and show it in the view.
in my form when i cropped the image it works, but when i want to change the image so i crop it again and save. it create two rows with same image.
and when i change the image 3 times it create 3 rows with the same image and so on.
there is method called replace() that i have to use but i dont know how to use it.
this is my code
window.addEventListener('DOMContentLoaded', function () {
var avatar = document.getElementById('avatar');
var image = document.getElementById('image');
var input = document.getElementById('input');
var $progress = $('.progress');
var $progressBar = $('.progress-bar');
var $alert = $('.alert');
var $modal = $('#modal');
var cropper;
var title = $('#title');
var description = $('#description');
var arabic_title = $('#arabic_title');
var arabic_description = $('#arabic_description');
$('[data-toggle="tooltip"]').tooltip();
input.addEventListener('change', function (e) {
var files = e.target.files;
var done = function (url) {
input.value = '';
image.src = url;
// $alert.hide();
$modal.modal('show');
};
var reader;
var file;
var url;
if (files && files.length > 0) {
file = files[0];
if (FileReader) {
reader = new FileReader();
reader.onload = function (e) {
done(reader.result);
console.log('ok2');
};
reader.readAsDataURL(file);
console.log('ok3');
}
}
});
$modal.on('shown.bs.modal', function () {
cropper = new Cropper(image, {
aspectRatio: 1.7,
viewMode: 3,
});
}).on('hidden.bs.modal', function () {
cropper.destroy();
cropper = null;
});
document.getElementById('crop').addEventListener('click', function () {
var initialAvatarURL;
var canvas;
$modal.modal('hide');
if (cropper) {
canvas = cropper.getCroppedCanvas({
width: 400,
height: 400,
});
initialAvatarURL = avatar.src;
avatar.src = canvas.toDataURL();
$progress.show();
$alert.removeClass('alert-success alert-warning');
document.getElementById('save').addEventListener('click', function () {
canvas.toBlob(function (blob) {
var formData = new FormData();
formData.append('avatar', blob);
formData.append('title', title.val());
formData.append('description', description.val());
formData.append('arabic_title', arabic_title.val());
formData.append('arabic_description', arabic_description.val());
if (title.val() !== '' && description.val() !== '' && arabic_title.val() !== '' && arabic_description.val() !== '') {
for (let pair of formData.entries()) {
console.log(pair[0] + ', ' + pair[1]);
}
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$.ajax("{{url('admin/services')}}", {
method: 'POST',
data: formData,
processData: false,
contentType: false,
xhr: function () {
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function (e) {
var percent = '0';
var percentage = '0%';
if (e.lengthComputable) {
percent = Math.round((e.loaded / e.total) * 100);
percentage = percent + '%';
$progressBar.width(percentage).attr('aria-valuenow', percent).text(percentage);
}
};
return xhr;
},
success: function (data) {
$alert.show().addClass('alert-success').text('Upload success');
console.log(data);
},
error: function (error) {
avatar.src = initialAvatarURL;
$alert.show().addClass('alert-warning').text('Upload error');
console.log(error);
},
complete: function () {
$progress.hide();
},
});
}
});
});
}
});
});
$service = 'No service';
if (isset($_FILES['img'])) {
$service = Service::create(['title'=>$request->title,
'description'=>$request->description,
'photo'=>$request->img]);
}
return $service;
Try this.
Your Form Should Be
<form action="files/upload" method="post" enctype="multipart/form-data">
<input type="file" name="photo"/>
</form
Your Controller Should Be like
if ($request->hasFile('photo')) {
// move file upload here
}

MediaSource randomly stops video

I am working on a project where I want to
getUserMedia -> MediaRecorder -> socketIO -> MediaSource appendbuffer
I got it to work, however after a few seconds it randomly stops.
I know about WebRTC, but in the project I am working on it's based on an environment which is a version of Chrome embedded that doesn't support WebRTC.
Server:
'use strict';
const io = require('socket.io')();
io.on('connection', (socket) => {
console.log('connection');
socket.on('stream', (data) => {
socket.emit('stream', data);
});
});
io.listen(3001);
Client:
//Codecs
var SOURCE_BUFFER_MIME = 'video/webm; codecs="vp8, vorbis"';
var RECORDER_MIME = 'video/webm; codecs="vp8"';
//Here buffers are stored when sourcebuffer is still 'updating'
var buffers = [];
//Video1: Directly from get user media
var $video1 = document.getElementById('video1');
//Video2: Using mediasource
var $video2 = document.getElementById('video2');
var mediaSource = new MediaSource();
var sourceBuffer = null;
$video2.src = window.URL.createObjectURL(mediaSource);
$video2.play();
mediaSource.addEventListener('sourceopen', function(e) {
sourceBuffer = mediaSource.addSourceBuffer(SOURCE_BUFFER_MIME);
});
//Start socketIO connection
socket = io.connect('http://localhost:3001/');
socket.on('connect', function() {
console.log('Connected');
socket.on('stream', function(data) {
if (sourceBuffer !== null) {
console.count('Received');
buffers.unshift(new Uint8Array(data));
append();
}
});
});
function append() {
if (buffers.length && mediaSource.readyState == "open" &&
(sourceBuffer !== null && !sourceBuffer.updating)) {
var buffer = buffers[0];
console.log(buffer.byteLength);
buffers.shift();
sourceBuffer.appendBuffer(buffer);
}
}
navigator.webkitGetUserMedia({audio: true, video: true},
function(stream) {
$video1.src = window.URL.createObjectURL(stream);
$video1.play();
//Extract data
recorder = new MediaRecorder(stream, { mimeType : RECORDER_MIME });
recorder.ondataavailable = function(e) {
// recorder.pause();
if (e.data && e.data.size > 0) {
socket.emit('stream', e.data);
}
};
recorder.start(500);
//recorder.start(5000); //this causes the pause right after the next batch is appended
},
function(err) {
console.log(err);
}
);

canvas.toDataURL to img src

I recently downloaded an open source webcam to gif script and when the gif is created it saves as a dataurl. Is their a way I can change this? I would rather it save in a folder on the server and be something like http://example.com/folder/image.gif
Code:
*global GIFEncoder,encode64*/
var encoder = new GIFEncoder(),
video = document.querySelector('video'),
canvas = document.querySelector('canvas'),
ctx = canvas.getContext('2d'),
localMediaStream = null,
snapshotPause = 0,
recording = true,
framesPause = 120,
maxFrames = 28,
totalFrames = 0,
t;
encoder.setSize(320, 240);
encoder.setRepeat(0);
encoder.setDelay(framesPause);
encoder.setQuality(90);
window.URL = window.URL || window.webkitURL;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
if (navigator.getUserMedia) {
navigator.getUserMedia({
audio: true,
video: true
}, function (stream) {
$('#start-image, #start-fake').hide();
$('#video, #start').show();
video.src = window.URL.createObjectURL(stream);
localMediaStream = stream;
}, function (e) {
console.log('Error:', e);
}
);
} else {
console.log('not supported');
}
function snapshot() {
if (localMediaStream) {
ctx.drawImage(video, 0, 0, 320, 240);
encoder.addFrame(ctx);
var image = $('<img />').attr('src', canvas.toDataURL('image/gif'));
$('#thumbs').append(image);
totalFrames++;
if (totalFrames === maxFrames) {
recordingEnd();
}
}
}
function recordingEnd() {
var binaryGif = encoder.stream().getData(),
dataUrl = 'data:image/gif;base64,' + encode64(binaryGif),
gif = $('<img />').attr('src', dataUrl);
totalFrames = 0;
recording = !recording;
$('#start').html('Start');
clearInterval(t);
$('#indicator').hide();
encoder.finish();
$('#result-gif').html('').append(gif);
overlayShow('preview');
//b64 = encode64(binaryGif);
}
function overlayShow(panel) {
$('.panel').hide();
$('#' + panel).show();
$('#overlay-bg').show();
$('#overlay').show();
}
function overlayHide() {
$('#overlay-bg').hide();
$('#overlay').hide();
}
$('#start').click(function () {
if (recording) {
recording = !recording;
$('#thumbs-holder-close').show();
$('#thumbs-holder').animate({
'margin-left': '320px'
}, 300);
$('#thumbs').html('');
encoder.start();
$('#indicator').show().animate({
width: '100%'
}, snapshotPause, function () {
$('#indicator').css({
'width': '0'
});
});
t = setInterval(function () {
snapshot();
$('#indicator').animate({
width: '100%'
}, snapshotPause, function () {
$('#indicator').css({
'width': '0'
});
});
}, snapshotPause);
$(this).html('Stop');
} else {
recordingEnd();
}
});
$('#thumbs-holder-close').click(function () {
$(this).hide();
$('#thumbs-holder').animate({
'margin-left': 0
}, 300);
});
$('#overlay-close').click(function () {
overlayHide();
});
$('.new').click(function () {
overlayHide();
});
$('#showSettings').click(function () {
overlayShow('settings');
});
$('input[type=range]').change(function () {
var id = $(this).attr('id'),
val = $(this).val();
$(this).next().html(val);
window[id] = parseInt(val);
if (id === 'framesPause') {
framesPause = val;
encoder.setDelay(framesPause);
}
});
$('#save').click(function () {
$.ajax({
url: 'images/save.php',
method: 'POST',
data: {
image: b64
},
dataType: 'json',
success: function(data) {
var a = $('<a />').attr('href', "images/" + data.name).html('permalink');
$('#url').append(a);
},
error: function(err) {
console.log(err);
}
});
});
##Convert your dataUrl into Blobs
function dataURLtoBlob(dataURL) {
// Decode the dataURL
var binary = atob(dataURL.split(',')[1]);
// Create 8-bit unsigned array
var array = [];
for(var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
// Return our Blob object
return new Blob([new Uint8Array(array)], {type: 'image/gif'});
}
/* var file= dataURLtoBlob(dataURL); */
Now, you can add the Blob as FormData and send to server
Sending data as Blob instead of dataUrl.
As bergi pointed out, the encode.stream().getData() actually returns a binary string.
var array = [];
for(var i = 0; i < binaryGIF.length; i++) {
array.push(binaryGIF.charCodeAt(i));
}
// Return our Blob object
var file = new Blob([new Uint8Array(array)], {type: 'image/gif'});
// Create new form data
var fd = new FormData();
// Append our Canvas image file to the form data
fd.append("sample-image-name-for-name-attribute", file);
// And send it
$.ajax({
url: "my-rest-endpoint",
type: "POST",
data: fd,
processData: false,
contentType: false,
}).done(function(respond){
alert(respond);
});
Hope it helps. You should be able to use the file on the server as you handle a normal file upload.

Categories