通过UDP发送音频时如何减少延迟?

时间:2019-01-05 03:34:59

标签: java audio udp

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

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

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

根据窗口,设备之间的ping时间仅为3毫秒,因此我认为网络连接不是问题,尽管我不是很肯定。

我的客户(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();
        }
    }

我期望数据包以接近5ms的网络延迟的速度到达,但实际上只有在1500ms之后才接收到它们。

我希望你们中的一些人可能对这种问题有经验。我知道诸如Discord和Skype之类的应用以更高的比特率流播,具有更高的延迟,但是延迟却低得多,所以我希望可能会错过一些东西。

1 个答案:

答案 0 :(得分:0)

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

首先,您使用的是512字节缓冲区-假设采样为float,采样率为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的硬件缓冲时间。如果您得到的金额远低于10ms,那么S1,S2,S3将会变得越来越重要。

注意:

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

真正降低延迟的方法是:

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