У клиента есть места, где видео может накапливаться и отставать от реального времени:
- входной сетевой буфер;
- внутренний синхронизационный буфер транспорта;
- протокольный буфер компенсации колебания скорости сети;
- буфер плеера;
- буфер декодера (видеокарты);
- аппаратные задержки.
И это опять буферы, буферы и ещё раз буферы. Давайте по порядку.
(ламповый буфер)
Входной сетевой буфер
Клиент получает данные через сетевой сокет. IP сокеты имеют буферы, которые могут заполняться при недостаточно быстром получении данных пользовательской программы.
Если буфер слишком мал, то видеотрансляция будет постоянно прерываться, а если слишком большим, то пользователь будет должен ждать наполнения буфера дольше необходимого времени, определяемого скоростью передачи данных. В худшем случае, если буфер будет под мегабайт, то в него может набиться до 4-8 секунд видео.
Если буфер наполнился, то восстановить риалтайм можно будет или ускоренной перемоткой, или выбрасыванием части данных. В действительности это почти никогда не происходит, поскольку разработчики плееров откладывают эту функциональность на будущие релизы.
С точки зрения близости к риалтайму большой сетевой буфер — зло. С точки зрения плавности проигрывания телеканала — вполне себе разумная вещь, потому что ядро работает достаточно быстро и хорошо, чтобы успевать наполнять свои буферы.
Посмотреть на состояние буфера на клиенте сложно, потому что если бы там был Linux, можно было бы запустить утилиту ss. Но выяснить эту информацию на Android или Windows сложнее.
Внутренний синхронизационный буфер демуксера
В различных протоколах любят по разному растащить в сторону аудио и видео. Например, в 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 для своевременного оповещения о готовности кадра.
Аппаратные задержки на последнем метре доставки видео
Огромный декодированный кадр надо показать. Если вы декодировали не на видеокарте, а на процессоре, то его надо скопировать в видеопамять для показа, а это занимает время. Видеокарты также могут добавлять миллисекунды, но обычно здесь уже все не так страшно по сравнению с тысячами миллисекунд в предыдущих случаях.
Итого
Разобравшись со всеми видами задержки в доставке и получении видео, можно начать измерять ее. В следующей статье мы расскажем, как это можно сделать.
Комментарии (14)
Videoman
20.12.2017 13:06DASH — типичный пример академического дизайна. Он рассчитан на поток строго из SDI и строго с атомных часов. Т.е. уже банальный стрим с мобильного телефона, пропущенный через RTMP уже не укладывается в прокрустово ложе DASH и MSE.
На 100% согласен.
HLS — практичная штука, оно работает, везде существует и гораздо менее хрупкое.
Ну вот, те же яйца, только с другого ракурса. Удобство только в том, что Apple жестко диктует что и как. DASH, по сравнению с HLS, намного более «обтекаем»: контейнер не описан, кодек не описан, движок на стороне браузера — тоже (MSE лишь частный случай) и т.д. В общем, делайте что хотите и как хотите.Про PCR: многим клиентам вообще наплевать на PCR, они его не читают, потому что в аудио и видео потоках идут DTS. Но конечно для олдовых mpegts-пуристов это ересь требующая очистительного костра.
Тут вы загнули. PCR — это синхронизация часов источника и приемника, а DTS/PTS это синхронизация медиапотоков между собой. Короче, PCR вообще нужен только при real-time вещании. Cвязи ненужности одного при наличии другого я, например, не уловил.Halt
20.12.2017 14:15Короче, PCR вообще нужен только при real-time вещании.
Поправлю. Он нужен во всех случаях, когда у вас на руках в каждый момент времени есть только фрагмент стрима. Даже в случае HLS/DASH, хотя вроде бы «все должно быть нормально, ибо есть большой запас в несколько сегментов и ~30 секунд». Именно из-за clock drift-а, который упорно не хочет понимать топикстартер, декодоер рано или поздно упрется в одну из границ буфера и привет лаги/дропы. А потом начинают говорить, что стандарт говно. Именно поэтому, стандарт MP2 специфицирует допустимый дрифт PCR не более чем на 500 наносекунд.
Резюмируя: когда вам весь TS доступен локально на диске, то PCR не нужен. Когда вы получаете его из кабеля (QAM) или скачиваете из сети по частям (стриминг, VOD и особенно realtime) — нужен. Во втором случае надо использовать PCR pacing в демультиплексоре.Videoman
20.12.2017 14:31Еще поправка:
Он нужен во всех случаях, когда у вас на руках в каждый момент времени есть только фрагмент стрима.
PCR — это сервис и в общем случае его может не быть в потоке, если это не TS. У меня было куча таких задач. И в любом случае, нужно синхронизовать таймер на приеме так, чтобы он тикал точно как на передающей стороне. Иначе, буфер либо опустошится, либо переполнится. В общем случае (без PCR), это делается по PTS в потоке и по времени прихода семплов физически, хитрым усреднением и разной эвристикой. Все это актуально только если точно известно что поток — live. Для файлов — времени не существует, в плане поступления данных.
erlyvideo
21.12.2017 08:51В целом получается, что в приличном потоке PCR, DTS (на аудио и видео), а так же fps и sample rate надежно дублируют друг друга. PCR тикает с одной точностью в одном месте потока, DTS на всех пидах тикает с меньшей точностью, но без дрифта от PCR, причем дельта между видеокадрами по DTS строго равна 3600 (для 25 fps), а между аудиокадрами samples/sample_rate (с коэффициентом).
Это всё в идеальном мире, т.е. когда кому-то показывают как это всё работает и просят через 3 минуты удалиться, пока не набежал рассинхрон. Вот когда эти цифры начинают расходиться, HLS клиенты выживывают и пару раз квакнув продолжают играть, вернув себе синхронзацию, а DASH клиенты зависают и рассыпаются.
Вы всё таки не забывайте, что HLS — это в каком-то смысле гибрид VOD и Live, т.е. сегменты то скачиваются и при пилообразном трафике говорить о равномерности поступления кадров очень сложно.
Rewire_getz
21.12.2017 13:21Вот интересно где этот DASH реализован что бы можно было посмотреть-покрутить ?!
Приходит в голову Youtube Live, OK Live кстати у последних с реализацией полный порядок:
неоднократно обычным FFMPEG RTMP энкодером отправлял им поток в течении 3-х часов, они его по DASH в плеер зрителям раздают ( до 39 000 одновременно в пике ) — никаких проблем.
vadimpl
21.12.2017 13:21Не по теме. Всё-таки на фото не буфер, а фильтр (сглаживающий пассивный; возможно, для 230В)
Halt
Спасибо за статью, интересно посмотреть на проблемы коллег с другой стороны. По работе мне приходится сталкиваться с QAM/IP вещанием в сетях кабельного телевидения.
Скажите, а DASH в вашей сфере насколько хорошо себя чувствует? С учетом того, что в плане полноты стандарта по сравнению с DASH, HLS выглядит как дешевая подтирка, очень странно, что все по-прежнему активно цепляются за HLS.
И еще момент: существуют ли свои подходы к ABR? Я так понимаю, что для рилтайм стриминга критерии могут существенно отличаться от таковых для обычного вещания и тем более от VOD.
Paul_Nice
DASH — это боль и страдание, особенно в условиях реальной жизни.
Потому и любить его можно только издалека.
Halt
Подробнее, пожалуйста.
Я то же самое могу сказать и про HLS, особенно, если сценарии использования выходят за пределы «проиграть хоть как-то, в бытовых условиях».
А если задавать разные неудобные вопросы, вроде синхронизации разных MPEG2-TS стримов через PCR и поведения, не определенного стандартом, то вообще мрак.
Paul_Nice
Если говорить про то, как DASH работает, а не про подоплёку его активного продвижения (хейтерство Apple, желание Google сделать себе жизнь проще и т.д.) то
DASH — очень капризен и требует очень высокого качества источника.
Его массовое внедрение бьёт в первую очередь по мелким игрокам у которых не всегда всё хорошо и часто нет возможности привести условия к идеальным.
Вместо тысячи слов посмотрите как самый свежий референс работает.
И попробуйте перемотку.
Если так выглядит референс, то наверно лучше ещё подождать, прежде чем к нему приходить.
Но и с MPEG2 на вход тоже наверное не стоит мириться. 2к17 уже кончается всё-таки.
Или у вас какой-то жёсткий прям сценарий?
erlyvideo
DASH — типичный пример академического дизайна. Он рассчитан на поток строго из SDI и строго с атомных часов. Т.е. уже банальный стрим с мобильного телефона, пропущенный через RTMP уже не укладывается в прокрустово ложе DASH и MSE.
HLS — практичная штука, оно работает, везде существует и гораздо менее хрупкое.
Про PCR: многим клиентам вообще наплевать на PCR, они его не читают, потому что в аудио и видео потоках идут DTS. Но конечно для олдовых mpegts-пуристов это ересь требующая очистительного костра =)
Halt
И это очень печально. Потому что PTS/DTS и PCR это фундаментально разные вещи. То что декодер можно завести только на PTS не означает, что PCR не нужен. Но вы, как специалист, это и так должны понимать.
А потом возникает ситуация, что «у меня все сутками работало на тестах», а как только код попадает в поле, да еще на миллионы устройств — приехали. Особенно это критично для транскодеров, стоящих, как правило, в аппаратной кабельного оператора и работающих годами.
erlyvideo
я и про стандарт, и про реализации. Они друг друга стоят и в отличие от закрытых, опенсорсные хоть как-то можно допилить и подправить. Закрытые лучше не трогать вообще, они рассчитаны строго на один единственный сценарий использования с единственным бекендом и набором контента.
Про PCR я ваших страданий не понимаю. DTS/PTS в потоках достаточно что бы без проблем показывать связное аудио и видео. PCR нужен для игрищ вида: а давайте здесь пихнем левый DTS или не пихнем его вообще.