Convert multiple video Blobs into one and save it - javascript

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);
};

Related

Javascript img.onload() not firing and/or promises always pending on firefox but not chrome

I'm loading one or multiple pictures from local storage and putting it to a canvas on top of another picture.
In the code below I first collect all the loading images in promises that only resolve after the image has loaded. Second after all the promises have resolved I send the created image data.
var Promises = [];
for (const selectedOverlayImage of selectedOverlayImages.values()) {
Promises.push(new Promise((resolve, reject) => {
const overlayImg = new Image();
overlayImg.onerror = () => { console.log(`Image ${selectedImage.value} loading error`); reject(); };
overlayImg.onload = () => {
canvasContext.drawImage(overlayImg, 0, 0, canvas.width, canvas.height);
resolve();
};
overlayImg.src = `overlayImages/${selectedOverlayImage.value}.png`;
console.log(selectedOverlayImage.value);
}));
}
console.log("1");
var ret = Promise.all(Promises).then(() => {
console.log("IN");
let imageData = canvas.toDataURL('image/png');
fetch("http://localhost:8000/picture-editing.php", { //Call php to create picture in database
method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"},
body: `imageData=${imageData}`
});
alert('Images successfully loaded!'); //This creates a gain of time before the refresh, allowing the new picture to be present more often after refresh
window.location.reload();
}).catch((error) => {console.log(error);});
console.log("2");
console.log(ret);
setTimeout(() => {
console.log('the queue is now empty');
console.log(ret);
});
});
On Chrome I am able to create one image consisting of multiple pictures.
Console logs when successfully creating picture with wine as overlay-image, we can see the Promise.all function gets entered
On firefox I am not able to create one image of multiple pictures.
We can see the code never enters Promise.all while Promise.all never resolves
Thank you for your help!
Reproduce the error
Set the following in index.php.
<h1 class="pageTitle">Create Picture</h1>
<form method="get">
<input name="modeOfPicture" type="submit" value="webcam"/>
<input name="modeOfPicture" type="submit" value="upload"/>
</form>
<div class="wrapperPictureEditing">
<div id="getPicture" class="block1PictureEditing"></div>
<canvas id="takePictureCanvas" style="display:none;" width="320" height="240"></canvas>
<form id="takePictureButton" class="block2PictureEditing">
<p>Choose Overlay Image:</p>
<label><input type="checkbox" name="overlay" value="wine"> Wine</label><br>
<button type="submit">Take photo</button><br>
</form>
<script>
function displaySelectedPicture(input) {
var selectedPictureDisplay = document.getElementById('selectedPictureDisplay');
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
selectedPictureDisplay.src = e.target.result;
};
reader.readAsDataURL(input.files[0]);
} else { selectedPictureDisplay.removeAttribute('src') }
}
function takePicture(camera) {
const takePictureButton = document.getElementById('takePictureButton');
takePictureButton.addEventListener("submit", () => {
let canvas = document.getElementById('takePictureCanvas');
let canvasContext = canvas.getContext('2d');
selectedOverlayImages = document.querySelectorAll('input[name="overlay"]:checked');
console.log(selectedOverlayImages);
if (camera) {
const stream = document.querySelector('video');
canvasContext.drawImage(stream, 0, 0, canvas.width, canvas.height);
} else {
const selectedImage = document.getElementById('selectedPictureDisplay');
canvasContext.drawImage(selectedImage, 0, 0, canvas.width, canvas.height);
}
var Promises = [];
for (const selectedOverlayImage of selectedOverlayImages.values()) {
Promises.push(new Promise((resolve, reject) => {
const overlayImg = new Image();
overlayImg.onerror = () => { console.log(`Image ${selectedImage.value} loading error`); reject(); };
overlayImg.onload = () => {
canvasContext.drawImage(overlayImg, 0, 0, canvas.width, canvas.height);
resolve();
};
overlayImg.src = `image.png`;
console.log(selectedOverlayImage.value);
}));
}
console.log("1");
var ret = Promise.all(Promises).then(() => {
console.log("IN");
let imageData = canvas.toDataURL('image/png');
alert('Images successfully loaded!'); //This creates a gain of time before the refresh, allowing the new picture to be present more often after refresh
window.location.reload();
}).catch((error) => {console.log(error);});
console.log("2");
console.log(ret);
setTimeout(() => {
console.log('the queue is now empty');
console.log(ret);
});
});
}
function setupVideoStream(stream) {
const video = document.querySelector('video');
video.srcObject = stream;
video.play();
}
function errorMsg(msg, error) {
const errorElement = document.getElementById('error');
errorElement.innerHTML += `${msg}`;
if (typeof error !== 'undefined') {
console.error(error);
}
}
function streamError(error) {
if (error.name === 'NotAllowedError') {
errorMsg('Permissions have not been granted to use your camera' +
', you need to allow the page access to your camera in ' +
'order to take pictures. Instead you can upload an image.');
} else if (error.name === "NotFoundError") {
errorMsg('No camera has been found on this device. ' +
'Instead you can upload an image.');
} else {
errorMsg(`getUserMedia error: ${error.name}`, error);
}
}
async function init() {
var getPictureHTML = document.getElementById('getPicture');
var modeOfPicture = (new URLSearchParams(document.location.search)).get('modeOfPicture');
if (modeOfPicture === null || modeOfPicture === "webcam") {
try {
const stream = await navigator.mediaDevices.getUserMedia({video: true});
getPictureHTML.insertAdjacentHTML('afterbegin',
"<video width='320' height='240' autoplay></video><br>");
setupVideoStream(stream);
takePicture(true);
} catch (error) {
getPictureHTML.insertAdjacentHTML('afterbegin',
"<p id='error' class='error'></p>" +
"<label><input type='file' name='selectedPicture' accept='image/*' " +
"onchange='displaySelectedPicture(this);'><br>Upload image<br></label>" +
"<br><img id='selectedPictureDisplay' width='320' height='240'/>");
streamError(error);
takePicture(false);
}
} else {
getPictureHTML.insertAdjacentHTML('afterbegin',
"<label><input type='file' name='selectedPicture' accept='image/*' " +
"onchange='displaySelectedPicture(this);'><br>Upload image<br></label>" +
"<br><img id='selectedPictureDisplay' width='320' height='240'/>");
takePicture(false);
}
}
init();
</script>
In same directory put an image named image.png, such as this one image example
Then run the server with this command in same directory:
php -S localhost:8000
Visit localhost:8000 in Chrome and Firefox, see the console, on success "IN" should appear after taking a picture with wine overlay image.

