Reputation: 12304
I want to save a video that is captured from webcam to a local file. So far I have been able to:
getUserMedia
RecordRTC
RecordRTC
I cannot figure out how to save the video to a file though. Calling save()
on RecordRTC
allows me to download the video file, but I want everything to happen in nodejs for further processing. The file is playable, regardless. I tried to write the blob and dataURL to file, but that file is not playable.
Upvotes: 1
Views: 6156
Reputation: 9140
The MediaRecorder class implemented in Chromium and thus Electron, can support your use case. Saving the media as a file is not only possible but arguably trivial:
const recorder = new MediaRecorder(await navigator.mediaDevices.getUserMedia());
const blob_reader = new FileReader();
const storage_stream = require("fs").createWriteStream(path);
const blobs = [];
blob_reader.addEventListener("load", ev => {
storage_stream.write(Buffer.from(ev.currentTarget.result));
if(blobs.length) ev.currentTarget.readAsArrayBuffer(blobs.shift());
});
recorder.addEventListener("dataavailable", ev => {
if(blob_reader.readyState != 1) blob_reader.readAsArrayBuffer(ev.data);
else blobs.push(ev.data);
});
Alternatively, instead of using the FileReader
class to obtain array buffer objects corresponding to their blob equivalents, one may use Blob.prototype.arrayBuffer
, which makes for even shorter and probably more readable source code:
const recorder = new MediaRecorder(await navigator.mediaDevices.getUserMedia());
const storage_stream = require("fs").createWriteStream(path);
recorder.addEventListener("dataavailable", async ev => {
storage_stream.write(Buffer.from(await ev.data.arrayBuffer()));
});
No matter the variant chosen from the two above, short of what I consider a technically expendable and undesired (but necessary) step of converting Blob
objects to ArrayBuffer
equivalents in order to wrap the latter in a Buffer
, this is as efficient as the API implementation itself is -- the JavaScript machine itself does no heavy lifting here.
Remarks and explanations on the solution proposed:
MediaRecorder.start
method. Note that the snippet is made to work with multiple generated blobs, if needed -- using a timeslice (first argument to start
) of 1 second may be a good idea, depending. Such timeslice allows you to do proper streaming of data, as opposed to getting a potentially gigantic single blob worth of encoded video stream stored in process memory (which is what you get if you omit the timeslice
parameter to start
).start
is issued (with a timeslice), the resulting file will start growing "on disk", depending on the timeslice value and lengths of intermediate buffers.MediaRecorder
object, as part of encoding media, generates blobs which, for one reason or another, aren't very "consumable" by many other APIs, so we have to convert them to something that is consumable, in this case objects of the more convenient for us ArrayBuffer
class.FileReader
, since conversion of a blob into an array buffer is asynchronous, we have a queue of blobs that is duly converted on a FIFO basis.require("fs")
gets us a Node.js module, "fs" is not a module otherwise available in your Web browser, at least not according to a Web standard or draft thereof that I know of. That's the module, however, that allows us to dump the resulting array buffers into a file.Buffer.from(...)
is more than meets the eye here -- there is no Buffer
class in the Web API space, it's a Node.js class that is able to wrap an ArrayBuffer
as a view (no data copying). This is necessary because you can't write an ArrayBuffer
into a file stream directly..webm
or an .mp4
).ffmpeg
, however, can trivially postprocess such files by indexing them and patching them accordingly. I consider such step optional -- there is nothing inherently wrong with a transport stream like the ones generated by the snippet above.path
property/variable passed to createWriteStream
above denotes the path to the file you want to save the video to.I have tested the snippet with the 3.0.4 version of Electron on Windows 10.
An alternative approach to saving the media as a file -- which interestingly works with a Web browser of a fairly modern make, too -- is to concatenate the blobs provided by the recorder into a single blob and let user download the entire product using the URL.createObjectURL
method. I'd argue, however, that this approach and the one illustrated above, are not equivalent in their function or capability -- I am not 100% confident about how blobs are implemented in different user agents -- whether backed by physical/virtual RAM, for example -- media files can grow very large and if, contained in blobs and referenced with URLs, these need to be stored in RAM in their entirety, well that's a potentially latent resource exhaustion fault. In comparison, writing a file piecemeal shouldn't naturally suffer from such constraints. Anyway, I leave the application of createObjectURL
an exercise to the reader, it should be rather trivial, everything else being in place.
Upvotes: 11
Reputation: 397
I have never used the RecordRTC.
Worked in use native(JavaScript) MediaRecorder API to record.
I wrote sample
Upvotes: 2