Pull to refresh

Запись видео с автоматическим выкидыванием пауз свободным ПО с велосипедостроением

Reading time 10 min
Views 8.4K
Велосипед на таймлайне

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


Под катом рассказал о своем непростом пути к записи и редактированию видео с помощью свободного ПО, скриптинга на Ruby и подручных средств.


Теория


Начал я с изучения теории о записи видеоблогов по англоязычным YouTube-роликам. А из русскоязычных материалов — довольно полезным оказался вот этот курс (в частности модуль про видеоблог и первое видео о построении кадра из модуля про репортаж). Также бегло ознакомился с популярными фичами проприетарных видеоредакторов, чтобы более осознанно подойти к выбору свободного редактора.


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


Видеоредактор


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


Проблем очень много и потребовалось время на то, чтобы выбрать видеоредактор и изучить его баги, чтобы просто научиться справляться с базовыми вещами. В конечном счете остановился на Pitivi, просто потому что времени и так много потратил на поиски и эксперименты.


Звук из Flatpak


Поддерживаемый способ установки Pitivi требует Flatpak. Какое-то время я обходил его стороной, т.к. у меня в системе нет systemd и PulseAudio.


Оказывается systemd давно не требуется. Ну а PulseAudio — пришлось поставить и настроить было проще модифицировать Flatpak. Но правильней было бы поставить PulseAudio, просто это несколько нудно и непонятно стоит ли ожидать от него проблем с записью звука на имеющемся железе или нет.


Устанавливаем Pitivi, удаляем конфиги PulseAudio, запускаем:


$ sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
$ sudo flatpak install flathub org.pitivi.Pitivi
$ sudo find {/var/lib,~/.local/share}/flatpak/runtime -type f -name '*pulseaudio*.conf' -delete
$ flatpak run --device=alsa --branch=stable --arch=x86_64 --command=pitivi org.pitivi.Pitivi

Звука нет. Попробуем запустить что-нибудь попроще, например aplay:


$ sudo find /var/lib/flatpak/app/org.pitivi.Pitivi/x86_64 -type d -path '*/files/bin' -exec cp `which aplay` {} \;
$ flatpak run --device=alsa --branch=stable --arch=x86_64 --command=aplay org.pitivi.Pitivi /dev/urandom
ALSA lib dlmisc.c:162:(snd_dlsym_verify) unable to verify version for symbol _snd_pcm_empty_open
ALSA lib dlmisc.c:283:(snd1_dlobj_cache_get) symbol _snd_pcm_empty_open is not defined inside [builtin]
aplay: main:828: audio open error: No such device or address

Вероятно alsa-lib, входящий во Flatpak, был собран с --with-versioned. Быстрое решение — заменить libasound.so системным:


$ sudo find /var/lib/flatpak -type f -name libasound.so.2.0.0 -exec cp /usr/lib64/libasound.so.2.0.0 {} \;
$ find ~/.local/share/flatpak -type f -name libasound.so.2.0.0 -exec cp /usr/lib64/libasound.so.2.0.0 {} \; # если устанавливали от прав пользователя что-то

Для меня и этого оказалось недостаточно:


$ flatpak run --device=alsa --branch=stable --arch=x86_64 --command=aplay org.pitivi.Pitivi /dev/urandom
ALSA lib /var/tmp/portage/media-libs/alsa-lib-1.1.6-r1/work/alsa-lib-1.1.6/src/pcm/pcm_direct.c:1943:(snd1_pcm_direct_parse_open_conf) The field ipc_gid must be a valid group (create group audio)
aplay: main:828: audio open error: Invalid argument

Нужен еще конфиг ALSA:


$ sudo find /var/lib/flatpak -type d -name etc -exec cp /etc/asound.conf {} \;
$ find ~/.local/share/flatpak -type d -name etc -exec cp /etc/asound.conf {} \; # если устанавливали от прав пользователя что-то
$ flatpak run --device=alsa --branch=stable --arch=x86_64 --command=aplay org.pitivi.Pitivi /dev/urandom

Наконец-то можно использовать Pitivi.


Настройки рендеринга для Pitivi, к которым пришел в результате
  • container format: MP4
  • video
    • codec x264enc
    • advanced
      • encoding pass/type: constant quantizer
      • constant quantizer: 18
      • bitrate: 16384 kbit/s
      • speed quality preset: ultrafast
      • psychovisual tuning preset: film
  • audio
    • libav ALAC
  • на свой риск и страх использую «Never render from proxy files»
  • всё остальное — по умолчанию

