MediaRecorder Blob to file in an electron app - electron

I have an electron app that has very simple desktop capturing functionality:
const {desktopCapturer} = require('electron')
const fs = require('fs');
var recorder;
var chunks = [];
var WINDOW_TITLE = "App Title";
function startRecording() {
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 === WINDOW_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);
chunks = [];
recorder.ondataavailable = function(event) {
chunks.push(event.data);
};
recorder.start();
}
function stopRecording() {
recorder.stop();
toArrayBuffer(new Blob(chunks, {type: 'video/webm'}), function(ab) {
var buffer = toBuffer(ab);
var file = `./test.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;
}
// Record for 3.5 seconds and save to disk
startRecording();
setTimeout(function() { stopRecording() }, 3500);
I know that to save the MediaRecorder blob sources, I need to read it into an ArrayBuffer, then copy that into a normal Buffer for the file to be saved.
However, where this seems to be failing for me is combining the chunk of blobs into blobs. When the chunks are added into a single Blob - it's like they just disappear. The new Blob is empty, and every other data structure they are copied into afterwards also is completely empty.
Before creating the Blob, I know I have valid Blob's in the chunks array.
Here's what the debug info of chunks is, before executing the new Blob(chunks, {.. part.
console.log(chunks)
Then here's the debug info of the new Blob(chunks, {type: 'video/webm'}) object.
console.log(ab)
I'm completely stumped. All the reference tutorials or other SO answers I can find basically follow this flow. What am I missing?
Electron version: 1.6.2

That's not possible to be working. 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();
}

This problem literally just fixed itself today without me changing anything. I'm not sure what about my system changed (other than a reboot) but it's now working exactly as it should.

Related

How to Render Webcam/Live video in WebGL without Library

I have been following this tutorial and it succesfully renders video on to a cube using webGL.
Is it possible for instead of using a video I would like to use a live feed from a webcam without using frameworks like Three.js?
The code below reads from the webcam into HTMLVideoElement how to convert it into texture so I can map it to my vertices in raw WebGL?
function setupVideo(url) {
const video = document.createElement('video');
var playing = false;
var timeupdate = false;
video.autoplay = true;
video.muted = true;
video.loop = true;
// Waiting for these 2 events ensures
// there is data in the video
video.addEventListener('playing', function() {
playing = true;
checkReady();
}, true);
video.addEventListener('timeupdate', function() {
timeupdate = true;
checkReady();
}, true);
navigator.getUserMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
var hasUserMedia = navigator.getUserMedia ? true : false;
console.log(hasUserMedia);
// Prefer camera resolution nearest to 1280x720.
var constraints = { audio: true, video: { width: 640, height: 360 } };
navigator.mediaDevices.getUserMedia(constraints)
.then(function(mediaStream) {
video.srcObject = mediaStream;
video.onloadedmetadata = function(e) {
};
})
.catch(function(err) { console.log(err.name + ": " + err.message); }); // always check for errors at the end.
video.play();
function checkReady() {
if (playing && timeupdate) {
copyVideo = true;
}
}
return video;
}

How do I check the size of the data file before I upload it (Flutter)

When I take the file I want the data file size information, in the future the upload file limit will be made based on the file size. the following code that I use
void _openFileExplorer() async {
setState(() => _loadingPath = true);
try {
_directoryPath = null;
_paths = (await FilePicker.platform.pickFiles(
type: _pickingType,
allowMultiple: _multiPick,
allowedExtensions: ['jpg', 'pdf', 'doc', 'docx', 'png', 'jpeg'],
))
?.files;
} on PlatformException catch (e) {
print("Unsupported operation" + e.toString());
} catch (ex) {
print(ex);
}
if (!mounted) return;
setState(() {
_loadingPath = false;
_fileName = _paths != null ? _paths.map((e) => e.name).toString() : '...';
});
}
you can get file size using function named lengthSync.
just use this function like
var size = file.lengthSync()
it will give file size in bytes.

Audio file does not persist in Cordova with LocalFileSystem.PERSISTENT

I have been trying to store Audio file in persistent storage for two days without success.
So far I am able to create an audio file which records audio from Microphone (The app has the permission) using the code attached below.
The audio file is getting generated & stored successfully, I can play it.
But the real problem is when I close the app and come back and try to play the file it shows error.
"{"message": "Cannot use audio file from resource '/myrecording.wav'",
"code":1}"
The file is not persistent across app sessions even though I used LocalFileSystem.PERSISTENT.
I am not sure whether the problem is with my Media/Audio code or File storage code.
Please find the code attached below:
Below function records the audio from the microphone.
function _recordAudio() {
var deferred = $q.defer();
var src = "myrecording.wav";
alert("SRC:" + src);
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fileSystem) {
fileSystem.root.getFile(src, {
create: true,
exclusive: false
}, function (fileEntry) {
alert("File " + src + " created at " + fileEntry.fullPath);
var mediaRec = new Media(fileEntry.fullPath,
function () {
alert("Success");
}, function (error) {
alert("error:" + JSON.stringify(error));
});
// Record audio
mediaRec.startRecord();
// Stop recording after 10 sec
var recTime = 0;
var recInterval = setInterval(function () {
recTime = recTime + 1;
if (recTime >= 5) {
clearInterval(recInterval);
mediaRec.stopRecord();
deferred.resolve(fileEntry.fullPath);
}
}, 1000);
}, function (error) {
alert("getFile error:" + JSON.stringify(error));
deferred.reject();
}); //of getFile
}, function (error) {
alert("requestFileSystem error:" + JSON.stringify(error));
deferred.reject();
}); //of requestFileSystem
return deferred.promise;
}
Below function plays the audio.
function _play2() {
var src = "myrecording.wav";
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fileSystem) {
fileSystem.root.getFile(src, null, function (fileEntry) {
alert("File " + src + " created at " + fileEntry.fullPath);
var mediaRec = new Media(fileEntry.fullPath,
function () {
alert("Success play2");
}, function (error) {
//Getting error after closing and opening the app
//Error message = {"message": "Cannot use audio file from resource '/myrecording.wav'","code":1}
alert("error play2:" + JSON.stringify(error));
});
mediaRec.play();
});
});
}
I solved this problem by passing cdvfile: path to the Media plugin in PlayAudio function code and copying the file from Temp storage to persistent storage.
I had to use localURL of the file.
This part solved my problem:
fileEntry.file(function (file) {
_playNow(file.localURL);
}
For full functions refer code snippets below:
recordAudio: function (projectNo, ItemNo) {
try {
var deferred = $q.defer();
var recordingTime = 0;
_audioLoader = $("#audioLoader");
_audioLoader.show();
UtilityService.showPopup('audio');
_isRecording = true;
_recordFileName = "Audio_" + projectNo + "_" + ItemNo + ".wav";
_mediaRecord = new Media(_recordFileName);
//Record audio
_mediaRecord.startRecord();
var recordingInterval = setInterval(function () {
recordingTime = recordingTime + 1;
$('#audioPosition').text(_secondsToHms(recordingTime));
if (!_isRecording) {
clearInterval(recordingInterval);
_mediaRecord.stopRecord();
_mediaRecord.release();
deferred.resolve();
}
}, 1000);
//document.getElementById('audioPosition').innerHTML = '0 sec';
$('#audioPosition').text('0 sec');
return deferred.promise;
}
catch (ex) {
alert('WMMCPA|recordAudio:- ' + ex.message);
}
},
Get file path from the persistent storage and send it to the play method.
//To play recorded audio for specific project item
playAudio: function (projectNo, ItemNo) {
try {
_recordFileName = "Audio_" + projectNo + "_" + ItemNo + ".wav";
var newFileUri = cordova.file.dataDirectory + _recordFileName;
window.resolveLocalFileSystemURL(newFileUri, function (fileEntry) {
fileEntry.file(function (file) {
_playNow(file.localURL);
}, function (error) {
alert("WMMCPA|playAudio.file:-" + JSON.stringify(error));
});
}, function (error) {
alert("WMMCPA|playAudio.resolveLocalFileSystemURL:-" + JSON.stringify(error));
});
}
catch (ex) {
alert("WMMCPA|playAudio:-" + ex.message);
}
}
function _playNow(src) {
try {
var mediaTimer = null;
_audioLoader = $("#audioLoader");
_audioLoader.show();
UtilityService.showPopup('audio');
//Create Media object from src
_mediaRecord = new Media(src);
//Play audio
_mediaRecord.play();
} catch (ex) {
alert('WMMCPA|_playNow.mediaTimer:- ' + ex.message);
}
}, 1000);
} catch (ex) {
alert('WMMCPA|_playNow:- ' + ex.message);

Content.once is not a function

I try to push a file to the IPFS, and I have converted to the Buffer. I got this error " content.once is not a function".
I am using this library in node.
var Buffer = require('buffer/').Buffer;
const doc = new jsPDF();
doc.fromHTML('test',10,10);
var covnertedBuffer = Buffer.from(doc.output('arraybuffer');
Then, I take the convertedBuffer and pass it to the IPFS api.
Any idea?
Updated test:
I have successfully pushed a file to the IPFS via the API with this code below.
const filename = '/home/administrator/Downloads/5HP8LWKHLV.pdf';
this.ipfsApi = ipfsApi('localhost', '5001');
let readablestream = fs.createReadStream(filename);
readablestream.on('readable', () => {
let result = readablestream.read();
console.log(result);
if (result) {
this.ipfsApi.files.add(result, function(err, files) {
if (err) {
res.json('err');
console.log(err);
}
res.json(files);
});
}
});
But, when I get the arrayBuffer from the doc.output and convert to the Buffer object and push to the IPFS and it failed. Please see below.
var _buffer = Buffer.from(req.buffer);
console.log('Converted to buffer:' + _buffer);
this.ipfsApi = ipfsApi('localhost', '5001');
this.ipfsApi.files.add(_buffer, function(err, files) {
if (!err) {
res.status(500);
console.log(err);
} else {
res.json(files);
res.status(200);
}
});
Thank you
Adding Buffer.from(your_buffer) to your buffer before doing ipfs push works.
ipfs.files.add(Buffer.from(put_your_buffer_here), (error, result) => {
if(error) {
console.error(error)
return
}
console.log("upload is successful");
});

Send a recorded file via Filetransfer with Cordova/Phonegap

I am trying to send a voice recording that I recorded via the Media plugin.
When I try to send the file I get this FileError.NOT_FOUND_ERR error:
Error opening file /myRecording100.wav: Error Domain=NSCocoaErrorDomain Code=260 "The operation couldn’t be completed. (Cocoa error 260.)" UserInfo=0xa358640 {NSFilePath=/myRecording100.wav, NSUnderlyingError=0xa34fb30 "The operation couldn’t be completed. No such file or directory"}
2014-08-06 17:02:26.919 Bring Me[40961:c07] FileTransferError {
code = 1;
source = "/myRecording100.wav";
target = "http://XXXX.xom";
}
However, I can play the voice recording after recording it.
Why would I be able to play the file (showing that the file was recorded and saved correctly) but FileTransfer be unable to send it?
Here is my code (for ios):
var my_recorder = null;
var mediaFileFullName = null; // iOS
var mediaRecFile = "myRecording100.wav";
var checkFileOnly = false;
/******
Call when start recording
******/
function startRecording() {
checkFileOnly = false;
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onSuccessFileSystem, function() {
console.log("***test: failed in creating media file in requestFileSystem");
});
}
function onSuccessFileSystem(fileSystem) {
if (checkFileOnly === true) {
// Get File and send
fileSystem.root.getFile(mediaRecFile, { create: false, exclusive: false }, onOK_GetFile, onFail_GetFile);
}
else {
// Create File
fileSystem.root.getFile(mediaRecFile, { create: true, exclusive: false }, onOK_SaveFile, onFail_GetFile);
}
}
/* Save the file*/
function onOK_SaveFile(fileEntry) {
mediaFileFullName = fileEntry.fullPath;
my_recorder = new Media(mediaFileFullName,
function() { document.location ="address_form.html"; // Redirect the user to an other page },
function(err) { console.log("playAudio():callback Record Error: "+err);}
);
my_recorder.startRecord();
}
/* Get the file and send it */
function onOK_GetFile(fileEntry) {
mediaFileFullName = fileEntry.fullPath;
/*
// Read the recorded file is WORKING !
my_player = new Media(mediaFileFullName, onMediaCallSuccess, onMediaCallError);
my_player.play();
*/
var options = new FileUploadOptions();
options.fileKey = "want";
options.fileName = "file.wav";
options.mimeType = "audio/wav";
options.chunkedMode = false;
options.params = parameters;
var ft = new FileTransfer();
ft.upload(mediaFileFullName, "https://SERVER_ADDRESS", win, fail, options);
}
/******
Called when stop recording
******/
function stopRecording() {
if (my_recorder) {
my_recorder.stopRecord();
}
}
Since the v1.0 of File plugin, to upload a file in the filesystem via the file-transfer plugin, you'll need to use the .toURL() method to access to it.
If you are upgrading to a new (1.0.0 or newer) version of File, and
you have previously been using entry.fullPath as arguments to
download() or upload(), then you will need to change your code to use
filesystem URLs instead.
FileEntry.toURL() and DirectoryEntry.toURL() return a filesystem URL
of the form
So the correct code is :
/* Get the file and send it */
function onOK_GetFile(fileEntry) {
var options = new FileUploadOptions();
options.fileKey = "want";
options.fileName = "file.wav";
options.mimeType = "audio/wav";
options.chunkedMode = false;
options.params = parameters;
var ft = new FileTransfer();
ft.upload(fileEntry.toURL(), "https://SERVER_ADDRESS", win, fail, options);
}
I got the exact same issue on iOS,and FileUploadOptions didn't work for me.
In case someone is struggling as well, the solution for me has been to switch to LocalFileSystem.Temporary.
Here there is a snippet which shows a full example (not tested on Android):
var accessType = LocalFileSystem.TEMPORARY; // It was LocalFileSystem.PERSISTENT;
/** Utility function to return a fileEntry together with the metadata. */
var getFile = function(name, create, successCallback, failCallback) {
WL.Logger.debug("Request for file " + name + " received, create is " + create + ".");
var onSuccessFileSystem = function(fileSystem) {
fileSystem.root.getFile(name, { create: create, exclusive: false },
function(fileEntry){
WL.Logger.debug("Success, file entry for " + name + " is " + JSON.stringify(fileEntry));
fileEntry.getMetadata(function(metadata){
WL.Logger.debug("File entry " + name + " metadata is: " + JSON.stringify(metadata));
successCallback(fileEntry, metadata);
}, function(err) {
WL.Logger.debug("Fail to retrieve metadata, error: " + JSON.stringify(err));
if(failCallback) failCallback(err);
});
},
function(err) {
WL.Logger.error("Failed to retrieve the media file " + name + ".");
if(failCallback) failCallback(err);
});
}
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(accessType, 0, onSuccessFileSystem, function(err) {
WL.Logger.error("Failed to access file system.");
if(failCallback) failCallback(err);
});
};
var Recorder = declare([ ], {
mediaSrc : null,
mediaObj : null,
constructor : function(data, domNode){
this.mediaSrc = "new_recording.wav";
},
startRecord : function() {
var self = this;
var startRecording = function(source) {
var onMediaCallSuccess = function() { WL.Logger.debug("Media object success."); };
var onMediaCallError = function(err) { WL.Logger.error("Error on the media object: " + JSON.stringify(err)); };
self.mediaObj = new Media(source, onMediaCallSuccess, onMediaCallError);
self.mediaObj.startRecord();
};
// On iOS, first I need to create the file and then I can record.
if (deviceCheck.phone.ios) {
WL.Logger.debug("iOS detected, making sure the file exists.");
getFile(this.mediaSrc, true, function(fileEntry){ startRecording(fileEntry.fullPath); });
} else {
if (!deviceCheck.phone.android)
WL.Logger.warn("Don't know the device, trying to record ...");
else
WL.Logger.debug("Android detected.");
startRecording(this.mediaSrc);
}
},
stopRecord : function() {
this.mediaObj.stopRecord();
this.mediaObj.release();
},
play: function() {
var p,
playSuccess = function() { WL.Logger.debug("Play success."); p.release(); },
playFail = function() { WL.Logger.debug("Play fail."); };
p = new Media(this.mediaSrc, playSuccess, playFail);
p.play();
},
getData : function(successCallback, failCallback) {
var fileName = (deviceCheck.phone.android ? "/sdcard/" : "") + this.mediaSrc;
WL.Logger.debug("Asking for the file entry ... ");
getFile(this.mediaSrc, false,
function(fileEntry, metadata) {
WL.Logger.debug("Success: I found a file entry: " + fileEntry.nativeURL + ", size is " + metadata.size);
fileEntry.file(function(file) {
WL.Logger.debug("Success: file retrieved!");
var reader = new FileReader();
reader.onloadend = function(evt) {
WL.Logger.debug("Sending content and event data to success callback.");
successCallback(this.result, metadata, evt);
};
reader.readAsDataURL(file);
}, function(err){
WL.Logger.error("Error: Impossible to retrieve the file");
failCallback(err);
})
}, function(err){
WL.Logger.error("Fail: no file entry found: " + JSON.stringify(err));
failCallback(err);
});
}
});
There is a bit of Worklight (debug output) and dojo (declare), but this code could be useful as reference.

Resources