8

I've built a Chrome extension that captures screen activity and microphone input and outputs a video file. Since chrome.desktopCapture can't record audio input alongside screen capture, I'm getting the mic in its own, separate stream. So:

//get screen stream
chrome.desktopCapture.chooseDesktopMedia(['screen'], null, (stream_id, opts) => {
    let constraints = {video: mandatory: {
        chromeMediaSource: 'desktop',
        chromeMediaSourceId: stream_id
    }};
    navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
        video_stream = stream;
    });
});

//get mic stream
navigator.mediaDevices.getUserMedia({audio: true}).then((stream) => {
    audio_stream = stream;
});

Then, later, when I've got both streams, before I start recording I merge them by creating a master stream and add the respective tracks from the separate video and audio streams to it.

let master_stream = new MediaStream(video_stream);
master_stream.addTrack(audio_stream.getTracks()[0]);

And ultimately, when I end up with a video file, this works GREAT. I get screen and mic.

Question: Why does this technique NOT work if I ask Chrome to ALSO record system sound?

So if I change ['screen'] to ['screen', 'audio'], with everything else the same, I end up with no mic in the resultant video.

If I output getTracks() on master_stream, here's what I get:

Array(3)
    0: MediaStreamTrack {kind: "audio", id: "9ee3ee33-73ee-41e4-957c-d6fd3aaada43", label: "System Audio", enabled: true, muted: false, …}
    1: MediaStreamTrack {kind: "audio", id: "ab2429a1-7f75-48f2-9ee1-6a4bfd7ca942", label: "Default - Microphone (Samson Meteor Mic) (17a0:0310)", enabled: true, muted: false, …}
    2: MediaStreamTrack {kind: "video", id: "4ecb1929-31d0-4a79-8cbc-1a8759323c3b", label: "screen:0:0", enabled: true, muted: false, …}

I can't see an obvious cause as to why adding system audio kills mic audio in the resultant output. Anyone any ideas?

Mitya
  • 33,629
  • 9
  • 60
  • 107
  • I've seen your comment in this [link](https://stackoverflow.com/questions/50096428/cant-get-speaker-microphone-audio-working-with-chrome-desktopcapture-and-reco?noredirect=1#comment89076275_50096428). It looks like it is related with this [open issue](https://github.com/muaz-khan/RecordRTC/issues/302). You can also create and track bugs and features in the [Chromium bug tracker](https://bugs.chromium.org/p/chromium/issues/list). – abielita Jun 26 '18 at 18:19
  • Hi, I have been trying to develop just a simple extension that records screen and audio but have been struggling till now. Could you share a bit more of your approach? It'd be very helpful as I am just a beginner and all of the desktopCapture docs and examples are very old. Thank you! – Deep Dhillon Apr 05 '20 at 14:15
  • 1
    I never got it to record system sound, only microphone audio + screen or webcam. I'd suggest checking out https://webrtc.org/. If you want to dig into my code, download a Chrome Extension called govidigo (blue logo). Find out where Chrome installs it on your machine, then you can inspect the JavaScript I wrote. – Mitya Apr 05 '20 at 20:19
  • Same, I'm unable to get system audio via a Chrome extension :( There are Chrome apps that can get system audio (https://stackoverflow.com/questions/40686795/chrome-extension-cant-make-chrome-desktopcapture-choosedesktopmedia-capture-wi ), but Chrome apps are being deprecated :( – functionptr May 15 '20 at 11:10
  • Yeah that's a good thing. Hopefully the standards-based extensions API will be free of this issue. – Mitya Jul 18 '21 at 10:03

1 Answers1

0

// add two buttons startrecording and stoprecording
let desktopStream; // variable to hold the desktop stream
let microphoneStream; // variable to hold the microphone stream
let recordedChunks = []; // array to store recorded chunks
let mediaRecorder; // variable to hold the MediaRecorder instance

const startRecording = async () => {
    try {
        desktopStream = await navigator.mediaDevices.getDisplayMedia({
            audio: true,
            video: true
        });
        microphoneStream = await navigator.mediaDevices.getUserMedia({
            audio: true
        });

        const audioContext = new AudioContext();
        const microphoneSource = audioContext.createMediaStreamSource(microphoneStream);

        // Create a MediaStreamAudioDestinationNode
        const destination = audioContext.createMediaStreamDestination();
        const desktopAudioSource = audioContext.createMediaStreamSource(desktopStream);
        desktopAudioSource.connect(destination);
        microphoneSource.connect(destination);

        // Combine the audio streams
        const combinedAudioStream = destination.stream;

        // Merge the audio and video streams
        const combinedStream = new MediaStream();
        combinedStream.addTrack(combinedAudioStream.getAudioTracks()[0]);
        combinedStream.addTrack(desktopStream.getVideoTracks()[0]);

        // Create a MediaRecorder to record the combined stream
        mediaRecorder = new MediaRecorder(combinedStream);

        mediaRecorder.ondataavailable = (event) => {
            recordedChunks.push(event.data);
        };

        mediaRecorder.onstop = () => {
            const blob = new Blob(recordedChunks, {
                type: 'video/webm'
            });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            document.body.appendChild(a);
            a.style = 'display: none';
            a.href = url;
            a.download = 'recorded-video.webm';
            a.click();
            window.URL.revokeObjectURL(url);
            recordedChunks = [];
        };

        mediaRecorder.start();
    } catch (error) {console.error('Error starting recording:', error);
    }
};

const stopRecording = () => {
    if (desktopStream) {
        desktopStream.getTracks().forEach((track) => track.stop());
    }

    if (microphoneStream) {
        microphoneStream.getTracks().forEach((track) => track.stop());
    }

    if (mediaRecorder && mediaRecorder.state !== 'inactive') {
        mediaRecorder.stop();
    }
};
  • Welcome to Stack Overflow! Please consider _explaining_ your answer rather than posting just code by itself, otherwise people won't necessarily what they're supposed to be looking at. – Mitya Jun 20 '23 at 14:52
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jun 21 '23 at 20:34