Pull to refresh

Comments 37

А почему не передавать записанные SPS и PPS перед каждым IDR NAL? Я пишу SPS + PPS и передаю преред каждым IDR. Кроме того, отдельно храню IDR и при первом подключении клиента сыплю сохраненный IDR+SPS+PPS (можно хранить и сыпать все налы между IDR), с минимальным интервалом между датаграммами, что позволяет получить картинку сразу после подключения.
С SPS и PPS кадрами в нашем случае проблем не было: наша камера шлет их регулярно, с каждым кадром (см. парсинг потока камеры ELP). Что касается сохранения старых IDR кадрой, то в таком случае в начале трансляции будет показан старый IDR кадр, но последующие Non-IDR кадры буду накладываться на старый IDR, что будет давать плохую картинку. Нас такая ситуация не устраивала, к тому же небольшая задержка старта видео (менее секунды) нас не беспокоила.
ничего накладываться не будет. При подключении посылаете записанные IDR+NIDRы, только не с интервалом 20-30мс, как при трансляции, а 3-4мс. Получаете готовый кадр и приемник готов к приему след. нонидров. Можно послать идр и игнорить все налы до след. идра, тогда вы получите статическую картинку на пару секунд. Все это верно и при трансляции записанного AnnexB файла, когда, как правило SPS/PPS передаются только раз.
В условиях ограниченной пропускной способности сети и ограниченной производительности планшета мы не можем сильно поднять скорость передачи кадров (максимум на 20-50 процентов). Таким образом мы будем ждать пока видео синхронизируется с реальным временем где-то секунду. При текущем подходе мы ждем примерно столько же.
Есть еще GStreamer с кучей плагинов и вроде с плагином от интела в том числе (GStreamer Media SDK plugins) В первом комменте в статье про mjpg-streamer его тоже вспоминают. Его не пробовали?
Пробовали, практически в самом начале разработки. Чем-то он нам сразу не подошел, уже точно не помню чем. По-моему, не удалось передавать видео без перекодирования.
Значит просто не разобрались в gstreamer.
Обычно то, что нерешает vlc и ffmpeg можно небольшими шаманствами с плагинами решить gstreamer'ом. Притом что в gstreamer'е можно спокойно использовать ffmpeg
Может и так. GStreamer конечно мощная и лаконичная штука. Если подскажете, как передать с помощью gstreamer'а видео без перекодирования, будем благодарны.
Если у вас камера выдает картинку в mjpg или в raw, а вы хотите h264, то перекодировать придется.
Если же камера сама умеет выдавать h264, то это будет что-то вроде:
gst-launch v4lsrc ! video/x-h264,width=640,height=480 ! rtph264pay ! udpsink host=127.0.0.1 port=5555
Писал по памяти, скорее всего нагнал, пишите в личку, если не взлетит.
в данном конкретном случае capsfilter (тот элемент пайплайна который «video/x-h264,width=640,height=480») даже не нужен наверно, так как rtph264pay по своей природе на вход просит следующее:
video/x-h264, stream-format=(string)avc, alignment=(string)au
video/x-h264, stream-format=(string)byte-stream, alignment=(string){ nal, au }

а вот если паковать в какой-то мультиформатный контейнер (у меня для стриминга в свое время очень хорошо mpegtsmux себя показал) то конечно стоит прописать капсфильтр чтобы исключить перекодирование.

Кроме того, если передача будет вестись по не самому надежному каналу (а тут у нас именно такой, БПЛА же) возникает проблема потерь udp пакетов (возникнет как только выйдете из лаборатории) соответственно я бы все же советовал tcp использовать, иначе рискуете вообще никакой картинки не получить. MJPEG кстати в этом отношении лучше, так как нет межкадрового, что получил то и отобразил, в худшем случае теряя фреймрейт.
Так вот, если использовать tcp то помимо той задержки которую вы видите на старте появляется назойливая «накапливающаяся» задержка.
Выглядит это следующим образом: связь устанавливается, картинка отображается, задержка небольшая. вдруг откуда ни возьмись возникают проблемы в канале связи, картинка фризится, стример/приемник начинает увеличивать буферы чтобы давать плавную картинку. В итоге через пару таких фризов имеем вместо стартовой задержки в 200 мс аж 2-3 секунды задержки. что конечно неприемлемо.
Всех путей решения данной проблемы я если честно и не вспомню уже сейчас, года 3-4 назад занимался. один из вариантов добавить leaky queue который будет дропать слишком старые, все равно никого не интересующие данные если есть более новые.

