如何在 Chrome 扩展程序清单 v3 中的选项卡中播放音频流

5
为了从清单v2中的标签中捕获输出音频流,可以在后台脚本中使用chrome.tabCapture.capture API获取流。但是,在清单v3中,tabCapture已经移动到前景,并且在后台脚本中不可用。它在内容脚本中也不可用。
尝试了一些方法后,我终于让tabCapture在弹出窗口脚本中工作。但是,在我的用例中,弹出窗口无法长时间保持打开状态以便捕获流。相反,我偶然发现了tabCapturegetMediaStreamId方法,它给我一个streamId。所以,我想出了这个解决方案:

chrome.tabs.query({
  active: true,
  currentWindow: true
}, (tabs) => {
  var tab = tabs[0];
  chrome.tabCapture.getMediaStreamId({consumerTabId: tab.id}, (streamId) => {
    chrome.tabs.sendMessage(tab.id, { action: 'streamId', streamId: streamId });
  });
});

在上述位于popup.js文件中的脚本中,获取流ID后,我将其发送到内容脚本。 我现在卡在如何基于此streamId创建流以便可以捕获/记录该流。 文档提到使用getUserMedia(),但我在那里没有成功。 这是我通过其他SO问题的一点帮助得出的结果(但它不起作用,我收到此错误DOMException:Error starting tab capture):

navigator.mediaDevices.getUserMedia({
  audio: {
    mandatory: {
      chromeMediaSource: 'tab',
      chromeMediaSourceId: streamId
    }
  }
})

我想了解如何在清单v3中使tabCapture正常工作。


在网页上添加一个扩展iframe(请参见web_accessible_resources),并捕获其中的音频。 - wOxxOm
如果网站正在显示流并且您想要记录它,则有一个API可以实现此操作;您可以克隆该流并通过画布呈现它,然后按照您的喜好进行修改。 - BGPHiJACK
@BGPHiJACK 不,该网站没有显示流。 - Rahul Sharma
嗨@wOxxOm,我尝试在iframe中打开popup.html。但是一旦调用tabCapture.capture API,我就会收到这样的错误:Extension has not been invoked for the current page,这很明显,因为iframe的src是chrome-extension://<extension_runtime_id>/popup.html。您能否提供更多有关您建议的方式的详细信息? - Rahul Sharma
@wOxxOm,我尝试了几个方法,但是我无法实现这个。您能否请指向任何示例/代码片段? - Rahul Sharma
显示剩余2条评论
2个回答

0
这是一个在扩展弹出窗口中捕获音频的解决方案,而不是服务工作者。

manifest.json

{
    "name": "Audio Share",
    "version": "0.1",
    "manifest_version": 3,
    "permissions": [
        "tabs",
        "activeTab",
        "tabCapture"
    ],
    "host_permissions": [
        "https://*/"
    ],
    "action": {
        "default_popup": "home.html",
        "default_action": "home.js"
    }
}

home.js

function saveToFile(blob, name) {
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    a.href = url;
    a.download = name;
    a.click();
    URL.revokeObjectURL(url);
    a.remove();
}

function captureTabAudio() {
    chrome.tabCapture.capture({audio: true, video: false}, (stream) => {

        // these lines enable the audio to continue playing while capturing
        context = new AudioContext();
        var newStream = context.createMediaStreamSource(stream);
        newStream.connect(context.destination);

        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);
    })
}

document.addEventListener('DOMContentLoaded', function () {
    document.getElementById("share-audio-button").addEventListener("click", function ()
        captureTabAudio();
    })
});

home.html

<html>
    <head>
        <script src="home.js"></script>
    </head>

    <body>
        <h1>Audio Share</h1>
        <button id="share-audio-button">Share Audio</button>
    </body>
</html>

你的代码可以正常工作,但你忘记提到它“只有在弹出窗口保持打开状态时才能正常工作”。这意味着不能点击扩展程序弹出窗口外的任何内容。 - Leo Napoleon
是的,说得好。我没有深入研究其他解决方法。我不确定MV3是否试图出于某种原因限制这种用例?还是我还没有弄清楚应该如何处理它。我想我为了我的目的回到了MV2。 - RJaus

0
sidePanel API 是今年发布的最令人兴奋的功能。与弹出窗口不同,它保持打开状态并且可以访问所有的 Chrome API。

根据目前的写法,你的回答不够清晰。请编辑以添加更多细节,帮助其他人理解这如何回答所提出的问题。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接