Проблема со сбросом и задержкой при создании фрагментированного MP4 в FFMPEG

Я создаю фрагментированный mp4 для потоковой передачи html5, используя следующую команду:

-i rtsp://172.20.28.52:554/h264 -vcodec copy -an -f mp4 -reset_timestamps 1 -movflags empty_moov+default_base_moof+frag_keyframe -loglevel quiet -
  1. «-i rtsp://172.20.28.52:554/h264», потому что источником является h264 в потоке rtp-пакетов с ip-камеры. Ради тестирования камера установлена ​​с GOP 1 (т.е. все кадры являются ключевыми).
  2. "-vcodec copy", потому что мне не нужно транскодирование, только ремикс в mp4.
  3. «-movflags empty_moov+default_base_moof+frag_keyframe», чтобы создать фрагментированный mp4 в соответствии со спецификацией расширений источника мультимедиа.
  4. «-» в конце для вывода mp4 на стандартный вывод. Я получаю вывод и отправляю его веб-клиенту через веб-сокеты.

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

16/06/2015 15:40:45.239 получил размер данных = 24

06.16.2015 15:40:45.240 получил размер данных = 7197

06.16.2015 15:40:45.241 получил размер данных = 32768

16/06/2015 15:40:45.241 получил размер данных = 4941

06.16.2015 15:40:45.241 получил размер данных = 12606

06.16.2015 15:40:45.241 получил размер данных = 6345

06.16.2015 15:40:45.241 получил размер данных = 6339

06.16.2015 15:40:45.242 получил размер данных = 6336

06.16.2015 15:40:45.242 получил размер данных = 6361

06.16.2015 15:40:45.242 получил размер данных = 6337

06.16.2015 15:40:45.242 получил размер данных = 6331

06.16.2015 15:40:45.242 получил размер данных = 6359

06.16.2015 15:40:45.243 получил размер данных = 6346

06.16.2015 15:40:45.243 получил размер данных = 6336

16/06/2015 15:40:45.243 получил размер данных = 6338

06.16.2015 15:40:45.243 получил размер данных = 6357

06.16.2015 15:40:45.243 получил размер данных = 6357

06.16.2015 15:40:45.243 получил размер данных = 6322

06.16.2015 15:40:45.243 получил размер данных = 6359

06.16.2015 15:40:45.244 получил размер данных = 6349

06.16.2015 15:40:45.244 получил размер данных = 6353

06.16.2015 15:40:45.244 получил размер данных = 6382

06.16.2015 15:40:45.244 получил размер данных = 6403

06.16.2015 15:40:45.304 получил размер данных = 6393

06.16.2015 15:40:45.371 получил размер данных = 6372

06.16.2015 15:40:45.437 получил размер данных = 6345

06.16.2015 15:40:45.504 получил размер данных = 6352

06.16.2015 15:40:45.571 получил размер данных = 6340

16/06/2015 15:40:45.637 получил размер данных = 6331

06.16.2015 15:40:45.704 получил размер данных = 6326

06.16.2015 15:40:45.771 получил размер данных = 6360

16/06/2015 15:40:45.838 получил размер данных = 6294

06.16.2015 15:40:45.904 получил размер данных = 6328

06.16.2015 15:40:45.971 получил размер данных = 6326

06.16.2015 15:40:46.038 получил размер данных = 6326

06.16.2015 15:40:46.105 получил размер данных = 6340

06.16.2015 15:40:46.171 получил размер данных = 6341

06.16.2015 15:40:46.238 получил размер данных = 6332

Как вы можете видеть, первые 23 строки (которые содержат данные примерно 1,5 секунды видео) поступают почти мгновенно, а затем задержка между каждыми двумя последовательными строками составляет ~ 70 мс, что имеет смысл, поскольку видео составляет 15 кадров в секунду. Такое поведение приводит к задержке около 1,5 с.

Это похоже на проблему с очисткой, потому что я не вижу причин, по которым ffmpeg должен хранить первые 23 кадра в памяти, тем более что каждый кадр является собственным фрагментом внутри mp4. Однако я не смог найти какой-либо метод, который заставил бы ffmpeg быстрее сбрасывать эти данные.

У кого-нибудь есть предложение?

Я хотел бы отметить, что это дополнительный вопрос к этому: контент с использованием mp4box


