Saving desktopCapturer to video file in Electron
Asked Answered
S

4

11

The desktopCapturer api example shows how to write a screen capture stream to a <video> element.

// In the renderer process.
var desktopCapturer = require('electron').desktopCapturer;

desktopCapturer.getSources({types: ['window', 'screen']}, function(error, sources) {
  if (error) throw error;
  for (var i = 0; i < sources.length; ++i) {
    if (sources[i].name == "Electron") {
      navigator.webkitGetUserMedia({
        audio: false,
        video: {
          mandatory: {
            chromeMediaSource: 'desktop',
            chromeMediaSourceId: sources[i].id,
            minWidth: 1280,
            maxWidth: 1280,
            minHeight: 720,
            maxHeight: 720
          }
        }
      }, gotStream, getUserMediaError);
      return;
    }
  }
});

function gotStream(stream) {
  document.querySelector('video').src = URL.createObjectURL(stream);
}

function getUserMediaError(e) {
  console.log('getUserMediaError');
}

I tried to replace the gotStream function with the following:

function gotStream(stream) {
  var fs = require('fs');
  fs.writeFileSync('vid.mp4', stream);
}

This creates a text file with [object MediaStream] as the contents.

How can I record this stream and save to a file on disk?

Sepulchral answered 20/4, 2016 at 19:24 Comment(3)
I answerd similar question. linkBluefarb
Related: https://mcmap.net/q/117961/-convert-a-binary-nodejs-buffer-to-javascript-arraybufferSepulchral
Related: https://mcmap.net/q/1015716/-saving-huge-filesSepulchral
S
14

I answered my own question with the help of Demian's link to MediaRecorder as well as other related questions.

Below is an excerpt from magnemite with some minor simplifications and converted from TypeScript to JavaScript ES5 for better understanding to most readers.

var fs = require('fs');
var electron = require('electron');

var SECRET_KEY = 'Magnemite';

var recorder;
var blobs = [];

function startRecording() {
    var title = document.title;
    document.title = SECRET_KEY;

    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) {
                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 = `./videos/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) {
    return Buffer.from(ab);
}

// Record for 7 seconds and save to disk
startRecording();
setTimeout(function() { stopRecording() }, 7000);

This will record the current electron window for 7 seconds and save to disk.

Sepulchral answered 3/1, 2017 at 16:17 Comment(2)
Anyone make a clue of how to record only audio and not video to a audio file output? I wish to avoid the overhead of recording audio and video and only "export" the audio.Burnedout
Looks like the creation of the buffer should be triggered by the onstop event of the recorder to avoid the buffer to be empty.Vigilance
A
3

Take a look at the MediaRecorder API

You should convert that stream to binary chunks.

Abeu answered 15/12, 2016 at 15:5 Comment(0)
S
1

The desktopCapturer example shows how to get a Blob. You then need to convert the Blob to something accepted by fs.writeFile. The following is short and efficient:

fs.writeFile(filepath, Buffer.from(await blob.arrayBuffer()), ...);
Subglacial answered 16/10, 2020 at 15:41 Comment(2)
I forgot to update my answer since I fixed that in 2018. Also, didn't realize that blob.arrayBuffer() was introduced in 2019, very nice!Sepulchral
@Sepulchral I removed my comment about your answer, since you updated it :-)Subglacial
W
1

just a reminder you should use writestream upon on dataavailable to avoid memory leak.

when calling mediaRecorder.start() you can pass a number that represents interval to trigger the ondataavailable

Example:

// Assuming you already created the mediaStream

const mediaRecorder = new MediaRecorder(stream);

// starting the recording
mediaRecorder.start(1_000); // 1 second interval to trigger the dataavailable

// Create the stream file;
let writeStream = createWriteStream('your_path'); // Import this from 'fs'

const handleData = async (e: BlobEvent) => {
  const arrayBuffer = await e.data.arrayBuffer(); // convert to array buffer
  writeStream.write(Buffer.from(arrayBuffer));
}

const handleStop = () => {
  writeStream.end(() => {
    writeStream = null; // Cleanup the writeStream once done.
  });
}

mediaRecorder.addEventListener('dataavailable', handleData);
mediaRecorder.addEventListener('stop', handleStop);

In this approach you can record long recordings without having a lag issue since you no longer storing buffers in memory.

Weimaraner answered 5/3 at 5:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.