【问题标题】:Chrome extension Can't play meeting audioChrome 扩展程序无法播放会议音频
【发布时间】:2022-01-13 22:10:16
【问题描述】:

我正在使用 Manifest V2 开发一个 google chrome 扩展

扩展的目标是使用户能够在浏览器中开始在线会议

我使用 react 开发页面,然后将这些页面注入到当前打开的网站内的 iFrame 中

我可以捕获用户的音频/视频,并且可以成功发送 我收到来自其他加入会议的用户的音频和视频流,我可以播放他们的视频。

问题出在音频上,我收到了流,但没有播放

我试过了

  • 在播放视频的视频播放器中
  • 在与视频相同位置的<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 来管理通话

【问题讨论】:

    标签: javascript reactjs google-chrome-extension


    【解决方案1】:

    我去年用 Daily 和 manifest v2 构建了类似的东西(顺便说一下,我在 Daily 工作,所以希望我能帮助解决这个问题!)这是参与者 Tile React 组件,我用来参考:https://github.com/daily-demos/daily-notion-chrome-extension/blob/main/src/pages/Content/components/Tile.jsx

    查看您的代码,没有什么特别明显的东西对我来说很突出。. 有没有我可以查看的公共回购?您可能需要收听的不仅仅是participant-joined Daily 事件。你还有participant-updated的事件监听器吗?

    【讨论】:

    • 还有一件事:您可能已经知道这一点,但 Chrome 商店不再支持 manifest v2 (developer.chrome.com/docs/extensions/mv3/mv2-sunset)。如果这只是一个个人项目也没关系,但如果不是,请注意。
    • 我相信我的代码基于你的代码,因为我使用日常概念扩展作为反应的基础。顺便说一句,很棒的工作。 - 我怀疑它与在框架中运行调用以及扩展的隔离环境有关,但找不到证明或反驳它的方法。 -- daily-notion 扩展在 ContentScript 的上下文中运行,我不能这样做,因为这意味着我必须在页面更改时断开连接并重新连接。 -- 感谢关于 MV2 的链接,由于多种原因,我似乎需要重新评估该项目的可行性
    • 啊,好吧。很高兴代码很有帮助.. 不高兴 Chrome 商店正在淘汰 v2!我们将在 Daily 发布一些即将发布的内容,这应该会使 manifest v3 与我们的库兼容,但目前 manifest 3 是不兼容的。 :( 他们对 CSP 真的很严格。
    猜你喜欢
    • 1970-01-01
    • 2018-03-25
    • 2011-08-30
    • 1970-01-01
    • 2019-11-05
    • 2017-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多