如何在javascript Web客户端中播放PCM流(通过UDP)?

时间:2019-07-12 10:56:54

标签: javascript audio client streaming

我正在编写一个JavaScript Web客户端,并且我想播放客户端中UDP流(原始RTP)中编码为PCM的音频(我可以自由更改流的目标主机和端口)。怎么办?

我阅读了有关Howler的文章,但找不到任何UDP侦听选项或PCM支持。我也看过WebRTC,但它似乎不支持PCM(如果我正确理解的话)。

非常感谢!

1 个答案:

答案 0 :(得分:0)

所以我终于做到了! 我写了一个小python服务器将UDP转换为websocket。 我是用Quart编写的,它就像一个异步烧瓶,目的是使websocket和UDP选择更加容易:

import json
import struct
import socket
import asyncio
from quart import Quart, websocket

app = Quart(__name__)


class PcmServerProtocol:
    def __init__(self, queue: asyncio.Queue):
        self._queue = queue

    def connection_made(self, transport):
        self.transport = transport

    def datagram_received(self, data, addr):
        pcm_chunks = struct.unpack(
            # Convert the data to pulses
            "<" + "h" * (len(data) // struct.calcsize("h")),
            data
        )
        self._queue.put_nowait(json.dumps(pcm_chunks))


async def sending(queue: asyncio.Queue):
    while True:
        data = await queue.get()
        await websocket.send(data)


@app.websocket("/sound")
async def sound():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind(("127.0.0.1", 15000))
    pcm_queue = asyncio.Queue()
    streamer = asyncio.create_task(sending(pcm_queue))
    pcm_receiver = asyncio.create_task(
        asyncio.get_event_loop().create_datagram_endpoint(
            lambda: PcmServerProtocol(pcm_queue), sock=sock
        )
    )
    await asyncio.gather(streamer, pcm_receiver)


if __name__ == "__main__":
    app.run()

现在我们所需要做的就是连接网络插座并播放javascript的声音 为此,我使用了Web Audio API:

let audioCtx = new AudioContext();
let ws = new WebSocket('ws://' + document.domain + ':' + location.port + '/sound');
ws.onmessage = function (event) {
    let pcm_chunks = JSON.parse(event.data);
    let node = audioCtx.createBufferSource();
    let buffer = audioCtx.createBuffer(1, pcm_chunks.length, 44100);
    let data = buffer.getChannelData(0);
    for (let i = 0; i < pcm_chunks.length; i++) {
        // Normalize to between -1.0 and 1.0, my PCM was signed 16bit
        data[i] = pcm_chunks[i] / 32768;
    }
    node.buffer = buffer;
    node.loop = false;
    node.connect(audioCtx.destination);
    node.start(0);
};

我发现的一个缺点是,即使在localhost上运行时声音完美,通过网络进行操作也会使声音滞后。

很显然,我们非常欢迎您发表评论并提出其他想法。