2016-02-11 10 views
6

Я пытаюсь преобразовать поток udp в фреймы с помощью ffmpeg. Я выполняю следующую команду:Буферизация при преобразовании потока в фреймы с помощью ffmpeg

ffmpeg -loglevel debug -strict 2 -re -i "udp://192.168.15.50:3200?fifo_size=1000000&overrun_nonfatal=1" -r 8 -vf scale=432:243 -f image2pipe -vcodec ppm pipe:1 

Это происходит с разными типами потоков, mpeg2video и h264. Cpu load для обработки ядра этот конкретный поток составляет менее 30%, его низкокачественный sd-поток с разрешением 640x576.

Он работает хорошо в течение всего времени, однако иногда, время от времени возникает латентность, и кадры прибывают позже. Поэтому я хочу ровно 8 кадров в секунду, но иногда я получаю меньше, иногда больше.

Почему возникает эта латентность и как я могу ее уменьшить?

обновление: я попытался изменить его:

ffmpeg -loglevel debug -i "udp://192.168.15.50:3200?fifo_size=1000000&overrun_nonfatal=1" -r 8 -preset ultrafast -fflags nobuffer -vf scale=432:243 -f image2pipe -vcodec ppm pipe:1 

Но я все еще получаю этот вопрос. Например, в журнале FFmpeg я получаю:

[2016/02/11 13:32:30] frame= 7477 fps=8.0 q=-0.0 size= 2299638kB time=00:15:34.62 bitrate=20156.4kbits/s dup=7 drop=15867 ^M*** dropping frame 7477 from stream 0 at ts 7475 
[2016/02/11 13:32:30] ***dropping frame 7477 from stream 0 at ts 7476 
[2016/02/11 13:32:30] ***dropping frame 7478 from stream 0 at ts 7476 
[2016/02/11 13:32:32] Last message repeated 1 times 
[2016/02/11 13:32:32] frame= 7479 fps=8.0 q=-0.0 size= 2300253kB time=00:15:34.87 bitrate=20156.4kbits/s dup=7 drop=15871 ^M*** dropping frame 7479 from stream 0 at ts 7477 

Как вы можете видеть, во втором 31, ни один кадр не выводится ... и FFmpeg сообщили время между двумя кадрами является команда 0.25s

ответ

10

FFmpeg размещен в вопрос обычно передается в другой двоичный файл. Этот двоичный файл сохраняет фреймы, предоставленные ffmpeg, и выполняет некоторую обработку на них.

В начале я не использовал "fifo_size = 1000000 & overrun_nonfatal = 1" варианты, и я получаю следующее сообщение об ошибке от FFmpeg

[udp @ 0x4ceb8a0] Circular buffer overrun. To avoid, increase fifo_size URL option. To survive in such case, use overrun_nonfatal option 
udp://192.168.15.50:3200: Input/output error 

, а затем FFmpeg обрушится. Чтобы избежать этого, я добавил «fifo_size = 1000000 & overrun_nonfatal = 1», как предлагает ffmpeg.

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

Как уже упоминалось, проблем с процессором не возникало. поэтому изначально, мы подозревали, что УДП поток, в частности, UdP размер буфера:

https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Web_Platform/5/html/Administration_And_Configuration_Guide/jgroups-perf-udpbuffer.html

поэтому мы изменили UdP размер буфера с:

sysctl -w net.core.rmem_max=26214400 

и изменил команду FFmpeg к «УДП: //231.20 .20.8: 2005? Buffer_size = 26214400 "

Однако, это не исправить проблему. ffmpeg все равно получит «Круговое переполнение буфера» и сбой. И я не смог воспроизвести этот круговой буфер переполнения, это просто случалось случайным образом.

Моя следующая мысль была размер буфера трубы, так как я нашел следующее:

http://blog.dataart.com/linux-pipes-tips-tricks/ Размер буфера, так как ядра версии 2.6.11 является 65536 байт (64K) и равна памяти страницы в старых ядрах. При попытке чтения из пустого буфера процесс чтения блокируется до появления данных. Аналогично, если вы попытаетесь записать в полный буфер, процесс записи будет заблокирован до тех пор, пока не будет доступно необходимое пространство. http://ffmpeg.gusari.org/viewtopic.php?f=12&t=624 Poster1: Что вызывает эти круговые буферные переполнения? Мое предположение заключается в том, что ffmpeg считывает входной поток в вышеупомянутый круговой буфер, а затем генерирует выходной поток, который также считывается из того же самого буфера. Переполнение произойдет, когда код, генерирующий вывод, не будет соответствовать скорости, с которой он записывается в буфер, верно? Poster2: Глядя на исходный код, кажется, что буфер переполняется либо слишком быстрым вводом, либо слишком медленным выходом (медленный cpu?). Ваше предположение верно.

Итак, теория заключалась в том, что наш бинарный файл не читает трубу достаточно быстро. В результате труба блокируется, и ffmpeg не может ее записать, и THAT приводит к переполнению буфера udp fifo (ffmpeg продолжает чтение udp INTO FIFO, но не может записать ОТ его в нашу трубу)

Мне удалось доказать эту теорию (в отдельных клеммах):

mkfifo mypipe 
ffmpeg -loglevel debug -i "udp://192.168.15.50:3200?fifo_size=1000000&overrun_nonfatal=1" -r 8 -preset ultrafast -fflags nobuffer -vf scale=432:243 -f image2pipe -vcodec ppm pipe:1 > mypipe 
cat <mypipe> /dev/null # run this for 10 seconds, allowing ffmpeg to start. then pause it with CTRL-Z and see ffmpeg crashing because it cannot read more udp stream 

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

Однако он также сохранил кадры на жесткий диск. и в НЕКОТОРЫХ ПУНКТАХ (иногда 12 минут, иногда 15 часов), операции с дисками замедлялись из-за операций чтения/записи (это был bcache (SSD и HDD hybrid, используя SSD в качестве кеша). Я случайно поймал этот факт, когда я удалял несколько миллионов файлов с этого диска paralelly к отладке.

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

так причина UdP кольцевого буфера накатом выпуска и в конечном итоге TimeShift был HDD и теоретическое решение - это SSD.

Это исследование заняло около 3 недель, поэтому все это в надежде, что оно, по крайней мере частично, поможет кому-то в будущем.

обновления к этому:

я также обнаружил другие узкое место, вызывающие этот же вопрос позже (замена HDD не было достаточно), который было TCP переполнения буфера сокета, вызванное POSTGRES вставок на внутреннем интерфейсе.

весь трубопровод выглядит следующим образом:

udp_videostream-> ffmpeg-> linux_pipe-> our_client_side_binary-> TCP-> our_server_side_binary-> Postgres

Postgres запросы были иногда медленно, который причинял наш сервер для прочитайте TCP-сокет медленнее, чем наш_бинал нажимал на него. в результате tcp-сокет будет заблокирован (максимум 4 Мб), и в результате клиент заблокирует его входной канал, и в результате этого ffmpeg будет сбой с этой ошибкой CBO.