Кстати. vlc имеет настроечку network-caching позволяющую настроить политику кеширования. если его в качестве клиента использовать — отлично работает
Кстати, поделюсь еще методикой точного вычисления latency:
запускаете секундомер, наводите камеру так чтобы она видела секундомер, ставите рядом в кадр монитор на котором выводится изображение, и делаете скриншот. разница в показаниях материального таймера и таймера на картинке и есть задержка
Да, мы такую же методику измерения применяли.
Но для наших применений пропадания пакетов были приемлемы, а вот накапливающаяся задержка от tcp — вообще никак.

Поскольку у нас не было камеры, которая может в h264, мы в него перекодировали, что давало возможность поиграться с параметрами энкодера. Если делать вот так:
x264enc speed-preset=ultrafast tune=zerolatency threads=8 bitrate=2048, то энкодер достаточно сильно уменьшает расстояние между ключевыми кадрами, а из-за threads=8 все кадры режутся на 8 горизонтальных полосок, которые передаются отдельно.
Соответственно, при потерях выпадают не кадры целиком, а только полоски. Получилось более-менее.

Но h264 слишком нагружал процессор, поэтому в итоге мы использовали mjpeg.

Был еще волшебный способ с использованием rtpbin, в котором по одному порту шло видео по udp, а по второму синхронизация rtcp, а по третьему шла синхронизация от приемника.
Но тут проблема была в том, что приемником был не gstreamer, а приложение для Windows, оно так не умело.
без аппаратного кодирования в h264 лучше не соваться. а набор параметров энкодера аппаратного кодировщика уже сильно варьируется от случая к случаю
Вроде ж можно задать размеры TCP буферов сокета. Соответственно, когда буфер отправителя заполнится (потому что получатель не подтвердит приём большого количества пакетов), то send на сервере внезапно заблокируется. Вы протупите забрать несколько кадров с камеры (ведь цикл считать кадр-отправить-повторить зависнет на втором шаге) и драйвер должен их дропнуть (ну или дропнуть новые, на самом деле не очень принципиально, ибо буферов захвата обычно не очень много и дроп новых кадров лишь отсрочит восстановление правильной картинки на долю секунды). Когда связь восстановится send разблокируется и всё «починится». В итоге получаем дроп кадров вместо нарастающей задержки, что и нужно для риалтайма (варианта и не дропать и не увеличивать задержку в реальном мире не существует, нужно выбирать).

Делаю так, задержка вроде не накапливается. Главное правильно подобрать размеры буферов. Слишком маленькие снижают производительность передачи данных и тоже получается плохо.

Все в точности наоборот, ffmpeg - более гибкий инструмент, чем gstreamer.

Решал практически точно такую же задачу при помощи gstreamer. Из плюсов — возможность запрототипировать предполагаемый функционал с помощью gst-launch (а иногда даже и в продакшен вытолкнуть если никто не видит а сроки поджимают)
Имеет некоторые подводные камни, но обладает отличной портируемостью, хорошей гибкостью и весьма приличной поддержкой самых разных аппаратных решений
Если камера не использует B-Frames, то при использовании libavcodec никаких задержек не будет, во всяком случае, когда я писал подобный софт (HTTP Live Streaming) никаких задержек не было. В ffmpeg сейчас новый асинхронный API запилили, и декодер пишется букально в три строчки.
Весной был на хакатоне с идеей подключения дополнительных мониторов к ноутбуку без проводов, и чтобы через вайфай при этом работал интернет. Решил, что создавать виртуальные дисплеи встроенным функционалом ОС и после захватывать их хорошая идея(такое возможно на маках, виндах и я сижу на линуксе). Наткнулся на проблему, что задержка даже на локалхосте составляла добрых 0.3-0.5 секунд, что существенно для восприятия видео со звуком и даже при написании кода. Задача чисто офисная, поэтому вопрос о стриминге игр не вставал вообще. Расчётным был порог задержки менее 66 мс, дальше уже становилось не комфортно воспринимать задержку звука. При 60 кадрах в секунду, генерация одного кадра составляет около 17 мс, соответственно моя задача была добиться буферизации не более 3х кадров на клиенте для отображения картинки. На моём ноуте есть дискретная nvidia с nvenc и встроенная интел видеокарта с quick sync аппаратным кодировщиком, но я увы не смог за 48 часов вместе с другим разработчиком победить буферизацию кодировщика как я в этой статье понял. Так же я пытался закинуть raw данные в udp пакет, а на клиенте читал напрямую из сокета без буферизации. В итоге малинка в качестве клиента беспроводного монитора осталась валятся на полке. Вопросы остались просты о скорости софтовых грабберов монитора, скорости кодирования картинки, объёме данных FullHD@60fps передаваемых по сети при таком использовании, так как это РоС идея была, качественная реализация была намечена на использование общего канала 802.11ac wave2 или уже 802.11.ax, но чтобы не привязывалось к вайфаю, а работало и по проводной сети, чего не дают miracast и интеловские wireless display.
Какую интересную задачу Вы решали! Мы конечно потратили существенно больше 48 часов на вот это вот всё.
Для БПЛА 0.3 секунды? Смешно. То же самое что кино в кинотеатре смотреть с рейтом в 15 кадров — в общем смотреть можно, но как-то не то.
А если серьезно — обратите внимание на проект Wifibroadcast — всю работу сделали за вас и запилили трансляцию овер 2.4 либо 5Ггц через обычные Wifi донглы, используя малинку с родной камерой — и с задержкой сильно ниже 100мс. Если что — ответы на вопросы искать тут github.com/bortek/EZ-WifiBroadcast
Спасибо, посмотрим. Сами мы сейчас Sky Hopper Pro ковыряем.

