You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Guacamole-common.js中使用AudioWorklet启用麦克风?

使用AudioWorkletNode结合Guacamole-common.js启用麦克风的问题排查与解决方案

问题描述

我正在用Guacamole-common.js在浏览器中创建远程桌面连接,尝试启用麦克风但一直失败。由于ScriptProcessorNode已被弃用,改用了AudioWorklet。麦克风权限已允许,但Windows远程桌面始终提示“未找到输入设备”。有没有人试过结合AudioWorkletNode与Guacamole-common.js启用麦克风?

原代码片段

const tunnel = new Guacamole.WebSocketTunnel(WEB_SOCKET_TUNNEL);
const client = new Guacamole.Client(tunnel);  

var audioStream = client.createAudioStream("audio/L16");
var writer = new Guacamole.ArrayBufferWriter(audioStream);

// Attempt to retrieve an audio input stream from the browser
navigator.mediaDevices.getUserMedia({ audio: true })
      .then(async function (stream) {

          const context = new AudioContext();
          const source = context.createMediaStreamSource(stream);

          // Load and execute the module script.
          await context.audioWorklet.addModule('worklet/processor.js');
          // Create an AudioWorkletNode. The name of the processor is the
          // one passed to registerProcessor() in the module script.

          const processor = new AudioWorkletNode(context, "processor");

          source.connect(processor).connect(context.destination);
       
          console.log("Microphone access allowed");

          const streamDestination = context.createMediaStreamDestination();
          processor.connect(streamDestination);

          const inputStream = new Guacamole.InputStream(streamDestination.stream);

          // Connect the audio recorder to the output stream
          processor.ondataavailable = function (blob) {
              console.log(blob, 'blob')
              inputStream.sendBlob(blob);
          };

          processor.addEventListener('processorerror', (event) => {
              console.log(`Audio processing error: ${event}`);
          });

          // Start the audio processing
          processor.addEventListener('audioprocess', (event) => {
              var inputBuffer = event.inputBuffer;
              var inputData = inputBuffer.getChannelData(0);
              // Write the audio data to the Guacamole audio input stream
              var bytes = new Int16Array(inputData.length);
              //console.log(bytes, 'bytes')
              for (var i = 0; i < inputData.length; i++) {
                  bytes[i] = inputData[i] * 32766;
              };
              writer.sendData(bytes.buffer);
          });

      })
      .catch(function (error) {
          console.log(error, 'error');
          console.log("Microphone access denied");
          writer.sendEnd();
      });

核心问题分析

  1. AudioWorklet事件误用:audioprocess事件属于已弃用的ScriptProcessorNode,AudioWorkletNode不支持该事件,音频处理逻辑必须写在Worklet处理器内部。
  2. 数据流冲突:同时通过writer.sendData和inputStream.sendBlob两种方式传输数据,会导致格式不匹配或数据混乱。
  3. 音频参数不匹配:Guacamole的audio/L16格式要求16位PCM单声道、固定采样率(通常为48kHz),原代码未明确指定这些参数。

修正后的解决方案

1. 编写正确的AudioWorklet处理器(worklet/processor.js)

在Worklet线程中完成音频格式转换,并通过消息端口将数据传递到主线程:

class AudioProcessor extends AudioWorkletProcessor {
  process(inputs, outputs, parameters) {
    const input = inputs[0];
    if (input.length === 0) return true;

    const channelData = input[0];
    // 将Float32格式(范围-1~1)转换为Int16格式(范围-32768~32767)
    const int16Data = new Int16Array(channelData.length);
    for (let i = 0; i < channelData.length; i++) {
      int16Data[i] = Math.max(-32768, Math.min(32767, channelData[i] * 32767));
    }

    // 传输数据到主线程,转移缓冲区所有权避免拷贝
    this.port.postMessage(int16Data.buffer, [int16Data.buffer]);
    return true;
  }
}

registerProcessor('processor', AudioProcessor);

2. 修正主线程代码

统一数据传输逻辑,明确指定音频参数匹配Guacamole要求:

const tunnel = new Guacamole.WebSocketTunnel(WEB_SOCKET_TUNNEL);
const client = new Guacamole.Client(tunnel);  

// 创建指定采样率的L16音频流
const audioStream = client.createAudioStream("audio/L16;rate=48000");
const writer = new Guacamole.ArrayBufferWriter(audioStream);

// 获取麦克风流,强制指定参数匹配Guacamole
navigator.mediaDevices.getUserMedia({ 
  audio: {
    sampleRate: 48000,
    channelCount: 1,
    echoCancellation: false,
    noiseSuppression: false
  } 
})
.then(async function (stream) {
  // 强制音频上下文采样率与Guacamole一致
  const context = new AudioContext({ sampleRate: 48000 });
  const source = context.createMediaStreamSource(stream);

  await context.audioWorklet.addModule('worklet/processor.js');
  const processor = new AudioWorkletNode(context, "processor");

  // 仅连接音频源到处理器,无需连接到本地输出(除非需要监听麦克风)
  source.connect(processor);

  console.log("麦克风权限已允许");

  // 接收Worklet传来的音频数据并发送给Guacamole
  processor.port.onmessage = (event) => {
    writer.sendData(event.data);
  };

  processor.port.onmessageerror = (event) => {
    console.log(`音频处理错误: ${event}`);
  };

})
.catch(function (error) {
  console.log(error, '错误');
  console.log("麦克风权限被拒绝");
  writer.sendEnd();
});

额外检查项

  • Guacamole服务端配置:确认guacd服务已启用音频输入支持,远程桌面连接配置中开启麦克风转发。
  • Windows远程桌面设置:本地连接时,在“本地资源”选项中勾选麦克风,远程会话内确认音频输入设备选择正确。
  • 浏览器兼容性:确保使用Chrome 66+、Firefox 76+等支持AudioWorklet的浏览器。

内容的提问来源于stack exchange,提问作者AmiS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 12:07:53