October 22, 2024
Chicago 12, Melborne City, USA
javascript

Audio Recording Javascript Chrome extension working with Youtube but not working on Google Meet Mic to record audio


I have fully working code which starts, stop recording audio from a tab. It also plays and download the most recent recorded audio. I tested it both Youtube and Google Meet. It captures the speaker output but when I try to capture mic audio from Google Meet, it fails. What should I be modifying in the code so that it captures both mic as well speaker audio?

Manifest.json

{
  "name": "Tab Capture - Recorder",
  "description": "Records the current tab in an offscreen document.",
  "version": "1",
  "manifest_version": 3,
  "minimum_chrome_version": "116",
  "action": {
    "default_popup": "popup.html",
    "default_icon": "not-recording.png"
  },
  "permissions": ["tabCapture", "offscreen", "storage", "contextMenus"]
}

offscreen.js

chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
  if (message.target === 'offscreen') {
    switch (message.type) {
      case 'start-recording':
        startRecording(message.data);
        break;
      case 'stop-recording':
        stopRecording();
        break;
      case 'play-audio':
        playAudio();
        break;
      case 'download-audio':
        downloadAudio();
        break;
      default:
        throw new Error('Unrecognized message:', message.type);
    }
  } else if (message.type === 'check-audio') {
    sendResponse({ audioAvailable: audioBlobs.length > 0 });
    return true;
  }
});

let recorder;
let data = [];
let audioBlobs = []; // Array to store audio blobs

async function startRecording(streamId) {
  if (recorder?.state === 'recording') {
    throw new Error('Called startRecording while recording is in progress.');
  }

  const media = await navigator.mediaDevices.getUserMedia({ 
    audio: {
      mandatory: {
        chromeMediaSource: 'tab',
        chromeMediaSourceId: streamId
      }
    }
  });

  const output = new AudioContext();
  const source = output.createMediaStreamSource(media);
  source.connect(output.destination);

  recorder = new MediaRecorder(media, { mimeType: 'audio/webm' });
  recorder.ondataavailable = (event) => data.push(event.data);
  recorder.onstop = () => {
    const blob = new Blob(data, { type: 'audio/webm' });
    audioBlobs.push(blob); // Store the blob in the array
    data = [];
  };
  recorder.start();

  window.location.hash="recording";
}

async function stopRecording() {
  recorder.stop();
  recorder.stream.getTracks().forEach((t) => t.stop());
  window.location.hash="";
}

function playAudio() {
  if (audioBlobs.length > 0) {
    const lastBlob = audioBlobs[audioBlobs.length - 1];
    const audioUrl = URL.createObjectURL(lastBlob);
    const audio = new Audio(audioUrl);
    audio.play();
  } else {
    console.error('No audio available to play.');
  }
}

function downloadAudio() {
  if (audioBlobs.length > 0) {
    const lastBlob = audioBlobs[audioBlobs.length - 1];
    const audioUrl = URL.createObjectURL(lastBlob);
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = audioUrl;
    a.download = 'recording.webm';
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  } else {
    console.error('No audio available to download.');
  }
}

popup.js

document.addEventListener('DOMContentLoaded', () => {
  const isRecording = localStorage.getItem('isRecording') === 'true';

  document.getElementById('startButton').disabled = isRecording;
  document.getElementById('stopButton').disabled = !isRecording;

  // Check if there are any audio recordings stored
  chrome.runtime.sendMessage({ type: 'check-audio' }, (response) => {
    const audioAvailable = response.audioAvailable;
    document.getElementById('playButton').disabled = !audioAvailable;
    document.getElementById('downloadButton').disabled = !audioAvailable;
  });
});

document.getElementById('startButton').addEventListener('click', async () => {
  document.getElementById('startButton').disabled = true;
  document.getElementById('stopButton').disabled = false;
  document.getElementById('playButton').disabled = true;
  document.getElementById('downloadButton').disabled = true;

  localStorage.setItem('isRecording', 'true');

  const existingContexts = await chrome.runtime.getContexts({});
  let offscreenDocument = existingContexts.find(
    (c) => c.contextType === 'OFFSCREEN_DOCUMENT'
  );

  if (!offscreenDocument) {
    await chrome.offscreen.createDocument({
      url: 'offscreen.html',
      reasons: ['USER_MEDIA'],
      justification: 'Recording from chrome.tabCapture API'
    });
  }

  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  const streamId = await chrome.tabCapture.getMediaStreamId({ targetTabId: tab.id });

  chrome.runtime.sendMessage({
    type: 'start-recording',
    target: 'offscreen',
    data: streamId
  });
});

document.getElementById('stopButton').addEventListener('click', () => {
  document.getElementById('startButton').disabled = false;
  document.getElementById('stopButton').disabled = true;

  chrome.runtime.sendMessage({
    type: 'stop-recording',
    target: 'offscreen'
  }, () => {
    chrome.runtime.sendMessage({ type: 'check-audio' }, (response) => {
      const audioAvailable = response.audioAvailable;
      document.getElementById('playButton').disabled = !audioAvailable;
      document.getElementById('downloadButton').disabled = !audioAvailable;
    });
  });

  localStorage.setItem('isRecording', 'false');
});

document.getElementById('playButton').addEventListener('click', () => {
  chrome.runtime.sendMessage({
    type: 'play-audio',
    target: 'offscreen'
  });
});

document.getElementById('downloadButton').addEventListener('click', () => {
  chrome.runtime.sendMessage({
    type: 'download-audio',
    target: 'offscreen'
  });
});



You need to sign in to view this answers

Leave feedback about this

  • Quality
  • Price
  • Service

PROS

+
Add Field

CONS

+
Add Field
Choose Image
Choose Video