Chrome 分机无法播放会议音频
Chrome extension Can't play meeting audio
我正在使用 Manifest V2
开发 google chrome 扩展
扩展的目标是使用户能够在浏览器中开始在线会议
我使用 React 开发页面,然后将这些页面注入到当前打开的网站内的 iFrame 中
我可以抓到用户的audio/video,我可以发送成功
我收到来自其他加入会议的用户的音频和视频流,我可以播放他们的视频。
问题出在音频上,我收到流,但无法播放
我试了一下
- 在播放视频的视频播放器中
- 在与视频相同位置的
<audio>
标签中
- 在用户点击的第一个 iframe 的
<audio>
标记中
- 通过在用户点击开始会议按钮后播放“silence.mp3”文件,然后添加
<audio>
标签,甚至使用 js (new Audio())
- 在后台脚本中
在所有这些情况下,我都无法播放流式传输的音频
我什至没有在控制台中收到错误消息
我注意到的一件事是,我可以播放我自己的音频(回声我的麦克风输入),如果我将它添加到一个 <audio>
标签并且控件可见,它就会开始计数。但是当我将与会者的音轨放在 <audio>
标签上时,控件停留在 0:00
这是 VideoPlayer.jsx
的代码
import React, { useEffect, useRef, useState } from 'react';
import msg from '../../lib/msg';
import { Flex } from '@chakra-ui/react';
const VideoPlayer = (props) => {
const { sessionId } = props;
const [participants, setParticipants] = useState([]);
const [currentParticipant, setCurrentParticipant] = useState(null);
const [videoTrack, setVideoTrack] = useState(null);
const [audioTrack, setAudioTrack] = useState(null);
const [isLocal, setIsLocal] = useState(false);
useEffect(() => {
if (currentParticipant) {
if (currentParticipant.videoTrack) {
setVideoTrack(currentParticipant.videoTrack);
} else {
setVideoTrack(null);
}
//set the audio track regardless if it's allowed or not, that's a decision for another function
if (currentParticipant.audioTrack) {
setAudioTrack(currentParticipant.audioTrack);
} else {
setAudioTrack(null);
}
}
}, [currentParticipant]);
const videoEl = useRef(null);
const audioEl = useRef(null);
/**
* Set the video element's source
*/
useEffect(() => {
if (!videoEl.current || !videoTrack) return;
videoEl.current.srcObject = new MediaStream([videoTrack]);
}, [videoEl, videoTrack]);
/**
* Set the audio element's source
*/
useEffect(() => {
if (!audioEl.current || !audioTrack || isLocal) return;
audioEl.current.srcObject = new MediaStream([audioTrack]);
}, [audioEl, isLocal, audioTrack]);
useEffect(() => {
const reloadParticipants = () => {
chrome.runtime.getBackgroundPage(function (win) {
var p = win.getParticipants();
setParticipants(p);
});
};
const updateTracks = async (message, sender, sendResponse) => {
chrome.runtime.getBackgroundPage(function (win) {
var participantList = win.getParticipants();
var index = participantList.findIndex(
(item) => item.session_id === sessionId
);
var participant = participantList[index];
setCurrentParticipant(participant);
setIsLocal(participant.local);
});
};
const msgListeners = [
{ action: msg.broadcast.participantListUpdated, fn: reloadParticipants },
{ action: msg.broadcast.tracksUpdated, fn: updateTracks },
];
const initMessageListeners = async () => {
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
msgListeners.forEach(async (item) => {
if (message.action === item.action) {
await item.fn(message, sender, sendResponse);
}
});
});
};
initMessageListeners();
reloadParticipants();
}, [sessionId]);
return (
<div className="tile-container">
<audio autoPlay playsInline ref={audioEl} />
<video
autoPlay
muted
playsInline
ref={videoEl}
style={{
width: '100%',
height: '100%',
position: 'absolute',
objectFit: 'cover',
}}
/>
</div>
);
};
export default VideoPlayer;
然后我用webpack打包成一个页面
import React from 'react';
import { render } from 'react-dom';
import VideoPlayer from './VideoPlayer';
import './index.css';
render(<VideoPlayer />, window.document.querySelector('#app-container'));
我使用这段代码从内容脚本中注入这个
// at top I have the variable
const videoPlayerUrl = chrome.runtime.getURL('/videoPlayer.html');
// I monitor for an event "participant joined" then call the following function
const createParticipantOverlay = (item) => {
var participantContainer = $(`
<div id="participant-${item.session_id}" class="bm_videoContainer">
<iframe
id="bm_ownerVideoFrame"
class="bm_videoFrame"
allow="autoplay *"
src="${videoPlayerUrl}?session_id=${item.session_id}"
></iframe>
<div id="bm_dragHandler" class="bm_dragHandler"></div>
</div>`);
var container = $('#meeting-container');
container.append(participantContainer);
var dragHandler = $(`#participant-${item.session_id}`).find(
'#bm_dragHandler'
);
dragHandler.on('mousedown', mouseDown);
dragHandler.on('mouseup', mouseUp);
dragHandler.on('mousemove', mouseMove);
};
注意:我正在使用 daily.co 来管理通话
我去年用 Daily 和清单 v2 构建了类似的东西(顺便说一句,我在 Daily 工作,所以希望我能帮助解决这个问题!)这是参与者 Tile
我以前得到的 React 组件此工作供参考:https://github.com/daily-demos/daily-notion-chrome-extension/blob/main/src/pages/Content/components/Tile.jsx
看看你的代码,没有什么特别明显的地方让我印象深刻。有没有 public 回购我可以看看?您可能需要收听的不仅仅是 participant-joined
每日活动。您是否也有 participant-updated
的事件侦听器?
我正在使用 Manifest V2
开发 google chrome 扩展扩展的目标是使用户能够在浏览器中开始在线会议
我使用 React 开发页面,然后将这些页面注入到当前打开的网站内的 iFrame 中
我可以抓到用户的audio/video,我可以发送成功 我收到来自其他加入会议的用户的音频和视频流,我可以播放他们的视频。
问题出在音频上,我收到流,但无法播放
我试了一下
- 在播放视频的视频播放器中
- 在与视频相同位置的
<audio>
标签中 - 在用户点击的第一个 iframe 的
<audio>
标记中 - 通过在用户点击开始会议按钮后播放“silence.mp3”文件,然后添加
<audio>
标签,甚至使用 js (new Audio()) - 在后台脚本中
在所有这些情况下,我都无法播放流式传输的音频
我什至没有在控制台中收到错误消息
我注意到的一件事是,我可以播放我自己的音频(回声我的麦克风输入),如果我将它添加到一个 <audio>
标签并且控件可见,它就会开始计数。但是当我将与会者的音轨放在 <audio>
标签上时,控件停留在 0:00
这是 VideoPlayer.jsx
的代码import React, { useEffect, useRef, useState } from 'react';
import msg from '../../lib/msg';
import { Flex } from '@chakra-ui/react';
const VideoPlayer = (props) => {
const { sessionId } = props;
const [participants, setParticipants] = useState([]);
const [currentParticipant, setCurrentParticipant] = useState(null);
const [videoTrack, setVideoTrack] = useState(null);
const [audioTrack, setAudioTrack] = useState(null);
const [isLocal, setIsLocal] = useState(false);
useEffect(() => {
if (currentParticipant) {
if (currentParticipant.videoTrack) {
setVideoTrack(currentParticipant.videoTrack);
} else {
setVideoTrack(null);
}
//set the audio track regardless if it's allowed or not, that's a decision for another function
if (currentParticipant.audioTrack) {
setAudioTrack(currentParticipant.audioTrack);
} else {
setAudioTrack(null);
}
}
}, [currentParticipant]);
const videoEl = useRef(null);
const audioEl = useRef(null);
/**
* Set the video element's source
*/
useEffect(() => {
if (!videoEl.current || !videoTrack) return;
videoEl.current.srcObject = new MediaStream([videoTrack]);
}, [videoEl, videoTrack]);
/**
* Set the audio element's source
*/
useEffect(() => {
if (!audioEl.current || !audioTrack || isLocal) return;
audioEl.current.srcObject = new MediaStream([audioTrack]);
}, [audioEl, isLocal, audioTrack]);
useEffect(() => {
const reloadParticipants = () => {
chrome.runtime.getBackgroundPage(function (win) {
var p = win.getParticipants();
setParticipants(p);
});
};
const updateTracks = async (message, sender, sendResponse) => {
chrome.runtime.getBackgroundPage(function (win) {
var participantList = win.getParticipants();
var index = participantList.findIndex(
(item) => item.session_id === sessionId
);
var participant = participantList[index];
setCurrentParticipant(participant);
setIsLocal(participant.local);
});
};
const msgListeners = [
{ action: msg.broadcast.participantListUpdated, fn: reloadParticipants },
{ action: msg.broadcast.tracksUpdated, fn: updateTracks },
];
const initMessageListeners = async () => {
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
msgListeners.forEach(async (item) => {
if (message.action === item.action) {
await item.fn(message, sender, sendResponse);
}
});
});
};
initMessageListeners();
reloadParticipants();
}, [sessionId]);
return (
<div className="tile-container">
<audio autoPlay playsInline ref={audioEl} />
<video
autoPlay
muted
playsInline
ref={videoEl}
style={{
width: '100%',
height: '100%',
position: 'absolute',
objectFit: 'cover',
}}
/>
</div>
);
};
export default VideoPlayer;
然后我用webpack打包成一个页面
import React from 'react';
import { render } from 'react-dom';
import VideoPlayer from './VideoPlayer';
import './index.css';
render(<VideoPlayer />, window.document.querySelector('#app-container'));
我使用这段代码从内容脚本中注入这个
// at top I have the variable
const videoPlayerUrl = chrome.runtime.getURL('/videoPlayer.html');
// I monitor for an event "participant joined" then call the following function
const createParticipantOverlay = (item) => {
var participantContainer = $(`
<div id="participant-${item.session_id}" class="bm_videoContainer">
<iframe
id="bm_ownerVideoFrame"
class="bm_videoFrame"
allow="autoplay *"
src="${videoPlayerUrl}?session_id=${item.session_id}"
></iframe>
<div id="bm_dragHandler" class="bm_dragHandler"></div>
</div>`);
var container = $('#meeting-container');
container.append(participantContainer);
var dragHandler = $(`#participant-${item.session_id}`).find(
'#bm_dragHandler'
);
dragHandler.on('mousedown', mouseDown);
dragHandler.on('mouseup', mouseUp);
dragHandler.on('mousemove', mouseMove);
};
注意:我正在使用 daily.co 来管理通话
我去年用 Daily 和清单 v2 构建了类似的东西(顺便说一句,我在 Daily 工作,所以希望我能帮助解决这个问题!)这是参与者 Tile
我以前得到的 React 组件此工作供参考:https://github.com/daily-demos/daily-notion-chrome-extension/blob/main/src/pages/Content/components/Tile.jsx
看看你的代码,没有什么特别明显的地方让我印象深刻。有没有 public 回购我可以看看?您可能需要收听的不仅仅是 participant-joined
每日活动。您是否也有 participant-updated
的事件侦听器?