TCP или UDP? Откладывает наращивание производства для видеопотока - PullRequest
4 голосов
/ 23 января 2020

Я создаю видеопоток с камеры с FFMPEG и nodejs stream Duplex class.

this.ffmpegProcess = spawn('"ffmpeg"', [

  '-i', '-',
  '-loglevel', 'info',

  /**
   * MJPEG Stream
   */

  '-map', '0:v',
  '-c:v', 'mjpeg',
  '-thread_type', 'frame', // suggested for performance on StackOverflow.
  '-q:v', '20', // force quality of image. 2-31 when 2=best, 31=worst
  '-r', '25', // force framerate
  '-f', 'mjpeg',
  `-`,

], {
  shell: true,
  detached: false,
});

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

Однако мы импортировали службу в AWS production, когда мы подключаем первый компьютер, у нас задержка около 2 секунд, но если другой клиент подключается к потоку, они оба начинают много задерживаться, где задержка составляет до 10+ секунд, и, кроме того, видео очень медленное, как при движении между кадрами.

Теперь я спросил о TCP или UDP, потому что мы используем TCP для потока, который означает, что каждый отправляемый пакет реализует протокол и последовательность syn-ack-synack TCP.

Мой вопрос: может ли TCP действительно вызывать такую ​​проблему, что задержки могут доходить до 10 секунд с очень медленным движением ? потому что в локальной сети он работает очень хорошо.

Ответы [ 3 ]

4 голосов
/ 28 января 2020

Да, TCP определенно не является правильным протоколом для него. Может использоваться, но с некоторыми изменениями на стороне источника. К сожалению, UDP не является волшебной c пулей, без дополнительной логики c UDP также не решит проблему (если вас не волнует, что вы увидите битые кадры, построенные случайным образом из других кадров).

Пояснение

Основными функциями TCP являются то, что пакеты доставляются в правильном порядке и все пакеты доставляются. Эти две функции очень полезны, но весьма вредны для потокового видео.

В локальной сети пропускная способность достаточно велика, а также потеря пакетов очень мала, поэтому TCP работает нормально. На inte rnet пропускная способность ограничена, и каждое достижение предела вызывает потерю пакета. TCP будет иметь дело с потерей пакета путем повторной отправки пакета. Каждая повторная отправка приведет к увеличению задержки потока.

Для лучшего понимания попробуйте представить, что пакет содержит целый кадр (изображение JPEG). Предположим, что нормальная задержка ссылки составляет 100 мс (время, в течение которого будет проходить кадр). Для 25 кадров в секунду вам нужно доставить каждый кадр каждые 40 мс. Если кадр потерян во время передачи, TCP обеспечит повторную отправку копии. TCP может обнаружить эту ситуацию и исправить в идеальной ситуации с 2-кратной задержкой - 2 * 100 мс (в действительности это будет больше, но ради простоты я сохраняю это). Таким образом, во время потери изображения 5 кадров ожидают в очереди получателя и ждут одного пропущенного кадра. В этом примере один пропущенный кадр вызывает 5 кадров задержки. А потому что TCP создает очередь пакетов. задержка никогда не будет уменьшена, только может расти. В идеальной ситуации, когда пропускная способность достаточна, задержка будет все та же.

Как это исправить

То, что я сделал в nodejs, было исправлением стороны источника. Пакеты в TCP могут быть пропущены только в том случае, если исходный код сделает это, TCP не может сделать это сам.

Для этой цели я использовал событие dump . Идея алгоритма заключается в том, что ffmpeg генерирует фрейм со своей скоростью. И node.js читает кадры и всегда сохраняет последние полученные. Он также имеет исходящий буфер с размером одного кадра. Таким образом, если отправка одного кадра задерживается из-за состояния сети, входящие изображения из ffmpeg молча отбрасываются (это компенсирует низкую пропускную способность), за исключением последнего полученного. Поэтому, когда буфер TCP сообщает (по событию drain), что какой-то кадр был правильно отправлен, nodejs возьмет последнее полученное изображение и запишет его в поток.

Этот алгоритм сам себя регулирует. Если пропускная способность передачи достаточна (отправка быстрее, чем кадры, сгенерированные ffmpeg), то пакет не будет отброшен и будет доставлено 25fps. Если пропускная способность может передавать только половину кадров, в среднем один из двух кадров будет отброшен, поэтому приемник будет набирать 12,5 кадров в секунду, но не будет увеличивать задержку.

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

1 голос
/ 31 января 2020

Это может не относиться к вашему варианту использования, но именно здесь система доставки контента на основе HTTP, такая как HLS, очень полезна.

UDP или TCP, попытка потоковой передачи данных в реальном времени через inte rnet является сложной задачей.

HLS будет выдавать файлы, которые могут быть загружены через HTTP (что на самом деле весьма эффективно при обслуживании больших файлов и обеспечивает целостность видео, поскольку оно встроено в функции исправления ошибок и т. Д.), И повторно Собран в порядке на стороне клиента игроком.

Он легко адаптируется к потокам различного качества (часть протокола), что позволяет клиенту решать, как быстро он использует данные.

Кроме того, Netflix и тому подобное используют Widevine DRM, который в чем-то похожи - доставка контента на основе HTTP, хотя widevine будет принимать коды различного качества, помещать его в один файл c гиганта и использовать HTTP диапазон запросов для переключения между качественными потоками.

1 голос
/ 30 января 2020

На сервере или клиенте запустите трассировку Wireshark, это скажет вам точную «задержку» пакетов, или какая сторона не делает правильную вещь. Похоже, сервер не соответствует вашим ожиданиям. Убедитесь, что потоком является UDP.

...