Да, 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 кадров в секунду, но не будет увеличивать задержку.
Вероятно, наиболее сложной частью этого алгоритма является правильное разделение потока байтов на кадры.