首页 > 解决方案 > Properly using chrome.tabCapture in a manifest v3 extension

问题描述

I'm trying to write a manifest v3 Chrome extension that captures tab audio. However as far as I can tell, with manifest v3 there are some changes that make this a bit difficult:

Despite this I managed to get something that nearly works as popup scripts still have access to chrome.tabCapture. However, there is a drawback - the audio of the tab is muted and there doesn't seem to be a way to unmute it. This is what I have so far:

  1. Query the service worker current tab from the popup script.
let tabId;

// Fetch tab immediately
chrome.runtime.sendMessage({command: 'query-active-tab'}, (response) => {
    tabId = response.id;
});

This is the service worker, which response with the current tab ID.

chrome.runtime.onMessage.addListener(
    (request, sender, sendResponse) => {
        // Popup asks for current tab
        if (request.command === 'query-active-tab') {
            chrome.tabs.query({active: true}, (tabs) => {
                if (tabs.length > 0) {
                    sendResponse({id: tabs[0].id});
                }
            });

            return true;
        }
        ...
  1. Again in the popup script, from a keyboard shortcut command, use chrome.tabCapture.getMediaStreamId to get a media stream ID to be consumed by the current tab, and send that stream ID back to the service worker.
// On command, get the stream ID and forward it back to the service worker
chrome.commands.onCommand.addListener((command) => {
    chrome.tabCapture.getMediaStreamId({consumerTabId: tabId}, (streamId) => {
        chrome.runtime.sendMessage({
            command: 'tab-media-stream',
            tabId: tabId,
            streamId: streamId
        })
    });
});
  1. The service worker forwards that stream ID to the content script.
chrome.runtime.onMessage.addListener(
    (request, sender, sendResponse) => {
        ...
        // Popup sent back media stream ID, forward it to the content script
        if (request.command === 'tab-media-stream') {
            chrome.tabs.sendMessage(request.tabId, {
                command: 'tab-media-stream',
                streamId: request.streamId
            });
        }
    }
);
  1. The content script uses navigator.mediaDevices.getUserMedia to get the stream.
// Service worker sent us the stream ID, use it to get the stream
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    navigator.mediaDevices.getUserMedia({
        video: false,
        audio: true,
        audio: {
            mandatory: {
                chromeMediaSource: 'tab',
                chromeMediaSourceId: request.streamId
            }
        }
    })
    .then((stream) => {
        // Once we're here, the audio in the tab is muted
        // However, recording the audio works!
        const recorder = new MediaRecorder(stream);
        const chunks = [];
        recorder.ondataavailable = (e) => {
            chunks.push(e.data);
        };
        recorder.onstop = (e) => saveToFile(new Blob(chunks), "test.wav");
        recorder.start();
        setTimeout(() => recorder.stop(), 5000);
    });
});

Here is the code that implements the above: https://github.com/killergerbah/-test-tab-capture-extension

This actually does produce a MediaStream, but the drawback is that the sound of the tab is muted. I've tried playing the stream through an audio element, but that seems to do nothing.

Is there a way to obtain a stream of the tab audio in a manifest v3 extension without muting the audio in the tab?

I suspect that this approach might be completely wrong as it's so roundabout, but this is the best I could come up with after reading through the docs and various StackOverflow posts. I've also read that the tabCapture API is going to be moved for manifest v3 at some point, so maybe the question doesn't even make sense to ask - however if there is a way to still properly use it I would like to know.

标签: google-chromegoogle-chrome-extensionweb-mediarecorder

解决方案


这可能不是您正在寻找的东西,但也许它可以提供一些见解。

我尝试过通过音频元素播放流,但这似乎无济于事。

具有讽刺意味的是,这就是我设法解决这个问题的方法;通过在弹出窗口本身中创建一个对象。在弹出脚本中使用 tabCapture 时,它​​返回流,我将音频 srcObject 设置为该流。

HTML: <audio id="audioObject" autoplay> No source detected </audio>

JS:

chrome.tabCapture.capture({audio: true, video: false}, function(stream) {
    var audio = document.getElementById("audioObject");
    audio.srcObject = stream
})

根据Manifest V3 上的这篇文章,chrome.capture 将成为 tabCapture 等的新命名空间,但除此之外我还没有看到任何东西。


推荐阅读