Ой, у меня задержка. Часть 2
В предыдущей статье мы говорили о сокращении задержки в трансляции видео. С отправкой разобрались, теперь поговорим о доставке.
У клиента есть места, где видео может накапливаться и отставать от реального времени:
- входной сетевой буфер;
- внутренний синхронизационный буфер транспорта;
- протокольный буфер компенсации колебания скорости сети;
- буфер плеера;
- буфер декодера (видеокарты);
- аппаратные задержки.
И это опять буферы, буферы и ещё раз буферы. Давайте по порядку.
(ламповый буфер)
Входной сетевой буфер
Клиент получает данные через сетевой сокет. IP сокеты имеют буферы, которые могут заполняться при недостаточно быстром получении данных пользовательской программы.
Если буфер слишком мал, то видеотрансляция будет постоянно прерываться, а если слишком большим, то пользователь будет должен ждать наполнения буфера дольше необходимого времени, определяемого скоростью передачи данных. В худшем случае, если буфер будет под мегабайт, то в него может набиться до 4–8 секунд видео. Т
Если буфер наполнился, то восстановить риалтайм можно будет или ускоренной перемоткой, или выбрасыванием части данных. Т.е. в реальности можно считать, что почти никогда, потому что во всех плеерах это подразумевают и решают отложить до будущего релиза.
С точки зрения близости к риалтайму большой сетевой буфер — зло. С точки зрения плавности проигрывания телеканала — вполне себе разумная вещь, потому что ядро работает достаточно быстро и хорошо, чтобы успевать наполнять свои буферы.
Посмотреть на состояние буфера на клиенте сложно, потому что если бы там был линукс, можно было бы запустить утилиту ss, но выяснить эту информацию на андроиде или виндовсе сложнее.
Внутренний синхронизационный буфер демуксера
В различных протоколах любят по разному растащить в сторону аудио и видео. Например, в MPEG-TS принято взять подряд по 3–5 аудиокадров и положить их в один пакет, чтобы сократить трафик. При этом поток кадров из «VAVAAVAVAA» превращается в «VVAAAAAVVAAAAVAAAVVVAAA».
Таймстемпы при этом перемешиваются и, если вам дальше хочется их иметь отсортированными (например, для отдачи в RTMP это критично), то надо заводить буфер, который будет притормаживать кадры. Особенно чудесное поведение у такого буфера возникает, когда аудио пропадает полностью или на одном канале из пяти.
Отличить полное пропадание от очень большой задержки в общем случае практически невозможно. Поэтому в процессе разработки приходится писать код, который должен «чувствовать», что же там произошло.
Буфер компенсации скорости сети
Здесь речь пойдет о том буфере, который мы имеем в HLS, или о том буфере, которым управляли в RTMP плеере.
Например, HLS плеер устроен так:
- плеер скачивает три сегмента;
- скачав третий, плеер начинает проигрывать поток, начиная с первого;
- один процесс независимо забирает самый старый сегмент, а другой подкачивает первый.
В идеальном случае поддерживается 2–3 сегмента в буфере, которые могут пропасть, если скорость сети начинает проседать.
Буфер для компенсации колебания скорости сети заложен в любом плеере. Например, у RTMP этот буфер, теоретически, 0, у HLS он, теоретически, скажем 10 секунд, у RTSP плееров он как правило реально 0, а в SIP он точно 0.
Мы говорим «теоретически», потому что на граничных параметрах ничего не будет работать. Например, HLS плееры на 1-секундных фрагментах могут начать работать крайне нестабильно. Причем это может быть из-за банальных ошибок типа учета длительности сегмента в секундах, а не миллисекундах.
А RTMP флеш плеер на нулевом буфере себя ведет просто «восхитительно» — он начинает накапливать задержку по одной секунде за минуту и через пару часов может умереть в страшных мучениях.
Буфер плеера
Механизм проигрывания почти всегда отделен от механизма, скачивающего видео по какому-либо протоколу. Кадры распаковываются, отделяются от метаданных и передаются в плеер. Дальше начинается «странное»: Flash, MSE плеер могут потребовать больше одного кадра для старта проигрывания.
Если это происходит, то у вас опять начинается отставание и задержка. Здесь цифры бывают порядка 2–5 кадров, т.е. до 200 мс.
Буфер декодера
Хороший риалтайм декодер сразу будет декодировать полученный поток и отдавать его. Блоки доступа поступают в буфер непрерывно, причем скорость заполнения буфера пропорциональна скорости кодированного потока. Блоки доступа загружаются в буфер за разное время, поскольку кодированные изображения имеют разный объем данных. Выгружаются данные из буфера через одинаковые интервалы, равные частоте кадров воспроизводимого изображения, причем выгружаются целиком и моментально. Если, например, стартовая задержка нового потока значительно больше финишной задержки старого, то после того, как будет воспроизведено и выгружено из буфера последнее изображение старого потока, придется долго ждать декодирования и воспроизведения первого изображения нового потока. Это приведет, например, к замораживанию последнего изображения старого потока и заметной склейке. Если скорость нового потока значительно больше скорости старого, то склейка будет еще более заметной, поскольку при этом буфер переполняется и часть данных теряется.
Если же вы на кодировании риалтайм потока включили b-frames, что само по себе чудесная затея, то немедленно на выходе получаете задержку в размере N*T, где:
- N — максимальное расстояние перестановки кадров, обычно около 4
- T — длительность кадра. Для 25 fps — это 40 мс.
Таким образом, мы на ровном месте получаем задержку в минимум 160 мс.
В реальности декодер может ещё притормозить кадр другой или просто не иметь хорошего событийного API для своевременного оповещения о готовности кадра.
Аппаратные задержки на последнем метре доставки видео
Огромный декодированный кадр надо показать. Если вы декодировали не на видеокарте, а на процессоре, то его надо скопировать в видеопамять для показа, а это занимает время. Видеокарты также могут добавлять миллисекунды, но обычно здесь уже все не так страшно по сравнению с тысячами миллисекунд в предыдущих случаях.
Итого
Разобравшись со всеми видами задержки в доставке и получении видео, можно начать измерять ее. В следующей статье мы расскажем, как это можно сделать.