Другие эффекты


Некоторые анимационные эффекты для текста я делаю с помощью скринкаста открытых на весь экран страниц, сверстанных с использованием reveal.js и animate.css. В reveal.js для некоторых слайдов добавляю звук перехода:


<section style="font-size: 5em">
  <audio data-autoplay src="/path/to/sound.wav"></audio>
  #1
</section>

Оказалось важно записывать скринкаст с 60 FPS, если текст очень большой. Скринкаст делаю так:


#!/bin/sh

SOUND_INPUT=shared_input_loopback
CHANNELS=2
SOUND_RATE=48000
FRAMERATE=60

DRAW_MOUSE=0
VIDEO_SIZE=$(xdpyinfo | awk '/dimensions:/ { print $2; exit }')
OUTPUT="${HOME}/video/screen/$(date --rfc-3339=seconds).mp4"

ffmpeg \
    -thread_queue_size 512 \
    -video_size "${VIDEO_SIZE}" \
    -framerate "${FRAMERATE}" \
    -f x11grab \
    -draw_mouse "${DRAW_MOUSE}" \
    -i :0.0+0,0 \
    -thread_queue_size 512 \
    -f alsa \
    -ac "${CHANNELS}" \
    -i "${SOUND_INPUT}"  \
    -ar "${SOUND_RATE}" \
    -vcodec libx264 -preset ultrafast -crf 18 \
    -acodec alac \
    -f ipod \
    "${OUTPUT}"

В моем случае shared_input_loopback — это устройство из конфига asound.conf.


Еще вот эта надстройка над ffmpeg для переходов между клипами, оказалась полезной.


Запись видео


Под рукой оказался телефон Meizu MX4, на котором я решил использовать фронтальную камеру и записываться с помощью Open Camera. Потребовалось некоторое время, чтобы натренировать себя смотреть в камеру и контролировать своё положение в пространстве, не делая типичных ошибок, вроде обрезания головы. При этом говорить достаточно четко, громко, жестикулировать и генерировать хоть какую-то мимику. Но это было только начало.


Что же побудило меня сделать автоматическую нарезку видео, да еще и на этапе записи?


  1. Тормоза и баги Pitivi при редактировании, в особенности при использовании инструмента Ripple Move/Edit, приводящие к необходимости периодического перезапуска Pitivi.
  2. Конкретно для меня процесс ручной нарезки видео — это одна из наиболее скучных вещей. Понятно, что полностью автоматизировать это не очень-то возможно (по крайней мере без сценария, в котором явно не указаны паузы, необходимые для осознания сказанного), но хотя бы оптимизировать этот процесс можно.

Вот какие требования к будущему велосипеду я себе поставил:


  1. Запись видео с помощью Android-телефона, а звука — с помощью ноутбука.
  2. Управление фокусом камеры.
  3. Возможность остановить запись, чтобы сохранить или удалить последний записанный фрагмент.
  4. Скачивание видео с телефона через USB, с повторными попытками и докачкой, без блокирования возможности записывать следующий фрагмент.
  5. Синхронизация звука.
  6. Определение наличия голоса и выкидывание пауз.
  7. Возможность быстро проиграть последние записанные фрагменты видео, с уже выкинутыми паузами.

Зачем столько контроля над устройствами на этапе записи? Почему бы просто не запустить запись на несколько часов подряд, а уж потом редактировать? Причин много:


  1. Банальный недостаток места на дисках.
  2. Склонность телефона перегреваться и быстро разряжаться при длительной записи.
  3. Неисправность сенсорного экрана из-за того, что телефон побывал в воде. А управлять фокусом как-то надо. Да и очередное нажатие создавало бы ненужную вибрацию устройства.
  4. Проблемы с перекидыванием больших файлов из-за плохого питания USB-портов на моем ноутбуке. В теории это решаемо с помощью USB-хаба с дополнительным питанием. Использовать сеть — слишком медленное решение.
  5. Желание быстро пересмотреть последние записанные фрагменты, чтобы убедиться в отсутствии ошибок и оперативно перезаписать, пока планета не повернулась не тем местом перед солнцем.
  6. Желание выкидывать явно плохие дубли как можно раньше, чтобы не тратить на них время и место на диске в будущем.
  7. Необходимость синхронизировать длинные аудио, записанные телефоном и ноутбуком. Тут может возникнуть рассинхронизация с видео из-за того, что фреймы аудиопотоков выбрасываются как при записи с ноутбука, так и при записи с телефона (что наверняка можно как-то решить, но не хочется рисковать и тратить время на эксперименты). Проще синхронизировать мелкие фрагменты по отдельности, тогда возможная рассинхронизация не будет заметна.
  8. Необходимость обрабатывать ситуацию, когда Open Camera перезапускает запись из-за достижения размера видео 4 GiB. Вероятно пришлось бы модифицировать Open Camera. Если это ограничение на 4 GiB невозможно убрать или увеличить — пришлось бы кидаться событием в ноутбук, чтобы тот пометил, что в этом месте произошел перезапуск записи.

