如何使用来自网络套接字的网络音频 API 流式传输音频块?

Posted

技术标签:

【中文标题】如何使用来自网络套接字的网络音频 API 流式传输音频块?【英文标题】:how to stream audio chunks using web audio API coming from web-socket? 【发布时间】:2016-12-27 14:07:59 【问题描述】:

我正在通过服务器的 web-Socket 以块的形式流式传输音频数据

ws.on('message', function incoming(message) 
    var readStream = fs.createReadStream("angular/data/google.mp3",
        
            'flags': 'r',
            'highWaterMark': 128 * 1024
        
    );
    readStream.on('data', function(data) 
        ws.send(data);
    );

    readStream.on('end', function() 
        ws.send('end');
    );

    readStream.on('error', function(err) 
        console.log(err)
    );
);

在客户端

var chunks = [];
var context = new AudioContext();
var soundSource;

var ws = new WebSocket(url);
    ws.binaryType = "arraybuffer";

ws.onmessage = function(message) 
    if (message.data instanceof ArrayBuffer) 
        chunks.push(message.data)
     else 
        createSoundSource(chunks);
    
;

function createSoundSource(audioData) 
    soundSource = context.createBufferSource();

    for (var i=0; i < audioData.length;i++) 
        context.decodeAudioData(audioData[i], function(soundBuffer)
            soundSource.buffer = soundBuffer;
            soundSource.connect(context.destination);
            soundSource.start(0);
        );
    

但是第二次设置缓冲区soundSource.buffer = soundBuffer;会报错

未捕获的 DOMException:无法在“AudioBufferSourceNode”上设置“缓冲区”属性:缓冲区已设置后无法设置

任何关于如何最好地使用update Web Audio API 播放新音频数据的建议或见解将不胜感激。

【问题讨论】:

你搞清楚了吗? 【参考方案1】:

AudioBufferSourceNode 上的缓冲区一旦设置就无法重置。这就像一劳永逸。每次要播放不同的缓冲区时,都必须创建一个新的AudioBufferSourceNode 才能继续播放。这些是非常轻量级的节点,因此即使创建大量节点也不必担心性能。

为了解决这个问题,您可以修改您的 createSoundSource 函数,以简单地为循环体内的每个块创建一个 AudioBufferSourceNode,如下所示:

function createSoundSource(audioData) 
    for (var i=0; i < audioData.length;i++) 
        context.decodeAudioData(audioData[i], function(soundBuffer)
            var soundSource = context.createBufferSource();
            soundSource.buffer = soundBuffer;
            soundSource.connect(context.destination);
            soundSource.start(0);
        );
    

我试图让代码风格尽可能接近原始,但现在是 2020 年,利用现代特性的函数实际上可能看起来像这样:

async function createSoundSource(audioData) 
  await Promise.all(
    audioData.map(async (chunk) => 
      const soundBuffer = await context.decodeAudioData(chunk);
      const soundSource = context.createBufferSource();
      soundSource.buffer = soundBuffer;
      soundSource.connect(context.destination);
      soundSource.start(0);
    )
  );

如果您想在新数据到达后立即停止旧节点(看起来您希望通过重置 .buffer 但我不确定),您必须存储它们并调用 disconnect到了时候就全部上去。

【讨论】:

【参考方案2】:

不是肯定的,但我认为你必须以不同的方式处理你的流式 websocket 缓冲区。也许websocket-streaming-audio 包源代码可以为您提供有关如何处理您的场景的更好线索。

【讨论】:

以上是关于如何使用来自网络套接字的网络音频 API 流式传输音频块?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iOS swift 中使用 UDP 套接字流式传输音频?

如何减少延迟 - 来自网络摄像头的 VLC 流式传输

将音频流式传输到本地网络

通过 tcp 套接字流式传输 PCM 音频

从网络服务器流式传输音频

Qt 通过 TCP 套接字实时流式传输音频