我知道标题可能有点令人困惑,所以让我给出一些背景。
我正在写一个扩展做一些音频操作与谷歌见面,并且,在研究它的行为后,我发现了一个奇怪的问题,我似乎不能把我的头围绕。
Google Meet似乎使用三个<audio>
元素来播放音频,每个元素都有自己的MediaStreams。经过一些测试,似乎:
- 静音
<audio>
元素停止谷歌会议的音频可视化,谁在说话。 - 交换两个音频元素的
.srcObject
属性,然后对它们调用.play()
不影响Google Meet的音频可视化。
这些似乎指向Google Meet将源MediaStream连接到其音频处理图中以创建可视化,而不是捕获<audio>
元素,因为我可以在不影响可视化的情况下交换MediaStream。
然而,考虑到过去的信息,我注意到的另一件事似乎没有意义:
- 从
<audio>
元素的.srcObject
添加一个新的MediaStreamAudioSourceNode
并将其连接到AnalyserNode
显示,即使我静音<audio>
元素,我仍然可以分析通过MediaStream
播放的音频。
下面是一些示例代码和通过浏览器控制台完成的输出:
ac = new AudioContext();
an = ac.createAnalyser()
sn = ac.createMediaStreamSource(document.querySelectorAll("audio")[0].srcObject)
sn.connect(an)
function analyse(aNode) {
const ret = new Float32Array(aNode.frequencyBinCount);
aNode.getFloatTimeDomainData(ret);
return ret;
}
analyse(an)
// > Float32Array(1024) [ 0.342987060546875, 0.36688232421875, 0.37115478515625, 0.362457275390625, 0.35150146484375, 0.3402099609375, 0.321075439453125, 0.308746337890625, 0.29779052734375, 0.272552490234375, … ]
document.querySelectorAll("audio")[0].muted = true
analyse(an)
// > Float32Array(1024) [ -0.203582763671875, -0.258026123046875, -0.31134033203125, -0.34375, -0.372802734375, -0.396484375, -0.3919677734375, -0.36328125, -0.31689453125, -0.247650146484375, … ]
// Here, I mute the microphone on *my end* through Google Meet.
analyse(an)
// > Float32Array(1024) [ -0.000030517578125, 0, 0, -0.000030517578125, -0.000091552734375, -0.000091552734375, -0.000091552734375, -0.00006103515625, 0, 0.000030517578125, … ]
// The values here are much closer to zero.
可以看到,当音频元素被静音时,AnalyserNode仍然可以拾取音频,但是Meet的可视化中断了。这就是我不明白的地方。这怎么可能呢?
当<audio>
元素被静音时,连接的AnalyserNode如何不中断,但其他东西是,不使用.captureStream()
?
另一个奇怪的事情是,它只发生在Chrome. 在Firefox上,即使音频元素是静音的,Meet的可视化效果也很好。我认为这可能与已知的Chrome问题有关,其中MediaStreams需要播放<audio>
元素来输出任何音频图形(https://stackoverflow.com/a/55644983),但我看不出这会如何影响静音<audio>
元素。
这有点令人困惑,但AudioElement.captureStream()
的行为实际上不同于使用MediaElementAudioSourceNode
。
new MediaStreamAudioSourceNode(audioContext, audioElement.captureStream());
// is not equal to
new MediaElementAudioSourceNode(audioContext, audioElement);
调用AudioElement.captureStream()
获得的流不受音频元素上任何音量变化的影响。调用AudioElement.captureStream()
也不会改变音频元素本身的音量。
然而,使用MediaElementAudioSourceNode
会将音频元素的音频重新路由到AudioContext
。音频将受到对音频元素所做的任何音量更改的影响。这意味着对音频元素进行静音将导致对传入AudioContext
的音频进行静音。
在此之上,使用MediaElementAudioSourceNode
将使audio元素本身静音。
我假设Google Meet为每个音频元素使用MediaElementAudioSourceNode
来处理音频。