google-chrome - 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:
- Background scripts are replaced by service workers.
- Service workers do not have access to the
chrome.tabCapture
API.
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:
- 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;
}
...
- 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
})
});
});
- 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
});
}
}
);
- 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.
解决方案
这可能不是您正在寻找的东西,但也许它可以提供一些见解。
我尝试过通过音频元素播放流,但这似乎无济于事。
具有讽刺意味的是,这就是我设法解决这个问题的方法;通过在弹出窗口本身中创建一个对象。在弹出脚本中使用 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 等的新命名空间,但除此之外我还没有看到任何东西。
推荐阅读
- javascript - React-admin:使用文档中显示的自定义登录页面时出错
- javascript - 当我选择开始日期和结束日期时,剑道日期范围弹出窗口没有关闭。(角度 9)
- flutter - 使用可重用的小部件我如何将状态传递给该小部件
- c# - 如何通过 asp.net core 获取当前计算机的域?
- python - 添加 MaxPooling 2D - ValueError:新数组的总大小必须保持不变
- elasticsearch - 我丢失了 kibana 数据,但其他弹性搜索索引数据很好
- python - 如何修复 Python 条形图中的图例颜色问题?
- python - 从对象数组中,打印仅包含一个对象属性的列表(Python)
- robotframework - 使用机器人框架重新运行测试套件并使用不同的登录信息进行骑行
- javascript - 为什么我的静态变量在分配后未定义?