Проще записываться небольшими фрагментами и сделать примитивную автоматизацию всего что можно. В качестве основного языка для разработки велосипеда выбрал Ruby. На самом деле сейчас я бы, вероятно, выбрал Python, но на тот момент я как раз изучал Ruby, а новые для меня языки я обкатываю на таких вот странных экспериментах.


Автоматическая нарезка видео


Информации в сети на эту тему не очень много. Про исследование Stanford и Adobe поздно вспомнил (что не страшно, мне всё равно менее навороченное решение нужно).


Нарезка происходит в 2 этапа: на этапе записи — грубая, на этапе рендеринга — более точная, с возможностью вручную подкорректировать слишком сильно обрезанные фрагменты. Грубая реализована с помощью VAD из WebRTC. Более точная — с помощью Google Speech (если конкретней — с помощью модификации проекта autosub, для генерирования субтитров к видео). Уверен что найдутся и более удачные решения, просто это оказалось лучшим из того, что удалось сделать быстро.


Если хотите разработать что-то подобное с использованием ffmpeg — придерживайтесь принципа не пытаться сделать слишком много за один вызов ffmpeg. Делайте промежуточные файлы и контролируйте каждый шаг, чтобы потом не заниматься поиском странных негугливаемых багов, вроде неправильного разрезания или непримененного эффекта.


Запускаю получившееся безобразие как-то так:


$ bin/vlog-recorder \
    --project /path/to/project \
    --debug true \
    --sound-settings ' --device=usb_card --format=dat' # аргументы к arecord

r - (RE)START recording
s - STOP and SAVE current clip
S - STOP and SAVE current clip, don't use auto trimming
d - STOP and DELETE current clip
p - PLAY last saved clip
f - FOCUS camera on center
h - show HELP
q / Ctrl+C - QUIT

[ stopped ] [ battery: 100% / 36°C ]

Аргументы к arecord мне нужны, чтобы явно указать устройство, дабы избежать периодических глюков, которые скорее всего происходят из-за ALSA-вского плагина dsnoop. Можно еще лог открыть, чтобы контролировать процесс скачивания файлов с телефона: tail -f /path/to/project/log.txt.


Быстро срендерить в одно видео для предпросмотра, можно так:


$ bin/vlog-render \
    --project /path/to/project \
    --language ru \
    --video-filters 'hqdn3d,hflip,curves=psfile=/path/to/curves.acv,vignette' \
    --speed 1.3 \
    --fps 60 \
    --preview true

Аргумент --video-filters — это фильтры, передаваемые в ffmpeg. Видео автоматически откроется в плеере mpv.


Можно также поменять местами или выкинуть оставшиеся ненужные дубли редактированием появившегося файла /path/to/project/render.conf, которые можно обнаружить благодаря распознанному голосу. Идея, кстати, не новая. Там же можно ускорить отдельные фрагменты и подредактировать неудачные разрезания видео, если такие есть. В следующий раз vlog-render перечитает render.conf и применит изменения.


Чтобы подготовить фрагменты для видеоредактора — нужно указать --preview false. Помимо фрагментов, которые будут лежать в output, он всё же объединит их в один файл output.mp4, потому что изначально я был не уверен:


  • буду ли я использовать мелкие клипы в Pitivi
  • или загружу одно длинное видео для дальнейшей нарезки (чтобы можно было применить ряд эффектов к «группе» клипов).

В основном использую первый вариант. Второй был полезен в одном видео с плохим светом: там я использовал лишь кусок output.mp4. Для второго варианта также может быть полезен скрипт vlog-play-segments: с помощью него можно быстро посмотреть все паузы между клипами в порядке убывания длительности. Это поможет более точно подредактировать render.conf и сэкономить потом время на редактировании этого длинного куска видео в Pitivi.


