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

标签 javascript audio client streaming rtp

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

我读到了有关 Howler 的内容,但找不到任何 UDP 监听选项或 PCM 支持。我还查看了 WebRTC,但它似乎不支持 PCM(如果我没猜错的话)。

非常感谢!

最佳答案

所以我终于做到了! 我写了一个小的 python 服务器来将 UDP 转换为 websocket。 我使用 Quart 编写它,它就像一个异步 flask ,以便使 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);
};

我发现的一个缺点是通过网络执行此操作会使声音滞后,即使在本地主机上设置它时它工作得很好。

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


编辑:

我实际上找到了一种使用 vlc(或 cvlc)更好的解决方案。看来 vlc 可以获取 rtp 流并通过多次操作重新传输它。

cvlc rtp://source_ip:source_port --sout '#transcode{acodec=mp3}:standard{access=http,mux=mp3,dst=dest_ip:dest_port}'

启动该进程时,客户端可以是最简单的 HTML5 音频标签:

<audio controls autoplay>
    <source src="http://dest_ip:dest_port" type="audio/mp3">
</audio>

关于javascript - 如何在 JavaScript Web 客户端中播放 PCM 流(通过 UDP)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57005582/

相关文章:

javascript - 如何将非数字字符串转换为 int

javascript - jquery刷新页面依赖if-else语句

javascript - 如何在ionic 3中使用SMTP发送电子邮件?

JavaScript 压缩实现

macos - 如何在 OS X 上实时录制和播放音频

wcf - 在 ServiceModel 客户端配置部分找不到名称为 ' and contract ' I' 的端点元素。

c++ - 使 PlaySound 成为非阻塞的

python - 使用 HTML5 websockets 实现基于 web 的实时视频聊天

security - 客户端密码哈希与纯文本

java - "Client - Server"性能问题