为什么对方接听电话后自动播放音频?
Why does audio automatically play after a peer accepts the call?
当我有两个使用 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)
- 打开服务于此页面的两个选项卡
- 将粗体标识符从一个选项卡复制到另一个选项卡,然后单击“呼叫”
- 它应该要求访问您的麦克风(这可能需要一段时间,请稍等片刻,或者只是向通话按钮发送垃圾邮件)
- 一旦它可以访问您的麦克风,另一个选项卡应该会自动播放音频(音频将是您自己的声音),而音频元素不会自动播放为真或在您可以看到的任何地方调用 play()
我不知道为什么会这样。如果您在音频暂停或自动播放打开时打印出来,它们都会打印暂停为真(即使音频仍在播放)和自动播放为假(即使音频仍在播放)。 None 的事件都会触发。这是 chrome 错误吗?还是我只是愚蠢? (我假设是后者)
这是一个 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;
});
当我有两个使用 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)
- 打开服务于此页面的两个选项卡
- 将粗体标识符从一个选项卡复制到另一个选项卡,然后单击“呼叫”
- 它应该要求访问您的麦克风(这可能需要一段时间,请稍等片刻,或者只是向通话按钮发送垃圾邮件)
- 一旦它可以访问您的麦克风,另一个选项卡应该会自动播放音频(音频将是您自己的声音),而音频元素不会自动播放为真或在您可以看到的任何地方调用 play()
我不知道为什么会这样。如果您在音频暂停或自动播放打开时打印出来,它们都会打印暂停为真(即使音频仍在播放)和自动播放为假(即使音频仍在播放)。 None 的事件都会触发。这是 chrome 错误吗?还是我只是愚蠢? (我假设是后者)
这是一个 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;
});