What to do to store recording stream into the mobile browser using Media Recorder API?

I have a project requirement to record and store the currently running video stream. I have used webRTC for video streaming, and to record the video streaming, I have used MediaRecorder API. It is completely working fine in the desktop system. But it is not working in the mobile browser.
Any idea why it is not working in the mobile browser?
Following is the code snippet:
componentDidMount = async () => {
recordingStream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true,
});
const mergedAudio = await this.mergeAudioStreams();
console.log("audio track length... ", mergedAudio.length);
const tracks = [
...recordingStream.getVideoTracks(),
...mergedAudio,
];
captureStream = new MediaStream(tracks);
mediaRecorder = new MediaRecorder(captureStream, options);
mediaRecorder.ondataavailable = this.handleDataAvailable;
mediaRecorder.start();
}
handleDataAvailable = async event => {
console.log("data-available", event);
if (event.data.size > 0) {
recordedChunks.push(event.data);
this.download();
} else {
// ...
console.log("in else");
}
};
download = async () => {
console.log("in download fn");
var blob = await new Blob(recordedChunks, {
type: "video/mp4",
});
//called the API to store the recorded video
}
mergeAudioStreams = async () => {
console.log("recordScreen fn called");
const ctx = new AudioContext();
const dest = ctx.createMediaStreamDestination();
let localSource, remoteSource;
if (this.state.localStream.getAudioTracks().length > 0) {
localSource = ctx.createMediaStreamSource(this.state.localStream);
}
if (this.state.selectedVideo.stream.getAudioTracks().length > 0) {
remoteSource = ctx.createMediaStreamSource(
this.state.selectedVideo.stream
);
}
const localGain = ctx.createGain();
const remoteGain = ctx.createGain();
localGain.gain.value = 0.7;
remoteGain.gain.value = 0.7;
localSource.connect(localGain).connect(dest);
remoteSource.connect(remoteGain).connect(dest);
console.log("combine tracks..", dest.stream.getAudioTracks());
return dest.stream.getAudioTracks();
};

Audio Record Problem in JavaScript some of the recording is missing