person galbarm    schedule 16.06.2015    source источник
comment
Мне пришло в голову, что у вас есть контроль над blocksize, используемым для буферизации вывода. Проверьте ffmpeg.org/ffmpeg-all.html#toc-pipe. и посмотрите, поможет ли вам настройка этого значения.   -  person Pablo Montilla    schedule 16.06.2015
comment
@PabloMontilla Я пытался поиграть с некоторыми разными значениями размера блока, и хотя это каким-то образом повлияло на вывод, это не решило первоначальную задержку.   -  person galbarm    schedule 18.06.2015
comment
Привет @галбарм! Я не могу запустить видео на странице с вашими параметрами ffmpeg, всегда получаю Skipping unrecognized top-level box: ftyp. (IP-камера h264). Я также пытался изменить -vcodec на libx264, в этом случае я получаю Skipping unrecognized top-level box: mdat. Не могли бы вы описать свой код подробнее или указать его где-нибудь? Самая интересная часть — это параметр .addSourceBuffer, то есть строка кодека. Заранее спасибо!   -  person zarkone    schedule 19.06.2015
comment
Привет, @zarkone Я также вижу ошибку пропуска ftyp, но, похоже, это не имеет никакого функционального эффекта. Вот суть клиентского кода, я уверен, он вам поможет: gist.github.com/galbarm /8cb1b684652de648ded3   -  person galbarm    schedule 21.06.2015
comment
Спасибо за ваш фрагмент!   -  person zarkone    schedule 23.06.2015
comment
Моя проблема была не в коде или параметрах, а в кулачковом кодировании.   -  person zarkone    schedule 23.06.2015
comment
@galbarm какова ваша общая задержка при этом? Мне было бы интересно узнать немного больше о том, как вы получаете вывод и отправляете его веб-клиенту через веб-сокеты, не могли бы вы рассказать мне об этом подробнее? Если задержка хорошая, мне было бы интересно иметь аналогичный подход. Спасибо !   -  person Sloosh    schedule 19.09.2015
comment
@Sloosh Общая задержка составляет ~ 700 мс + GOP в Chrome. Здесь вы узнаете, почему существует ограничение GOP: code.google.com. /p/chromium/issues/detail?id=229412 Как только это будет исправлено, я ожидаю, что задержка в Chrome не превысит 700 мс.   -  person galbarm    schedule 19.09.2015
comment
@Sloosh относительно того, как я получаю вывод и отправляю его через веб-сокет: попробуйте найти реализацию веб-сокетов на стороне сервера, я использовал Fleck (C #). Вам нужно будет использовать его для отправки вашего MP4 в виде двоичных данных клиенту. Что касается захвата вывода: попробуйте прочитать об открытии процесса и прочитать его стандартный вывод.   -  person galbarm    schedule 19.09.2015
comment
@galbarm спасибо, я сделал небольшой тест с использованием node.js ... он работает, НО моя задержка огромна, около 30 секунд! Каково ваше окружение? Вы используете локальный сетевой сервер? Что я должен сделать, чтобы уменьшить задержку? Вы сделали специальную кодировку для этого?   -  person Sloosh    schedule 21.09.2015
comment
@Sloosh см. принятый ответ, который я опубликовал сейчас. Возможно, это ваша проблема.   -  person galbarm    schedule 30.09.2015


Ответы (3)


Ключом к устранению задержки является использование аргумента -probesize:

probesize целое число (ввод)

Установите размер зондирования в байтах, то есть размер данных для анализа для получения информации о потоке. Более высокое значение позволит обнаруживать больше информации в случае ее рассредоточения по потоку, но увеличит задержку. Должно быть целым числом не меньше 32. По умолчанию 5000000.

По умолчанию значение составляет 5 000 000 байт, что эквивалентно примерно 1,5 секундам видео. Мне удалось почти полностью устранить задержку, уменьшив значение до 200 000.

person galbarm    schedule 30.09.2015
comment
Я немного опаздываю на вечеринку, но я работаю над тем же самым. За исключением того, что я планирую отправлять данные с помощью RTCDataChannel (по сути, UDP) вместо WebSockets, что технически должно дать мне еще большую задержку. Я новичок во всех этих видеоматериалах, и мне очень трудно понять все эти разговоры о moof, mdat и moov и о том, что мне нужно делать с фрагментами mp4, которые я получаю, прежде чем передавать их в SourceBuffer. Можете ли вы дать некоторые рекомендации? - person snowfrogdev; 08.01.2019

Я решил проблему с задержкой, используя параметр -g для установки количества кадров в группе. В моем случае я использовал -g 2. Я подозреваю, что если вы не сделаете это явным, фрагмент либо ждет, пока источник предоставит ключевой кадр, либо использует действительно большое значение по умолчанию для генерации ключевого кадра, прежде чем закрывать фрагмент и выгружать его на стандартный вывод.

person Brian Adams    schedule 30.07.2015
comment
Я предполагаю, что вы перекодируете видео, чтобы контролировать размер выходной группы изображений. Я использую ffmpeg в режиме копирования vcodec, таким образом, только ремиксируя его во фрагментированный mp4. Я попытался настроить источник моего видео (ip-камера), чтобы предоставить только ключевые кадры, но это не помогло с начальной задержкой. - person galbarm; 30.07.2015

Обычно буферизация для стандартного вывода отключена в случае вывода на консоль. Если вы запускаете ffmpeg из кода, буферизация включена, поэтому вы получите свои данные только тогда, когда буфер будет заполнен или команда завершится.

Вы должны устранить буферизацию stdout вашей ОС. В Windows это невозможно, imo, но в Ubuntu, например. Существует http://manpages.ubuntu.com/manpages/maverick/man1/stdbuf.1.html

person curtiss    schedule 26.08.2015