LINUX.ORG.RU

ffmpeg - синхронизировать два входящих видеопотока

 


0

1

Привет! У меня есть два источника входящего видео и три источника входящего аудио. Надо взять все это добро и синхронно записать в один файл при помощи ffmpeg - видео объединяется в формате "картинка в картинке", аудио добавляется отдельными дорожками. Проблема в том что у источников у меня разный fps - /dev/video0 отдает 60 FPS , /dev/video1 - 30 FPS. Результирующий FPS ожидается 60. Так вот, если убрать 30-фпс'ный источник то проблем никаких. Если с обоих источников брать 30 и записывать результат в 30 - проблем никаких. Если добавить 30-fps-input после инпута на 60 и перед входящими аудиопотоками - аудио рассинхронивается с 60-фпсным потоком. Если добавить 30fps в самое начало или конец цепочки инпутов - оно получается несинхронным с другим видеопотоком.

Я перепробовал уже кучу вариантов async\vsync, экспериментировал с таймстампами, pts'ами и прочей этой лабудой. Это не дает вообще ничего. Самое классное что ffmpeg при запуске показывает что типа "есть поток 1, у него время запуска 10; а вот поток 2 - у него время запуска 15", и дальше у меня эти пять секунд расхождения стабильно присутствуют в видео. Советы типа "сделай оффсет" не подойдут - эта задержка каждый раз разная, и я понятия не имею чем она вызвана, потому что стоящий рядом mplayer\vlc открывают девайсы практически мгновенно, ffmpegу надо тупить по 10 секунд на каждый инпут. Какого хрена он сам не может засинхронить потоки, при том что знает какая разница между ними, я не могу понять.

В общем, прошу помощи, у меня сил воевать с этим больше нет, идеи кончились, я несколько дней уже туплю над этим и похоже сам не справлюсь. Ах, да, рядом стоящий OBS отлично справляется с задачей синхронизации инпутов вообще из коробки (но не делает некоторые важные штуки, которые может ffmpeg).

Текущий вариант запуска с логами (тут я подождал всего пять секунд, но не важно, оно и через полчаса абсолютно также работает):

$ ffmpeg -y -f v4l2 -thread_queue_size 512 -input_format yuyv422 -pix_fmt yuv420p -s 320x240 -framerate 30 -i /dev/video1 -f v4l2 -thread_queue_size 512 -input_format yuyv422 -pix_fmt yuv420p -s 1920x1080 -framerate 60 -i /dev/video0 -f pulse -guess_layout_max 0 -thread_queue_size 512 -channels 2 -i alsa_input.dev1.analog-stereo -f pulse -guess_layout_max 0 -thread_queue_size 512 -channels 2 -i alsa_input.dev2.analog-stereo -f pulse -guess_layout_max 0 -thread_queue_size 512 -channels 2 -i alsa_input.dev3.analog-stereo -filter_complex "[0:v]pad=height=ih+10:color=black[b]; [1:v][b]overlay=(main_w-overlay_w):main_h-overlay_h[o]" -c:a libfdk_aac -ac 2 -q:v 2 -c:v mjpeg -r 60 -muxdelay 0 -map [o] -map 2:a /tmp/output.mkv

ffmpeg version 4.2.2 Copyright (c) 2000-2019 the FFmpeg developers
  built with gcc 9 (Debian 9.2.1-28)
  configuration: --disable-decoder=amrnb --disable-decoder=libopenjpeg --disable-gnutls --disable-libopencv --disable-podpages --disable-sndio --disable-stripping --enable-avfilter --enable-gcrypt --enable-gpl --enable-ladspa --enable-libaom --enable-libaribb24 --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libfdk-aac --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libilbc --enable-libjack --enable-libkvazaar --enable-liblensfun --enable-libmp3lame --enable-libmysofa --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-libwebp --enable-libzvbi --enable-lv2 --enable-nonfree --enable-openal --enable-opencl --enable-opengl --enable-openssl --enable-postproc --enable-pthreads --enable-shared --enable-version3 --incdir=/usr/include/x86_64-linux-gnu --libdir=/usr/lib/x86_64-linux-gnu --prefix=/usr --toolchain=hardened --enable-frei0r --enable-chromaprint --enable-libx264 --enable-libiec61883 --enable-libdc1394 --enable-vaapi --enable-libmfx --enable-libvmaf --disable-altivec --shlibdir=/usr/lib/x86_64-linux-gnu
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
  libpostproc    55.  5.100 / 55.  5.100
Input #0, video4linux2,v4l2, from '/dev/video1':
  Duration: N/A, start: 21674.247421, bitrate: 36864 kb/s
    Stream #0:0: Video: rawvideo (YUY2 / 0x32595559), yuyv422, 320x240, 36864 kb/s, 30 fps, 30 tbr, 1000k tbn, 1000k tbc
Input #1, video4linux2,v4l2, from '/dev/video0':
  Duration: N/A, start: 21682.714085, bitrate: 1990656 kb/s
    Stream #1:0: Video: rawvideo (YUY2 / 0x32595559), yuyv422, 1920x1080, 1990656 kb/s, 60 fps, 60 tbr, 1000k tbn, 1000k tbc
