Как я могу уменьшить задержку при отправке аудио по UDP? - PullRequest
0 голосов
/ 05 января 2019

Я написал небольшую Java-программу (ПК) и приложение для Android, которое предназначено для потоковой передачи звука с ПК на мой телефон. Я отправляю аудиоданные по протоколу UDP, и у меня возникают проблемы с определением источника задержки. От клиента к серверу проходит около 1,5-2 секунд до обработки пакетов.

Моя программа буферизует 512 байт для отправки клиенту, которые воспроизводятся сразу после получения.

Я протестировал несколько разных конфигураций, с чем-либо (существенно) превышающим это значение, задержки только увеличиваются При более низких значениях нет заметного улучшения с точки зрения задержки, но есть заметная потеря качества.

Время проверки связи между устройствами составляет всего 3 мс в соответствии с Windows, поэтому я предполагаю, что сетевое соединение не является проблемой, хотя я не уверен.

Код моего клиента (ПК) показан ниже.

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 мс. </p>

Я надеялся, что некоторые из вас могут иметь опыт решения такого рода вопросов. Я знаю, что такие приложения, как Discord и Skype, транслируются на более высоких битрейтах, с гораздо более высокой задержкой, но имеют значительно меньшую задержку, поэтому я надеялся, что, возможно, что-то пропустил.

1 Ответ

0 голосов
/ 05 января 2019

Вы пытаетесь выполнять потоковую передачу в реальном времени. Давайте посмотрим на бюджет задержек на протяжении всего процесса.

Во-первых, вы используете 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 начнут становиться значимыми.

Примечание:

  • S1, S2 являются задержками планирования работы и зависят от загрузки системы и политики планировщика. Обработчики рендеринга аудио обычно запускаются с приоритетом в реальном времени.
  • Вы можете устранить S3, не войдя в консоль. Эта задержка особенно непредсказуема.
  • Время выполнения Java может налагать некоторые скрытые затраты времени выполнения (например, GC). Это будет ограничивающим фактором для надежного звука с низкой задержкой.

Рецепт действительно с низкой задержкой:

  • Реализация на C или C ++
  • Нет выделения памяти, логирование в цикле рендеринга
  • Закрепить стопку и кучу страниц, чтобы предотвратить их замену
  • Запуск потоков рендеринга аудио с приоритетом планирования в реальном времени.
  • Структуры данных без блокировки для предотвращения инверсии приоритетов.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...