为啥对方接听电话后会自动播放音频?

Posted

技术标签:

【中文标题】为啥对方接听电话后会自动播放音频?【英文标题】:Why does audio automatically play after a peer accepts the call?为什么对方接听电话后会自动播放音频? 【发布时间】:2022-01-16 15:01:06 【问题描述】:

当我有两个使用 peerjs 互相呼叫的对等方时,一旦对等方接受呼叫,即使我没有明确设置自动播放或调用 play(),来自发起呼叫的对等方的音频也会开始播放。这个问题出现在chrome浏览器中。

<!DOCTYPE html>
<html>
    <head>
        <script src="https://unpkg.com/peerjs@1.3.1/dist/peerjs.min.js"></script>
    </head>
    <body>
        <h1 id='peerid'></h1>
        <input type='text' id='in'/>
        <button onclick="call()">Call</button>
        <button onclick="debug()">debug</button>
        <button onclick="printtime()">time</button>
        <button onclick="printauto()">auto</button>
    </body>
        <script type='text/javascript'>
            let a = new Audio();
            a.onplay = () => 
                console.log('play');
            ;
            a.onplaying = () => 
                console.log('playing');
            ;
            a.onseeking = () => 
                console.log('seeking');
            ;
            a.onseeked = () => 
                console.log('seeked');
            ;
            let m = new MediaStream();
            a.srcObject = m;
            let header = document.getElementById('peerid');
            let i = document.getElementById('in');
            let peer = new Peer();
            peer.on('open', (id) => 
                header.innerHTML = id;
            );
            peer.on('call', (call) => 
                call.answer();
                call.on('stream', (stream) => 
                    stream.getAudioTracks().forEach(track => m.addTrack(track));
                );
            );
            let call = () => 
                navigator.mediaDevices.getUserMedia( audio: true ).then(mediaStream => 
                    let call = peer.call(i.value, mediaStream);
                    call.on('stream', (stream) => 
                        stream.getAudioTracks().forEach(track => m.addTrack(track));
                    );
                , rejected => 
                    console.log('rejected');
                ).catch(e => console.error(e));
            ;
            let debug = () => 
                console.log(a.paused);
            ;
            let printtime = () => 
                console.log(a.currentTime);
            ;
            let printauto = () => 
                console.log(a.autoplay);
            ;
        </script>
</html>

为了复制这个,

    使用网络服务器提供上述 html 代码(例如:python3 -m http.server) 打开两个为此页面提供服务的标签 将粗体标识符从一个选项卡复制到另一个选项卡,然后单击“调用” 它应该要求访问您的麦克风(这可能需要一段时间,所以请稍等,或者只是向通话按钮发送垃圾邮件) 一旦它可以访问您的麦克风,另一个选项卡应自动播放音频(音频将是您自己的声音),而音频元素的自动播放为 true 或在您可以看到的任何地方调用 play() 李>

我不知道为什么会这样。如果您打印音频是否暂停或自动播放已打开,它们都会打印暂停为真(即使音频仍在播放)和自动播放为假(即使音频仍在播放)。也没有任何事件触发。这是铬错误吗?还是我只是愚蠢? (我假设是后者)

【问题讨论】:

这是一个 Chrome 错误,here is an MCVE。奇怪的是,使用你的代码我无法在稳定的 96 上重现,但可以在最新的 98 上重现,而我的 min-repro 也为我在稳定的 96 上重现(全部在 macOS 上)。我会再调查一下,几个小时后回来。 我的 chrome 版本是 Version 96.0.4664.93 (Official Build) (64-bit) 我在 windows 11 上。 有点奇怪,我的马厩无法重现它,我可以在平分的同时重现更旧的版本,显然在同一台机器上......好吧无论如何,我打开了一个问题并在下面给了你一个解决方法.`` 【参考方案1】:

这是一个 Chrome 错误,我打开了https://crbug.com/1279299。 该错误的要点是,在设置为 HTMLAudioElement 的srcObject 的 MediaSource 上添加音轨将强制自动播放该 MediaStream,即使该元素实际上从未告诉它...

因此可以像这样进行较小的复制:

const a = new Audio();
const m = new MediaStream();
a.srcObject = m;
a.volume = 0.1; // volume still works...

onclick = e => 
  const ctx = new AudioContext();
  const osc = ctx.createOscillator();
  const out = ctx.createMediaStreamDestination();
  osc.connect(out);
  osc.start(0);
  out.stream.getAudioTracks().forEach(t => m.addTrack(t));
;
Click to start

知道了这一点,解决方法也很明显:不要将元素的 srcObject 设置为这个空的 MediaStream,直接将其设置为从对等点接收到的一个流:

call.on('stream', (stream) => 
  a.srcObject = stream;
);

【讨论】:

以上是关于为啥对方接听电话后会自动播放音频?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 iOS 应用会自动启动?

freeswitch 调试dialplan,自动接听,播放一段彩铃后开始录音,录音指定时间后自动挂机

为啥打对方电话打不通,显示用户正忙

在android中将音频注入语音流

jq页面加载事件,页面加载完调用音频自动播放怎么写?

微信网页ios自动播放音频、视频