Input #2, pulse, from 'alsa_input.dev1.analog-stereo':
  Duration: N/A, start: 1584448360.733878, bitrate: 1536 kb/s
    Stream #2:0: Audio: pcm_s16le, 48000 Hz, 2 channels, s16, 1536 kb/s
Input #3, pulse, from 'alsa_input.dev2.analog-stereo':
  Duration: N/A, start: 1584448361.639887, bitrate: 1536 kb/s
    Stream #3:0: Audio: pcm_s16le, 48000 Hz, 2 channels, s16, 1536 kb/s
Input #4, pulse, from 'alsa_input.dev3.analog-stereo':
  Duration: N/A, start: 1584448361.588144, bitrate: 1536 kb/s
    Stream #4:0: Audio: pcm_s16le, 48000 Hz, 2 channels, s16, 1536 kb/s
Stream mapping:
  Stream #0:0 (rawvideo) -> pad (graph 0)
  Stream #1:0 (rawvideo) -> overlay:main (graph 0)
  overlay (graph 0) -> Stream #0:0 (mjpeg)
  Stream #2:0 -> #0:1 (pcm_s16le (native) -> aac (libfdk_aac))
Press [q] to stop, [?] for help
[swscaler @ 0x55d4e89e5b00] deprecated pixel format used, make sure you did set range correctly
Output #0, matroska, to '/tmp/output.mkv':
  Metadata:
    encoder         : Lavf58.29.100
    Stream #0:0: Video: mjpeg (MJPG / 0x47504A4D), yuvj420p(pc, progressive), 1920x1080, q=2-31, 200 kb/s, 60 fps, 1k tbn, 60 tbc (default)
    Metadata:
      encoder         : Lavc58.54.100 mjpeg
    Side data:
      cpb: bitrate max/min/avg: 0/0/200000 buffer size: 0 vbv_delay: -1
    Stream #0:1: Audio: aac (libfdk_aac) ([255][0][0][0] / 0x00FF), 48000 Hz, stereo, s16, 139 kb/s
    Metadata:
      encoder         : Lavc58.54.100 libfdk_aac
[libfdk_aac @ 0x55d4e89ab380] Queue input is backward in time
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 149, current: 2; changing to 149. This may result in incorrect timestamps in the output file.
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 149, current: 24; changing to 149. This may result in incorrect timestamps in the output file.
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 149, current: 45; changing to 149. This may result in incorrect timestamps in the output file.
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 149, current: 66; changing to 149. This may result in incorrect timestamps in the output file.
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 149, current: 88; changing to 149. This may result in incorrect timestamps in the output file.
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 149, current: 109; changing to 149. This may result in incorrect timestamps in the output file.
[libfdk_aac @ 0x55d4e89ab380] Queue input is backward in time
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 149, current: 130; changing to 149. This may result in incorrect timestamps in the output file.
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 152, current: 71; changing to 152. This may result in incorrect timestamps in the output file.
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 152, current: 92; changing to 152. This may result in incorrect timestamps in the output file.
[libfdk_aac @ 0x55d4e89ab380] Queue input is backward in time
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 152, current: 114; changing to 152. This may result in incorrect timestamps in the output file.
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 152, current: 135; changing to 152. This may result in incorrect timestamps in the output file.
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 152, current: 124; changing to 152. This may result in incorrect timestamps in the output file.
[matroska @ 0x55d4e89a57c0] Non-monotonous DTS in output stream 0:1; previous: 152, current: 145; changing to 152. This may result in incorrect timestamps in the output file.
[libfdk_aac @ 0x55d4e89ab380] Queue input is backward in time
frame=  249 fps= 43 q=2.0 Lsize=   23060kB time=00:00:05.85 bitrate=32286.8kbits/s speed=1.01x    
video:22942kB audio:105kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.059679%

UPD: Забыл сказать - я пробовал всякими способовами конвертить входящие 30 fps в 60 (через minterpolate например) - это получается какая-то бредовая жесть с дерганными кадрами (даже когда говоришь что нужно дублировать рядомстоящий такое ощущение что оно их дергает из всего потока). И все равно даже так синхронности не удалось добиться.

Очень надеюсь на вашу помощь! Заранее огромное спасибо!

Ответ на: комментарий от anonymous

Мне нужно наоборот из 30 сделать 60. Ну точнее мне нужно подружить тридцатифпсное видео с шестидесятифпсным, если оно так и останется в 30, просто каждый кадр будет длиться в два раза дольше оригинала мне все равно, лишь бы визуально не особо выделялось.

micronekodesu ★★ ()
Последнее исправление: micronekodesu (всего исправлений: 1)

С глобальными флагами -fflags +genpts -async 1 и -ts abs на каждый видеоинпут получается более-менее сносно (30fps стоит между 60fps и аудиоинпутами, что раньше вызывало дикий рассинхрон), но все равно