Привет! А на каком железе и версии софта "сильно ниже 100мс"?

Пока не слышал о чем то ниже 150мс в 720р.

Использовать MFX_BITSTREAM_EOS все равно, что забивать гвозди микроскопом. Если вы точно знаете, что в потоке нет B кадров, то можно смело использовать DecodeOrder. Плюс если передаете строго по кадрам, то можно добавлять еще и MFX_BITSTREAM_COMPLETE_FRAME. В этом случае декодер не будет ждать начало следующего кадра, чтобы закончить текущий.
MFX_BITSTREAM_COMPLETE_FRAME мы добавляли, но он никак не повлиял на задержку. MFX_BITSTREAM_EOS оказалось достаточно.
MFX_BITSTREAM_COMPLETE_FRAME уменьшает задержку на один кадр (Без этого флага декодер не знает, что переданный фрейм полный, поэтому вынужден ждать прихода первого слайса (или SPS/PPS) следующего фрейма, тогда он понимает, что текущий фрейм завершен, для видеоконференций один кадр — доп. задержка в 0.33 мс при 30fps). Понятно, что при задержке в 5-10 кадров выставление complete frame малозаметно.
EOS (end of stream) — это плохая попытка эмуляции DecodeOrder. Выставляя EOS вы, по сути, говорите декодеру, что больше фреймов не будет, значит то, что есть в буфере можно смело выдать в порядке возрастания поков (не придет фрейм, который нужно показать раньше тех, что есть в буфере). DecodeOrder означает, что вам не нужен реордеринг фреймов вообще (если B фреймов нет, то он действительно не нужен), декодер просто будет выдавать фреймы по готовности.
MFX_BITSTREAM_COMPLETE_FRAME вместе с DecodeOrder позволяет передать битстрим декодеру и сразу же получить выход.
Спасибо за комментарий. Попробуем использовать DecodeOrder.
Полазили по документации и форумам. Видимо Вы имели в виду установку DecodedOrder=1. Сделали это, а также избавились от флага MFX_BITSTREAM_EOS и хака с “окончательным окончанием потока”. Флаг MFX_BITSTREAM_COMPLETE_FRAME уже у нас стоял. В итоге все получилось, задержка даже слегка уменьшилась. Спасибо за очень полезный комментарий!

P.S. Ложка дёгтя: DecodedOrder сейчас deprecated.
Он последние лет 5-7 в deprecated. Его даже убирали ненадолго. Но потом вернули и убирать вряд ли кто-то станет (Вернули специально для решения вашей проблемы). Просто раньше он делал больше, чем то, что вам нужно (в этом режиме декодер также хранил выданные сразу фреймы и позже выставлял корректный frameOrder на серфейс). От этого «больше» отказались. Немедленную выдачу оставили.
К сожалению выяснилась еще одна проблема: на нашем полетном планшете стоит процессор Intel Celeron N2807, и, если установить флаг DecodedOrder=1, видео перестает работать.
Если использовали ffmpeg (точнее его API), то можно посмотреть на AVCodecContext.delay = 0 при инициализации контекста декодера. Эта проблема точно решалась настройками контекстов декодера.
кто может подсказать в чем причина MFX_WRN_DEVICE_BUSY?
используем ffmpeg + qsv для записи и кодирования видео иногда MFX_WRN_DEVICE_BUSY приходил в течении 2-3 минут что приводило к жуткому рассинхрону с аудио сигналом
пришлось в итоге подправить ffmpeg для того что бы если в течении 1 секунды MFX_WRN_DEVICE_BUSY не прекращал появляться принудительно переинициализировать поток кодирования
В данном случае это хенг. Например, декодер отправил в железку фрейм, а ответа так и не получил. Ресурс не освободил, и поэтому принять новые данные не может (возвращает MFX_WRN_DEVICE_BUSY). Возможно стрим битый. Напишите им в саппорт.
UFO just landed and posted this here
Sign up to leave a comment.