Hello Everyone I am trying to Record Audio and then after Recording I play it and when I hit the Cut function it just get the second I am on it in the player and cut to the end and start recording again and then overwrite the chunks array till the end with the new recording to sum up all of that all I need is when I record 5 seconds and then I want to overwrite the last 3 seconds so I remove the last 3 elements in the array and push the new chunks to the original array
when I do that and send the new chunk to the audio player it plays the first time as intended and if I pressed play again it just plays the newly recorded part only without the first 2 seconds that I preserved from the old recording
let audioChunks = [];
let Recorder = null;
const recordAudio = () => {
return new Promise(resolve => {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
const mediaRecorder = new MediaRecorder(stream);
mediaRecorder.addEventListener("dataavailable", event => {
audioChunks.push(event.data);
});
const start = () => {
mediaRecorder.start(1000);
};
const stop = () => {
return new Promise(resolve => {
mediaRecorder.addEventListener("stop", () => {
const audioBlob = new Blob(audioChunks,{type: 'audio/mpeg-3'});
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
document.getElementById("AudioRecodingPlayer").src = audio.src;
document.getElementById("AudioRecodingPlayer").currentTime = 0;
const play = () => {
audio.play();
};
resolve({ audioBlob, audioUrl, play });
});
mediaRecorder.stop();
});
};
resolve({ start, stop });
});
});
};
async function StartRecording(){
Recorder = await recordAudio();
Recorder.start();
}
async function StopRecording(){
const audio = Recorder.stop();
}
function Cut(){
var Audio = document.getElementById("AudioRecodingPlayer");
var CurrenTime = Math.round(Audio.currentTime);
audioChunks.length = CurrenTime;
StartRecording();
}
On the Cut() function, you are not actually removing the elements of the array, nor changing the index or window. You are only changing the value of length.
What you would have to do is overwrite the audio chunks, or remove the chunks from that point forward.
let audioChunks = [];
let Recorder = null;
const recordAudio = () => {
return new Promise(resolve => {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
const mediaRecorder = new MediaRecorder(stream);
mediaRecorder.addEventListener("dataavailable", event => {
audioChunks.push(event.data);
});
const start = () => {
mediaRecorder.start(1000);
};
const stop = () => {
return new Promise(resolve => {
mediaRecorder.addEventListener("stop", () => {
const audioBlob = new Blob(audioChunks,{type: 'audio/mpeg-3'});
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
document.getElementById("AudioRecodingPlayer").src = audio.src;
document.getElementById("AudioRecodingPlayer").currentTime = 0;
const play = () => {
audio.play();
};
resolve({ audioBlob, audioUrl, play });
});
mediaRecorder.stop();
});
};
resolve({ start, stop });
});
});
};
async function StartRecording(){
Recorder = await recordAudio();
Recorder.start();
}
async function StopRecording(){
const audio = Recorder.stop();
}
function Cut(){
var Audio = document.getElementById("AudioRecodingPlayer");
var CurrenTime = Math.round(Audio.currentTime);
audioChunks.splice(CurrentTime); // this will actually remove the elements after the "CurrentTime" index
StartRecording();
}

Cropper.js getValue is not working every time I upload a photo

I am using cropper.js to upload an image in a form. Sometimes it works fine, but sometimes when I upload the image and I use cropper.getValue it displays an error: Uncaught TypeError: Cannot read property 'naturalWidth' of undefined. How can i fix this?
var imageForm = document.querySelector(".userImage");
var fileInput = document.querySelector("input[name=user_photo]");
fileInput.addEventListener("change", function(event){
var reader = new FileReader();
reader.onload = function(){
var output = document.querySelector('.croppr-image');
var submitImageCont = document.querySelector('#submitImage');
output.src = reader.result;
imageForm.insertBefore(output, submitImageCont);
var overlay = document.querySelector(".overlay")
overlay.classList.remove("displayNone");
var publicProfileForm = document.querySelector("form.publicProfile");
var cropper = new Croppr('#cropper', {
onInitialize: (instance) => { console.log(instance); },
onCropStart: (data) => { console.log('start', data); },
onCropEnd: (data) => { console.log('end', data); },
onCropMove: (data) => { console.log('move', data); }
});
var value = cropper.getValue();
publicProfileForm.querySelector("input[name=cropped_image_x]").value = value.x;
publicProfileForm.querySelector("input[name=cropped_image_y]").value = value.y;
publicProfileForm.querySelector("input[name=cropped_image_width]").value = value.width;
publicProfileForm.querySelector("input[name=cropped_image_height]").value = value.height;
}
}, false);
When you create an instance of Cropper you must destroy previuous instance. Calling .destroy() method. This unbind Cropper and you must start it over again. A short example:
var cropper = null;
fileInput.addEventListener("change", function(event){
// a lot of stuffs
if(cropper){
cropper.destroy();
}
cropper = new Croppr('#cropper', {
onInitialize: (instance) => { console.log(instance); },
onCropStart: (data) => { console.log('start', data); },
onCropEnd: (data) => { console.log('end', data); },
onCropMove: (data) => { console.log('move', data); }
});
// another stuffs
}, false);

MediaRecorder not saving properly in Electron

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();
}

Categories