Получившиеся мелкие клипы можно загрузить одним разом на таймлайн в Pitivi: выделяем все импортированные клипы и тащим с помощью drag-n-drop.


Крепление для телефона


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


Крепление для телефона

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


Запись звука


Приемлемый звук очень критичен. Под рукой оказался микрофон Boya BY-M1. Хоть его и рекламируют как всенаправленный микрофон, хороший звук на практике получается только когда используешь его как однонаправленный.


Стойку для микрофона сделать еще проще: берем попавшуюся под руку бутылку от гранатового сока, рулон скотча и собираем этот конструктор воедино:


Стойка для микрофона

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


Звуковая карта


В моем случае это ASUS Xonar U3. Оказалось, правда, что она не совместима с таким микрофоном: у микрофона штекер CTIA, рассчитанный на телефоны. Проблема решилась переходником в штекеры TRS для микрофона и наушников. И найти его было непросто: производители таких переходников редко пишут детали. В моем случае помог некий Cablexpert CCA-418W.


Еще одна проблема этой карты — в DC offset в правом канале при записи. Что никак не мешает, т.к. записываюсь я все равно в моно. А для софта, который не позволяет выставить моно сделал перенаправление хорошего канала в плохой, средствами ALSA.


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


Обработка звука


Звук редактирую в наушниках (в моем случае это Pioneer SE-M390), на громкости выше чем та, на которой обычно слушаю музыку. Алгоритм примерно такой:


  1. С помощью Pitivi рендерю отдельно звук (с использованием всё тех же ALAC и MP4). Зачастую делаю несколько отдельных дорожек, выбирая конкретные слои в Pitivi и временно удаляя ненужные.
  2. Если полученные файлы сразу же загрузить в Audacity — мы потеряем растяжения/сжатия аудиопотока, которые потом могут привести к рассинхронизации видео и аудио. Что неочевидно, это происходит не со всеми видео. Чтобы этого не случилось — просто применяем эти растяжения/сжатия: ffmpeg -async 1 -i input.mp4 output.flac
  3. Загружаем все дорожки в Audacity. Добавляем фоновую музыку, если нужно.
  4. Для всех дорожек устанавливаем желаемую громкость с помощью Gain.
  5. К дорожке с голосом применяем эффекты Noise Reduction (в моем случае двойной), Compressor и Equalization по советам из этого видео.
  6. Выравниваем и усиливаем громкость у дорожки с голосом. Один из классических способов — это Normalize, Amplify, Limiter и снова Normalize, однако таким подходом мне пока не удалось получить желаемое качество звука. Временно поступаю так: сначала делаю Gain всей дорожке так, чтобы наиболее громкая часть звучала без перегрузок, а далее вручную применяю Amplify для отдельных фрагментов. Update: еще один мощный способ — RMS Normalize, Limiter и обычный Normalize. Настройки RMS Normalize и Limiter можно взять отсюда. Всё же этот способ мне не пригодился, т.к. я всё равно решил перейти на другой микрофон (Zoom H1n) со встроенным Limiter, который меня устраивает (так что с новым микрофоном мне скорее всего придется делать только обычный Normalize, вместо всех этих вещей).
  7. Микрофон иногда записывает звук с некими дефектами, которые похожи на щелчки. Их можно удалить с помощью эффекта Spectral edit multi tool. Чаще всего его приходится применять несколько раз подряд для выделенной области, с помощью Ctrl+R. Update: благодаря новому микрофону выяснил, что эти дефекты связаны с чем-то внешним, скорее всего это комбинация шума во рту и других посторонних звуков.
  8. Экспортируем из Audacity во FLAC и объединяем всё в один файл: ffmpeg -i sound.flac -an -i video.mp4 -c copy output.mkv
  9. По крайней мере первое видео я проверял на разных громкостях и разных устройствах.

Результат


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


Удачи в разработке программ и создании видеоблогов!


Update: перевел эту статью для своего англоязычного блога.

Only registered users can participate in poll. Log in, please.
Хотели бы вы запустить свой видеоблог?
8.7% Да, собираюсь запустить или запустил 2
21.74% Да, но не хватает оборудования/времени/умений 5
52.17% Нет, не о чем рассказывать 12
4.35% Нет, не хочу иметь дело с негативной публикой 1
13.04% Нет, их и так слишком много на интересную мне тематику 3
23 users voted. 9 users abstained.
Tags:
Hubs:
+17
Comments 11
Comments Comments 11

Articles