[video4linux2,v4l2 @ 0x5596a4923b80] Detected monotonic timestamps, converting
Input #0, video4linux2,v4l2, from '/dev/video0':
  Duration: N/A, start: 1584459328.048961, bitrate: 1990656 kb/s
    Stream #0:0: Video: rawvideo (YUY2 / 0x32595559), yuyv422, 1920x1080, 1990656 kb/s, 60 fps, 60 tbr, 1000k tbn, 1000k tbc
[video4linux2,v4l2 @ 0x5596a4927d00] Detected monotonic timestamps, converting
Input #1, video4linux2,v4l2, from '/dev/video1':
  Duration: N/A, start: 1584459328.232054, bitrate: 36864 kb/s
    Stream #1:0: Video: rawvideo (YUY2 / 0x32595559), yuyv422, 320x240, 36864 kb/s, 30 fps, 30 tbr, 1000k tbn, 1000k tbc
Input #2, pulse, from 'alsa_input.dev1.analog-stereo':
  Duration: N/A, start: 1584459328.918121, bitrate: 1536 kb/s
    Stream #2:0: Audio: pcm_s16le, 48000 Hz, 2 channels, s16, 1536 kb/s
Input #3, pulse, from 'alsa_input.dev2.analog-stereo':
  Duration: N/A, start: 1584459329.915147, bitrate: 1536 kb/s
    Stream #3:0: Audio: pcm_s16le, 48000 Hz, 2 channels, s16, 1536 kb/s
Input #4, pulse, from 'alsa_input.dev3.analog-stereo':
  Duration: N/A, start: 1584459331.551276, bitrate: 1536 kb/s
    Stream #4:0: Audio: pcm_s16le, 48000 Hz, 2 channels, s16, 1536 kb/s

видно что "start" на три секунды уезжает в последнем инпуте относительно первого (ну и каждый инпут получается с лагом), и вот это отставание (ну или опережение) реально присутствует в итоговом файле и заметно.

micronekodesu ★★ ()

Что если не указывать -framerate перед -i ? fps для нужной дорожки можно изменить (не сменой скорости, а дублированием или выкидыванием кадров) точно указывая для какой дорожки применять фильтр (нумерация начинается с нуля). Я только не понял, относительно входных файлов или выходных. На примере параметра -metadata
-metadata:s:v:0 (первая видеодорожка)
-metadata:s:a:1 (вторая аудиодорожка)
-metadata:s:s:0 (первые субтитры)
Или можно просто по номерам
-metadata:1 (вторая дорожка не важно с чем).
Соответственно, в твоем случае должно быть
-filter:v:0 fps=fps=60 или -filter:s:v:0 fps=fps=60 если обрабатываемое видео идет самым первым.

Чтобы ffmpeg гарантированно взял все дорожки в выходной файл, надо дописать -map 0:0 -map 1:0 -map 2:0 -map 3:0 и т.д. Пояснение 1:0 значит второй файл 1, первая дорожка 0 (и единственная). Нумерация с нуля. Например, если нужно все дорожки включить из ЕДИНСТВЕННОГО файла, достаточно было указать -map 0 (ffmpeg по умолчанию берет только первые или дефолтные дорожки).

minterpolate не пользуйся. Очень медленный, могут быть сбои. При кратном изменении достаточно простейших алгоритмов (параметры -vf fps или -r).

Non-monotonous DTS

Обычно это некритично.

anonymous ()
Ответ на: комментарий от anonymous

Что если не указывать -framerate перед -i ? fps для нужной дорожки можно изменить

Можно изменять фреймрейт после того как данные из инпута прочитаны (как ты сказал через дублирование\выкидывание кадров), а можно сказать инпуту в каком фреймрейте данные нужно отдавать - для этого и используется "-framerate".

-map 0:0 -map 1:0 -map 2:0 -map 3:0

Там немного не так - у меня фильр использется, который оверлеит одну дорожку с другой - он генерит новый видеопоток и его нужно маппить отдельно (у меня он называется "[о]" в примере)

Нумерация с нуля.

Для инпутов, после мапа используется другая система - нумерация сбрасывается и отсчет идет по порядку каналов в маппинге, то есть если сделать -map 1:0 и потом писать файл через какой-нибудь какой-нибудь tee то эта дорожка будет уже 0:0.

Например, если нужно все дорожки включить из ЕДИНСТВЕННОГО файла, достаточно было указать -map 0

Для единственного файла можно вообще ничего не писать.

micronekodesu ★★ ()

В общем, дело тут было не в параметрах ffmpeg - это кривая реализация v4l2\uvcvideo или того как оно используется в ffmpeg - через gstreamer это все работает вообще без танцев. Единственное что пока инпут не подгрузится данные с него не пишутся, то есть первые несколько секунд у меня не все потоки сохраняются, но это фигня - когда все инпуты подгружаются дальше все идет синхронно и как и ожидалось. Вот описание как это все завести - https://linuxtv.org/wiki/index.php/V4L_capturing .

// Убил на это две недели, менял камеру, менял пекарни, добавил оперативу, писал на лор, писал в чатик разрабам ффмпег... Вы даже не представляете как я офигел и на сколько был рад когда это все наконец-то заработало...

micronekodesu ★★ ()
Последнее исправление: micronekodesu (всего исправлений: 1)