首页 > 解决方案 > 通过 UDP 发送音频时如何减少延迟?

问题描述

我编写了一个小型 java 程序 (pc) 和 android 应用程序,旨在将音频从 pc 流式传输到我的手机。我通过 UDP 发送音频数据,并且无法确定发生延迟的来源。从客户端到服务器,在处理数据包之前大约需要 1.5-2 秒。

我的程序缓冲 512 个字节发送给客户端,接收后立即播放。

我测试了一些不同的配置,任何(基本上)高于这个值的配置,延迟只会增加。使用较低的值,延迟方面没有明显改善,但质量损失明显。

根据 windows,设备之间的 ping 时间只有 3ms,所以我假设网络连接不是问题,尽管我并不肯定。

我的客户端(PC)的代码如下所示。

byte[] buffer = new byte[512];
while (true) {
    try {
        audioInput.read(buffer, 0, buffer.length);
        DatagramPacket data = new DatagramPacket(buffer, buffer.length, address, port);
        dout.send(data);
        System.out.println("Sent Packet #" + i++);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

服务器(电话)的代码如下。

    byte[] buffer = new byte[512];
    DatagramPacket incomingPacket = new DatagramPacket(buffer, buffer.length);
    while (true) {
        try {
            dgs.receive(incomingPacket);
            buffer = incomingPacket.getData();
            audioOutput.write(buffer, 0, buffer.length);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

我预计数据包的到达速度接近小于 5 毫秒的网络延迟,但实际上仅在约 1500 毫秒后才收到它们。

我希望你们中的一些人可能对这类问题有经验。我知道像 Discord 和 Skype 这样的应用程序以更高的比特率进行流传输,延迟更高,但延迟却大大降低,所以我希望我可能错过了一些东西。

标签: javaaudioudp

解决方案


您正在尝试进行实时流式传输。让我们看看整个过程的延迟预算。

首先,您正在使用 512 字节的缓冲区 - 假设样本为floats 且采样率为44.1k,您128每次处理样本,这给出了~2.9ms.

你能得到的最好的就是~7.5ms这个缓冲区大小。

控制流程:

1:音频输入硬件生成样本并将它们存储在缓冲区中。在预定数量的样本之后,操作系统被中断以服务音频。这个缓冲期可能比 128 个样本长得多。这是T1

2:操作系统调度音频守护进程运行。S1它在运行前等待

3:音频守护程序运行,处理音频,将其写入缓冲区,并向操作系统发出信号,表明可以读取样本。这个时间短到可以忽略不计。

4:操作系统通过解除对 的调用来安排您的输入进程运行audioInput.read(buffer, 0, buffer.length);S2它在被安排之前等待。

5:你打电话 System.out.println()。这可能会阻塞 - 特别是当您每秒写入约 350 次时 - 可能直到另一个不相关的进程被安排。S3

6:将 UDP 写入网络套接字。操作系统可能会将其排队等待传输——这可能会在极短的时间内发生

7:通过网络中转。T2

接收方几乎与上述相反。

因此总延迟是

2 * (T1 + S1 + S2 + S3 + T2)

我想你的大部分延迟是发送和接收的硬件缓冲期 - T1。如果你得到远低于10msS1,S2,S3将开始变得重要。

笔记:

  • S1,S2是操作调度延迟并且依赖于系统负载和调度策略。音频渲染处理程序通常以实时威胁优先级运行。
  • 您可以S3通过不登录控制台来消除。这种延迟是特别不可预测的。
  • Java 运行时可能会带来一些隐藏的运行时成本(例如 GC)。这将是可靠的低延迟音频的限制因素。

真正低延迟的秘诀是:

  • 用 C 或 C++ 实现
  • 没有内存分配,登录渲染循环
  • 固定堆栈和堆页面以防止它们被交换
  • 以实时调度优先级运行音频渲染线程。
  • 防止优先级倒置的无锁数据结构。

推荐阅读