Я использую встроенный в FFmpeg gdigrab и dshow для записи дисплея и звука моей системы. Я кодирую несжатое видео H.264/AVC и несжатое аудио PCM/WAV.
У меня были две проблемы с синхронизацией аудио/видео:
Первоначально я использовал одну команду для захвата/кодирования. Что-то вроде следующего:
ffmpeg -hide_banner -rtbufsize 1000M -f gdigrab -framerate 60 -draw_mouse 0 \
-i "title=<window_name>" -f dshow -i audio="<sys_audio/mic>" -c:v libx264 \
-preset ultrafast -qp 0 -x264opts keyint=1 -c:a pcm_s16le -ac 1 "<out_file>.mkv"
Но мне удалось решить мою вторую проблему (дрейф) путем захвата видео и аудио в отдельных процессах FFmpeg. Обратите внимание, что я использую UNIX-подобную оболочку ( MSYS2 с BASH). Ниже приведен пример сценария оболочки, который я запускаю:
# capture audio & get PID
ffmpeg -hide_banner -rtbufsize 500M -f dshow -i audio="<sys_audio/mic>" \
-c:a pcm_s16le -ac 1 "<out_file>-audio.wav" & APID=$!
# capture video
ffmpeg -hide_banner -rtbufsize 1000M -f gdigrab -framerate 60 -draw_mouse 0 \
-i "<window_name>" -c:v libx264 -preset ultrafast -qp 0 -x264opts keyint=1 \
"<out_file>-video.mkv"
# get exit code of video process
VIDRET=$?
# send interrupt signal to audio process after video process exits
kill -s SIGINT ${APID}
# mux video & audio if video process exited okay
if [ "${VIDRET}" -eq "0" ]; then
ffmpeg -hide_banner -i "${OUTVID}" -i "${OUTAUD}" -map 0:0 -map 1:0 \
-c copy "<out_file>.mkv"
# delete temp video & audio streams if muxing succeeded
if [ "$?" -eq "0" ]; then
rm "${OUTVID}" "${OUTAUD}"
fi
fi
Таким образом, оставшаяся проблема заключается в том, что видео- и аудиоданные не начинают правильно выравниваться. Звук обычно где-то между 0 мс-300 мс опережает видео.
Это можно легко решить, снова запустив выходной файл через FFmpeg или отдельную программу, такую как Avidemux , для настройки задержки звука. Это можно сделать без перекодирования в Avidemux (насчет FFmpeg не уверен).
Однако я бы предпочел решить эту проблему в процессе/скрипте захвата, чтобы избежать дополнительного шага выравнивания данных вручную.
Недавно я выполнил очистку системы, удалив ненужные файлы, убедившись, что фрагментация на моем диске низкая, и отключил ненужные фоновые процессы. Но выравнивание аудио/видео по-прежнему часто не работает.
Итак, наконец, к простому и простому вопросу: есть ли способ заставить два процесса FFmpeg начать захват одновременно, чтобы получить видео и аудио как можно ближе к синхронизации? Могу ли я использовать системные часы в качестве начального значения?
Или есть лучший метод, чем я использую прямо сейчас? Например, один процесс FFmpeg, который будет захватывать оба, сохраняя при этом выравнивание данных и избегая дрейфа звука.
Мне кажется, что проблема просто в том, что аудиопроцесс запускается раньше видеопроцесса. Я слышал о передаче команд FFmpeg, но не уверен, как правильно это сделать. Я нашел некоторую информацию о трубопроводе, которую я пытаюсь выяснить:
Информация о FFmpeg: Официальная статическая 64-битная сборка от Zeranoe .
ffmpeg version N-92511-g0279cb4f69 Copyright (c) 2000-2018 the FFmpeg developers
built with gcc 8.2.1 (GCC) 20181017
configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig
--enable-gnutls --enable-iconv --enable-libass --enable-libbluray
--enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb
--enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine
--enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame
--enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264
--enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib
--enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc
--enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom
--enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va
--enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth
Это мои системные характеристики:
- Модель: HP Павильон 24-b217c
- ОС: Windows 10 64-битная
- Процессор: Intel Core i5-7400T @ 2,40 ГГц, 2401 МГц, 4 ядра
- Оперативная память: 12 ГБ
- Графические процессоры:
- nVidia GeForce 930MX
- Оперативная память: выделено 2 ГБ
- Intel HD Graphics 630
-- Редактировать --
Я понял команду pipe, но результат тот же. Аудио чуть впереди:
ffmpeg -hide_banner -rtbufsize 500M -f dshow -ac 1 -i audio="<sys_audio/mic>" \
-c:a pcm_s16le -f s16le pipe: | ffmpeg -y -hide_banner -rtbufsize 1500M -f gdigrab \
-thread_queue_size 128 -framerate 60 -draw_mouse 0 -i title="<window_name>" -f s16le \
-thread_queue_size 128 -i pipe: -map 0:0 -map 1:0 -c:v libx264 -preset ultrafast -qp 0 \
-x264opts keyint=1 -c:a copy "<out_file>.mkv"
После того, как вы некоторое время делали то, что вы описываете, с отдельными файлами, я недавно начал использовать вместо этого встроенные потоки. Уже поздно, и я устал, но вы должны увидеть, как это работает, и я могу уточнить позже, если вам нужно.
ffmpeg -hide_banner \
-re \
$videoDecodeMode \
-f pulse \
-i $audioOut \
-f pulse \
-i $audioMic \
-f x11grab \
-r $frameRate \
-s $resolution \
-i :0.0 \
-g 60 \
$videoEncodeMode \
-map 2:0 \
-metadata handler="video:1920x1080@60" \
"${exportName}" \
-map 0:0 \
-metadata title="audio:out" \
"${exportName%.*}_audioOut.wav" \
-map 1:0 \
-metadata title="audio:microphone" \
"${exportName%.*}_audioMic.wav" </dev/null &
ffmpeg -hide_banner \
-re \
$videoDecodeMode \
-f pulse \
-thread_queue_size 1024 \
-i $audioOut \
-f pulse \
-thread_queue_size 1024 \
-i $audioMic \
-f x11grab \
-r $frameRate \
-s $resolution \
-thread_queue_size 1024 \
-i :0.0 \
-g 60 \
-map 2:v \
-map 0:a \
-map 1:a \
-c:a aac -ac 2 -b:a 128k -r:a 48000 \
-metadata:s:v:0 handler="video:1920x1080@60" \
-metadata:s:a:2 handler="audio:out" \
-metadata:s:a:3 handler="audio:microphone" \
$videoEncodeMode \
"${exportName}" </dev/null &
ffmpeg -hide_banner \
-re \
$videoDecodeMode \
-f pulse \
-thread_queue_size 1024 \
-i $audioOut \
-f pulse \
-thread_queue_size 1024 \
-i $audioMic \
-f x11grab \
-r $frameRate \
-s $resolution \
-thread_queue_size 1024 \
-i :0.0 \
-g 60 \
-filter_complex \
"[1:a]aformat=channel_layouts=stereo,asplit=2[micOrig][micNew]\
;[micNew]\
compand=0:0.2:-26/-900|-16/-16|0/-10|10/-900:6:0:0:0\
[micCleaned]\
;[0:a][micCleaned]amix=inputs=2:duration=first[allAudio]" \
-map 2:v -map 0:a \
-map [allAudio] -map [micOrig] \
-c:a aac -ac 2 -b:a 128k -r:a 48000 \
-metadata:s:v:0 handler="video:1920x1080@60" \
-metadata:s:a:2 handler="audio:out" \
-metadata:s:a:1 handler="audio:combined" \
-metadata:s:a:3 handler="audio:microphone" \
$videoEncodeMode \
"${exportName}" </dev/null &
Если это Windows, то почему бы не использовать dshow вместо gdigrab?
Пожалуйста, посмотрите https://github.com/rdp/screen-capture-recorder-to-video-windows-free
Если и видео, и аудио будут передаваться через dshow, вы можете использовать ffmpeg следующим образом:
ffmpeg -f dshow -i video="VIDEO_DEVICE":audio="FIRST_AUDIO_DEVICE" -f dshow -i audio="ANOTHER_AUDIO_DEVICE" ...