将实时音频流传输到Node.js服务器

4

我正在进行一个项目,需要将音频流发送到Node.js服务器。 我能够使用以下功能捕获麦克风声音:

function micCapture(){
    'use strict';

    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

    var constraints = {
        audio: true,
        video: false
    };

    var video = document.querySelector('video');

    function successCallback(stream) {
        window.stream = stream; // stream available to console
        if (window.URL) {
            video.src = window.webkitURL.createObjectURL(stream);
        } else {
            video.src = stream;
        }
        //Send audio stream
        //server.send(stream);
    }

    function errorCallback(error) {
        console.log('navigator.getUserMedia error: ', error);
    }

    navigator.getUserMedia(constraints, successCallback, errorCallback);
}

如您所见,我能够在网站上捕获音频并播放。

现在我想将该音频流发送到Node.js服务器,并将其发送回其他客户端。 就像语音聊天一样,但我不想使用WebRTC,因为我需要在服务器中使用该流。 我该如何实现这一点? 我可以使用socket.io-stream吗? 在我看到的示例中,他们录制了音频并发送了文件,但我需要“实时”音频。


是的,您确实可以使用WebSockets从客户端流式传输音频到服务器...我建议您先编写一些代码,然后再提出具体可回答的编程问题...如上所述的代码并不针对您的套接字问题。 可以这样翻译:是的,您肯定可以使用WebSockets将音频从客户端流式传输到服务器...我建议您先编写一些代码,然后再提出具体可回答的编程问题...如上所述的代码并不针对您的套接字问题。 - Scott Stensland
1
谢谢您的建议。我会编写代码并更新问题。 - JCAguilera
你在这方面取得了什么好的进展吗? - chris
我最终尝试了WebRTC,但最终放弃了这个项目。 - JCAguilera
2个回答

4

我最近使用socket.io从浏览器向服务器实时上传音频。我会在这里回答,以便其他人需要时可以参考。

    var stream;
    var socket = io();
    var bufferSize = 1024 * 16;
    var audioContext = new AudioContext();
    // createScriptProcessor is deprecated. Let me know if anyone find alternative
    var processor = audioContext.createScriptProcessor(bufferSize, 1, 1);
    processor.connect(audioContext.destination);

    navigator.mediaDevices.getUserMedia({ video: false, audio: true }).then(handleMicStream).catch(err => {
      console.log('error from getUserMedia', err);
    });

handleMicStream函数将在用户允许使用麦克风权限后运行。

  function handleMicStream(streamObj) {
    // keep the context in a global variable
    stream = streamObj;

    input = audioContext.createMediaStreamSource(stream);

    input.connect(processor);

    processor.onaudioprocess = e => {
      microphoneProcess(e); // receives data from microphone
    };
  }


  function microphoneProcess(e) {
    const left = e.inputBuffer.getChannelData(0); // get only one audio channel
    const left16 = convertFloat32ToInt16(left); // skip if you don't need this
    socket.emit('micBinaryStream', left16); // send to server via web socket
  }

// Converts data to BINARY16
function convertFloat32ToInt16(buffer) {
    let l = buffer.length;
    const buf = new Int16Array(l / 3);

    while (l--) {
      if (l % 3 === 0) {
        buf[l / 3] = buffer[l] * 0xFFFF;
      }
    }
    return buf.buffer;
  }



让你的Socket.IO服务器监听 micBinaryStream ,你就可以获得数据了。如果你不需要将其转换为Google API支持的 BINARY16 格式,你可以跳过调用函数 convertFloat32ToInt16()

重要提示

当你需要停止监听时,你必须断开处理器并结束流。运行下面的函数closeAll()

function closeAll() {
    const tracks = stream ? stream.getTracks() : null;
    const track = tracks ? tracks[0] : null;

    if (track) {
      track.stop();
    }

    if (processor) {
      if (input) {
        try {
          input.disconnect(processor);
        } catch (error) {
          console.warn('Attempt to disconnect input failed.');
        }
      }
      processor.disconnect(audioContext.destination);
    }

    if (audioContext) {
      audioContext.close().then(() => {
        input = null;
        processor = null;
        audioContext = null;
      });
    }
  }


你之前在使用这段代码来调用Google语音转文字服务吗? - Shubham
是的,我认为我的做法是将麦克风上传至Google API并获取文字。我还在我的GitHub存储库中编写了代码。如果需要的话,我可以在这里提供链接。 - Sisir
谢谢你的帮助,伙计。上面的代码在 EJS 上运行得非常好,但是在使用 Angular 时有一些问题。如果可能的话,请添加仓库链接,这可能会帮助其他用户。在我解决 Angular 的问题之前,请耐心等待。 - Shubham
1
看起来ScriptProcessorNode已经被audioWorkletNode替换了,但是它是一种不同的架构,可以将音频从主UI线程中分离出来,这需要一些实验...请参见https://developer.chrome.com/blog/audio-worklet/。 - Dr. Aaron Dishno
1
这里是文档:https://googlechromelabs.github.io/web-audio-samples/audio-worklet/ - Dr. Aaron Dishno
1
我找到了一个关于 audioWorkletNode 的示例,演示了如何显示麦克风输入音量。https://dev59.com/OFIG5IYBdhLWcg3w2VMZ - Dr. Aaron Dishno

1

这是一个老问题,我看到了。我正在做同样的事情(除了我的服务器不运行node.js并且是用C#编写的),并且遇到了这个问题。

不知道是否仍然有人感兴趣,但我进行了一些详细的解释。目前替代已弃用的createScriptProcessor的是AudioWorklet接口。

来自:https://webaudio.github.io/web-audio-api/#audioworklet

1.32.1. 概念

AudioWorklet对象允许开发人员提供脚本(例如JavaScript或WebAssembly代码)以在渲染线程上处理音频,支持自定义AudioNodes。该处理机制确保脚本代码与音频图中其他内置AudioNodes的同步执行。

据我所知,您无法在JavaScript中实现接口,但可以扩展从中派生的类。

我们需要的是:https://developer.mozilla.org/en-US/docs/Web/API/AudioWorkletProcessor

所以,我编写了一个处理器,它只是将输出与输入值进行镜像,并显示它们。
class CustomAudioProcessor extends AudioWorkletProcessor {
    process (inputs, outputs, parameters) {
        const input = inputs[0];
        const output = output[0];
        for (let channel = 0; channel < input.length; ++channel) {   
            for (let i = 0; i < input[channel].length; ++i) {
            // Just copying all the data from input to output
            output[channel][i] = input[channel][i];
            // The next one will make the app crash but yeah, the values are there
            // console.log(output[channel][i]);
            }
        }
    }
}

处理器必须放置在音频管道中,麦克风之后,扬声器之前。
function record() {

constraints = { audio: true };
navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
   audioCtx = new AudioContext();
    var source = audioCtx.createMediaStreamSource(stream);
    audioCtx.audioWorklet.addModule("custom-audio-processor.js").then(() => {
        customAudioProcessor = new AudioWorkletNode(audioCtx, "custom-audio-processor");
        source.connect(customAudioProcessor);
        customAudioProcessor.connect(audioCtx.destination);
    }) 

    audioCtx.destination.play();

工作正常!祝你好运!:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接