10

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?

styfle
  • 22,361
  • 27
  • 86
  • 128

3 Answers3

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.

styfle
  • 22,361
  • 27
  • 86
  • 128
  • 1
    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. – Gonçalo Jan 24 '17 at 15:42
  • 2
    Looks like the creation of the buffer should be triggered by the onstop event of the recorder to avoid the buffer to be empty. – Nuthinking Feb 20 '17 at 14:56
2

Take a look at the MediaRecorder API

You should convert that stream to binary chunks.

demian85
  • 2,394
  • 3
  • 20
  • 20
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()), ...);
jameshfisher
  • 34,029
  • 31
  • 121
  • 167
  • 1
    I forgot to update my answer since I fixed that in [2018](https://github.com/styfle/magnemite/commit/a6f08c9c4a3779206b80380a9efc949dc63ecd62). Also, didn't realize that `blob.arrayBuffer()` was introduced in [2019](https://developer.mozilla.org/en-US/docs/Web/API/Blob/arrayBuffer), very nice! – styfle Oct 17 '20 at 15:40
  • @styfle I removed my comment about your answer, since you updated it :-) – jameshfisher Oct 17 '20 at 22:06