Web Audio API的 navigator.getUserMedia会返回来自错误设备的音频流

前端开发 2026-07-12

我正在尝试使用Web Audio API将一个正弦音播放到一个特定的音频输出设备。
(如果你感兴趣,使用场景是对连接到业余无线电的音频接口播放一个短促的 1750 Hz 音调,这个音频接口会“打开”中继器。)

为此,我首先使用:

navigator.mediaDevices.enumerateDevices()

来获取音频输出接口的列表并将其填充到一个下拉菜单中。

然后,在绑定到“运行”按钮的函数中,我将当前选中的 deviceId 作为约束应用于:

navigator.mediaDevices.getUserMedia()

以获取该特定音频设备的 MediaStream

之后,我:

  1. 创建一个 AudioContext 实例。
  2. 在该音频上下文中基于该流创建一个 MediaStreamDestinationNode
  3. 创建一个 OscillatorNode,并将其输出连接到目标设备。

然而,在第二步就已经遇到了问题。

据观察(在Chrome 145.0.7632.117、macOS Tahoe 26.3 上测试),在查看轨道标签时,MediaStream 的第一条也是唯一的一条轨道总是来自我MacBook的麦克风,而不是我所选择的扬声器设备。因此,我也听不到任何音频在播放。

我创建了一个最小可复现的片段来展示这个问题。

你需要打开控制台才能看到异常的轨道标签。你也可能需要手动授予 stacksnippets.net 麦克风和扬声器权限,因为Chrome并不总是会自动提示这些权限。

$(document).ready(function() {
  let enumeratorPromise = navigator.mediaDevices.enumerateDevices();
  enumeratorPromise.then((devices) => {
    console.log(devices);
    devices.forEach((device) => {
      console.log("Got device from enumerator", device);
      if (device.kind !== "audiooutput") return;
      let $deviceOption = $("<option>")
        .text(device.label)
        .val(device.deviceId)
        .appendTo($("#audioout"));
    });
  });
  $("#run").click(function() {
    let id = $("#audioout").val();
    navigator.mediaDevices
      .getUserMedia({
        audio: {
          deviceId: id
        }
      })
      .then(function(stream) {
        console.log(
          "got MediaStream for",
          id,
          stream,
          stream.getTracks(),
          stream.getTracks()[0].label
        );
        let audioContext = new AudioContext();
        let mediaStreamDestination = audioContext.createMediaStreamDestination(
          stream
        );

        audioContext.createOscillator();
        oscillatorNode.type = "sine";
        oscillatorNode.frequency.setValueAtTime(1750, audioContext.currentTime);
        console.log(mediaStreamDestination);
        oscillatorNode.connect(mediaStreamDestination);
        //oscillatorNode.connect(audioContext.destination);
        oscillatorNode.start();
      });
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
Select audio device: <select id="audioout"></select><br />
<button id="run">Play 1750 Hz</button>

解决方案

getUserMedia() 仅适用于输入设备(audioinput / videoinput)。它不能捕获一个 audiooutput 设备。

尽管 enumerateDevices() 会列出 audiooutput 设备,但那些是回放目标,而不是媒体源,因此将它们的 deviceId 传递给 getUserMedia() 并不会从该设备生成流。getUserMedia() 能返回的唯一音频轨道来自麦克风。

如果目标是通过特定输出设备播放音频,通常的做法是使用一个 <audio>/<video> 元素,并通过 HTMLMediaElement.setSinkId() 选择设备。

在实际操作中,常见的模式是:

  1. 用Web Audio生成音频。
  2. 将其路由到一个 MediaStreamDestination
  3. 将得到的流附加到一个 <audio> 元素(srcObject)。
  4. 使用 setSinkId() 选择输出设备。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章