Редактиране на видео под Линукс

[ video  linux  software  ]

Съвети за редактиране на видео под Линукс

Въведение

Тук ще споделя моя опит с приложения и команди за обработка на видео под Линукс.

Docker

Docker е много удобен инструмент, когато трябва да се работи със сложни взаимовръзки между библиотеките. Препоръчителният начин за инсталация е описан на https://docs.docker.com/engine/install/ubuntu/#install-using-the-convenience-script:

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker ВАШИЯТ–ПОТРЕБИТЕЛ

ffmpeg

TODO качество: https://stackoverflow.com/questions/20435420/ffmpeg-how-to-apply-filters-without-losing-quality https://stackoverflow.com/questions/17793150/default-ffmpeg-codec-when-nothing-is-specified#:~:text=ffmpeg%20will%20re-encode%20each,then%20mpeg4%20will%20be%20used

Стабилизиране на видео

Резултатът от стабилизирането може да се види тук:

Плъгинът vid.stab не е компилиран стандартно в Ubuntu 18.04 и за това командите се изпълняват в докер.

Прави се на две стъпки - в първата се установява на къде е отместено (векторът на отместването) видеото във всеки един момент от време (симулира жироскоп), а на втората стъпка се променя самото видео (прилага се трансформация с обратния вектор).

  • един файл
docker run --rm  -v "$(pwd)":/data jrottenberg/ffmpeg -i /data/ВИДЕО.MP4 -vf vidstabdetect=accuracy=15:shakiness=10:mincontrast=0.3:show=0:result=/data/transforms.trf -an -f null -
docker run --rm  -v "$(pwd)":/data jrottenberg/ffmpeg -i /data/ВИДЕО.MP4 -vf vidstabtransform=smoothing=20:input="/data/transforms.trf:optzoom=0,unsharp" /data/РЕЗУЛТАТ.mp4
  • всички файлове в директория
#!/bin/bash
set -e
relativeFolder="${1}"
folder=$(cd "${relativeFolder}"; pwd)
for relativeFile in "${folder}"/*.MP4; do
        [ -e "${relativeFile}" ] || continue
        filename="${relativeFile##*/}"
        docker run --rm  -v "${folder}":/data jrottenberg/ffmpeg -i "/data/${filename}" -vf vidstabdetect=accuracy=15:shakiness=10:mincontrast=0.3:show=0:result=/data/transforms.trf -an -f null -
        docker run --rm  -v "${folder}":/data jrottenberg/ffmpeg -i "/data/${filename}" -vf vidstabtransform=smoothing=20:input="/data/transforms.trf:optzoom=0,unsharp" "/data/output-${filename}"
done
rm -rf "${folder}"/transforms.trf

sudo chown "$(whoami)":"$(whoami)" output-*.MP4
  • за ползване на GPU (добавя се :vaapi)

docker run (...) jrottenberg/ffmpeg:vaapi (...)

Телеметрия към видео (Picture in Picture)

Резултатът от добавянето на телеметрия може да се види тук:

Съществуват множество приложения за добавяне на телеметрични данни (локация, скорост, посока и други) към видео, но те са платени, за Windows, за конкретен модел камера или резултатът не е особено сполучлив. За това тук ще опиша един друг вариант. При него използвам gpx файл от GPS, който качвам на ayvri и записвам видеото посредством SimpleScreenRecorder. Резултатът може да видите тук: резултат, базиран на оригинално видео и телеметрия.

  • добавям gpx файла в ayvri (може и през интеграцията със страва)
  • отварям създаденото видео, преди да зареди натискам пауза, намалявам скоростта до х1, натискам STATS за да се покажат всички телеметрични данни
  • в конзолата на Chromе (Ctrl+Shift+I или F12) изпълнявам document.getElementById('StatsPanelContainer').style = "padding-right: 750px; padding-top: 337px;";document.getElementById("StatsPanelContainer").childNodes[0].childNodes[1].remove();document.getElementById("StatsPanelContainer").childNodes[0].childNodes[0].childNodes[3].remove();
    • имайте предвид че това маха някои от метриките, а тъй като те зависят от зададения тип на записа (туризъм, ски, парапланер) може да се наложи да се промени скрипта по-горе в зависимост от типа на активността
    • по-принцип избягвайте да изпълнявате такива скриптове в браузъра си без да ги разбирате напълно, защото това може да доведе до големи неприятности (открадване на акаунт и други) - в случая (както се вижда - .style = ...; .remove();) само местя и трия разни компоненти, но скриптът може да прави доста повече неща за това бъдете внимателни!
  • пускам SimpleScreenRecorder, настройвам го да записва цял екран, размерите на резултатното видеото да е 0.6 от реалните размери (така или иначе го намаляваме за да се вижда само в края на екрана), отказвам записът на аудио, избирам добро качество
  • пускам видеото на цял екран (от бутона долу-дясно)
  • избирам правилната гледна точка в ayvri така че маршрутът да е някъде в средата на екрана (и да не се закрива от насрещни възвишения/върхове), пускам запис в SimpleScreenRecorder посредством Ctrl+R и натискам Play след това го спирам след края на видеото в ayvri.
  • ffmpeg -i "ВИДЕО.MP4" -i "ТЕЛЕМЕТРИЯ.mkv" -filter_complex "[1]trim=31,setpts=PTS-STARTPTS[telemetry];color=c=white,vignette=angle=PI/2:aspect=4/3:x0=w/2+30:y0=h/2-10[mask];[mask][telemetry]scale2ref[scaledmask][scaledtelemetry];[scaledtelemetry][scaledmask]alphamerge,crop=exact=1:x=0:y=ih/2:w=iw/2+245:h=ih-410[pip]; [0][pip] overlay=main_w-overlay_w:0:eof_action=endall" -acodec copy РЕЗУЛТАТ.mp4
    • в зависимост от размерите на монитора/прозореца ще трябва да промените w=iw/2+245:h=ih-410 за да позиционирате телеметрията горе-дясно
    • в зависимост от отместването във времето между телеметрията и видеото ще трябва да промените колко да изрежете от началото на телеметрията (trim=31)
    • ако искате телеметрията да избледнява повече или по-малко към краищата променете angle=PI/2:aspect=4/3:x0=w/2+30:y0=h/2-10, където PI/2 контролира колко бързо да избледнява, 4/3 - в коя посока колко да избледнява, x0=w/2+30:y0=h/2-10 от къде да започне избледняването - в случая не е от центъра и по този начин се контролира колко да е избледняло към краищата
  • допълнителна информация: https://www.oodlestechnologies.com/blogs/PICTURE-IN-PICTURE-effect-using-FFMPEG/
  • може да промените изглежда от ayvri като ползвате Chrome и натиснете с десния клавиш на мишката върху елемента и след това Inspect и изтриване/промяна на съответния HTML елемент, това може да стане и с javascript, например: document.getElementById('StatsPanelContainer').style = "padding-right: 750px; padding-top: 337px;";document.getElementById("StatsPanelContainer").childNodes[0].childNodes[1].remove();document.getElementById("StatsPanelContainer").childNodes[0].childNodes[0].childNodes[3].remove();
    • имайте предвид че това маха някои от метриките, а тъй като те зависят от зададения тип на записа (туризъм, ски, парапланер) може да се наложи да се промени скрипта по-горе в зависимост от типа на активността
    • по-принцип избягвайте да изпълнявате такива скриптове в браузъра си без да ги разбирате напълно, защото това може да доведе до големи неприятности (открадване на акаунт и други) - в случая (както се вижда - .style = ...; .remove();) само местя и трия разни компоненти, но скриптът може да прави доста повече неща за това бъдете внимателни!
  • за проба може да се генерира видео с по-лошо качество, но по-бързо и по този начин да се ориентирате дали видеото и телеметрията са синхронизирани: ffmpeg -i "ВИДЕО.MP4" -i "ТЕЛЕМЕТРИЯ.mkv" -filter_complex "[1]copy [pip]; [0][pip] overlay=main_w-overlay_w:0" -profile:v main -level 3.1 -b:v 140k -ar 44100 -ab 128k -s 720x400 -vcodec h264 -acodec copy РЕЗУЛТАТ.mp4 или ffmpeg -i "ВИДЕО.MP4" -i "ТЕЛЕМЕТРИЯ.mkv" -filter_complex "[1]copy [pip]; [0][pip] overlay=main_w-overlay_w:0" -vcodec h264 -acodec copy РЕЗУЛТАТ.mp4
    • не е нужно да изчакате генерирането до край - в началото го пуснете за няколко секунди с добро качество (спира се с еднократно натискане на Ctrl+C) за да проверите дали всичко е наред (телеметрията дали е на правилното място и дали видеото е в синхрон с телеметрията)
    • най-добре да ползвате .mkv формат за да можете да видите резултатът без да спирате процесът (mp4 поставя moov atom накрая, докато в mkv това не е така и можете да гледате резултатът докато ffmpeg работи): ffmpeg -i "ВИДЕО.MP4" -i "ТЕЛЕМЕТРИЯ.mkv" -filter_complex "[1]trim=31,setpts=PTS-STARTPTS[telemetry];[telemetry]crop=exact=1:x=900:y=ih/2-100:w=iw/2+245-900:h=ih-900[pip]; [0:v:0][pip] overlay=main_w-overlay_w:0:eof_action=endall" -profile:v main -level 3.1 -b:v 2140k -s 1024x768 -vcodec h264 ПРОБА.mkv
  • за съжаление тази обработка отнема доста време и за това бъдете търпеливи (може да го пуснете през нощта)

Подобряване на цветовете

Резултатът от подобряването на цветовете може да се види тук:

Повечето хора съветват, когато видеото се записва това да се прави с Flat профил и ръчни настройки за да може то да се подобри по-лесно на втори етап. Като цяло настройките по време на заснемане са от особено значение - прегледайте следните видеа: общи съвети, вкл. за GoPro, настройки, YI 4K+ или потърсете нещо конкретно за вашата камера. С две думи - fps трябва да е кратна на честотата на електрическата мрежа в страната (страните в Европа 50 Hz, САЩ - 60 Hz) за да не се получават хоризонтални плуващи черти при снимане на изкуствено осветление (това е и една от причината да има разлика между стандартите PAL/SECAM). Останалите настройки - FOV: (Ultra) Wide, Metering mode: Average, Quality: най-висока, White Balance зависи от светлината - облачно/сняг/6500K+, изгрев/залез/нажежаема крушка/3000K, дневна светлина/5500K, Color: Flat, ISO Max: на светло 400, иначе max 1600 (зависи от камерата), EV: зависи от камерата, стабилизация: включена, Sharpness: low.

Много добри съвети за обработка можете да видите тук: DaVinci Resolve, GIMP/ffmpeg.

За мен ffmpeg вариантът ми е най-удобен: print screen на кадър с най-много цветове. В GIMP>Colors>Levels>Auto и след това копираме съответните min/max стойности за Red/Green/Blue и използваме (заместваме RED_MIN и останалите) ffmpeg -i "ВИДЕО.MP4" -filter_complex "colorlevels=rimin=RED_MIN/255:rimax=RED_MAX/255:gimin=GREEN_MIN/255:gimax=GREEN_MAX/255:bimin=BLUE_MIN/255:bimax=BLUE_MAX/255" -acodec copy РЕЗУЛТАТ.mp4. Друг вариант (повече възможности, но не толкова автоматичен) е използвайки GIMP>Colors>Curves… или GIMP>Colors>Levels>Auto и бутон “Edit these Settings as Curves”, експорт до файл (Manage presets > Export Current Settings to File) и след това docker run -v "$(pwd):/data" --rm python:2 bash -c "wget https://raw.githubusercontent.com/fehlfarbe/gimp2acv/master/acv.py && wget https://raw.githubusercontent.com/fehlfarbe/gimp2acv/master/gimp2acv.py && pip install numpy && python /gimp2acv.py /data/curves" и ffmpeg -i "ВИДЕО.MP4" -filter_complex "curves=psfile=curves.acv" -acodec copy РЕЗУЛТАТ.mp4.

Погледнете още и следните филтри и линкове: Color Correct Scuba Diving, colorbalance, manupilation of video colors, GIMP 2.10 Curves, GIMP 2.10 Shadows and Highlights.

Телеметрия, стабилизиране, цветове

Описаните по-горе подобрения могат да бъдат обединени, ето и резултатът:

docker run --rm  -v "$(pwd)":/data jrottenberg/ffmpeg -i /data/ВИДЕО.MP4 -vf vidstabdetect=accuracy=15:shakiness=10:mincontrast=0.3:show=0:result=/data/transforms.trf -an -f null -
docker run --rm  -v "$(pwd)":/data jrottenberg/ffmpeg -i "/data/ВИДЕО.MP4" -i "/data/ТЕЛЕМЕТРИЯ.mkv" -filter_complex "[0]vidstabtransform=smoothing=20:input=/data/transforms.trf:optzoom=0,unsharp,colorlevels=rimin=RED_MIN/255:rimax=RED_MAX/255:gimin=GREEN_MIN/255:gimax=GREEN_MAX/255:bimin=BLUE_MIN/255:bimax=BLUE_MAX/255 [stabilized];[1]trim=31,setpts=PTS-STARTPTS[telemetry];color=c=white,vignette=angle=PI/2:aspect=4/3:x0=w/2+30:y0=h/2-10[mask];[mask][telemetry]scale2ref[scaledmask][scaledtelemetry];[scaledtelemetry][scaledmask]alphamerge,crop=exact=1:x=0:y=ih/2:w=iw/2+245:h=ih-410[pip]; [stabilized][pip] overlay=main_w-overlay_w:0:eof_action=endall" -vcodec h264 -acodec copy /data/РЕЗУЛТАТ.mp4
  • сравненията са генериране посредством
    • половин екран: docker run --rm -v "$(pwd)":/data jrottenberg/ffmpeg -i "/data/ВИДЕО.MP4" -i "/data/ТЕЛЕМЕТРИЯ.mkv" -i "/data/СЪЩОТОВИДЕОВТОРИПЪТ.MP4" -filter_complex "[0]vidstabtransform=smoothing=20:input=/data/transforms.trf:optzoom=0,unsharp,colorlevels=rimin=RED_MIN/255:rimax=RED_MAX/255:gimin=GREEN_MIN/255:gimax=GREEN_MAX/255:bimin=BLUE_MIN/255:bimax=BLUE_MAX/255 [stabilized];[1]trim=ОФСЕТ,setpts=PTS-STARTPTS[telemetry];color=c=white,vignette=angle=PI/2:aspect=4/3:x0=w/2+30:y0=h/2-10[mask];[mask][telemetry]scale2ref[scaledmask][scaledtelemetry];[scaledtelemetry][scaledmask]alphamerge,crop=exact=1:x=0:y=ih/2:w=iw/2+245:h=ih-410[pip]; [stabilized][pip] overlay=main_w-overlay_w:0:eof_action=endall,crop=exact=1:x=iw/2:y=0:w=iw/2:h=ih[right];[2:v:0][right]overlay=main_w/2:0" -vcodec h264 -acodec copy /data/РЕЗУЛТАТ.mp4
    • цял екран: docker run --rm -v "$(pwd)":/data jrottenberg/ffmpeg -i "/data/ВИДЕО.MP4" -i "/data/ТЕЛЕМЕТРИЯ.mkv" -i "/data/СЪЩОТОВИДЕОВТОРИПЪТ.MP4" -filter_complex "[0]vidstabtransform=smoothing=20:input=/data/transforms.trf:optzoom=0,unsharp,colorlevels=rimin=RED_MIN/255:rimax=RED_MAX/255:gimin=GREEN_MIN/255:gimax=GREEN_MAX/255:bimin=BLUE_MIN/255:bimax=BLUE_MAX/255 [stabilized];[1]trim=ОФСЕТ,setpts=PTS-STARTPTS[telemetry];color=c=white,vignette=angle=PI/2:aspect=4/3:x0=w/2+30:y0=h/2-10[mask];[mask][telemetry]scale2ref[scaledmask][scaledtelemetry];[scaledtelemetry][scaledmask]alphamerge,crop=exact=1:x=0:y=ih/2:w=iw/2+245:h=ih-410[pip]; [stabilized][pip] overlay=main_w-overlay_w:0:eof_action=endall[right];[2:v:0]pad=iw*2:ih[original];[original][right]overlay=w" -vcodec h264 -acodec copy /data/РЕЗУЛТАТ.mp4
  • за съжаление тази обработка отнема доста време и за това бъдете търпеливи (може да го пуснете през нощта)

Разделяне на отделни клипове

  • ffmpeg -i ГОЛЯМФАЙЛ.mp4 -ss 593.3 -t 551.64 -c copy РЕЗУЛТАТ.mp4
  • ffmpeg -i ГОЛЯМФАЙЛ.mp4 -ss 00:10:00 -to 00:20:00 -c copy РЕЗУЛТАТ.mp4
  • ffmpeg -i ГОЛЯМФАЙЛ.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment -reset_timestamps 1 РЕЗУЛТАТ%03d.mp4
  • За повече информация https://unix.stackexchange.com/questions/1670/how-can-i-use-ffmpeg-to-split-mpeg-video-into-10-minute-chunks

  • автоматизиран скрипт - необходими са името на файла, от който ще вземем частите (ВИДЕО.mp4) и моментите, които трябва да останат (начало-край, в променливата INCLUDE)
  • записвам си такъв файл за всяко видео
set -xe

INPUT="${INPUT:-ВИДЕО.mp4}"
INCLUDE="${INCLUDE:-
00:00:17 00:01:22
00:04:10 00:05:10
}"

# тук обикновено слагам като коментар всички команди, които съм изпълнил за стабилизация, телеметрия, цветове, curve и т.н.

CONCAT_LIST=""

function extract() {
# based on https://stackoverflow.com/questions/18444194/cutting-the-videos-based-on-start-and-end-time-using-ffmpeg
	function toSeconds() {
		awk -F: 'NF==3 { print ($1 * 3600) + ($2 * 60) + $3 } NF==2 { print ($1 * 60) + $2 } NF==1 { print 0 + $1 }' <<< ${1}
	}

	local start="${1}"
	local end="${2}"
	local inputFile="${3:-${INPUT}}"

	local startSeconds=$(toSeconds "${start}")
	local endSeconds=$(toSeconds "${end}")
	local duration=$(bc <<< "(${endSeconds} + 0.01) - ${startSeconds}" | awk '{ printf "%.4f", $0 }')
	local result="$(pwd)/${inputFile%.*}-${start//:/-}.${inputFile##*.}"
	ffmpeg -ss ${startSeconds} -i "${inputFile}" -t ${duration} -codec copy "${result}"

	CONCAT_LIST="$(printf "%s\nfile %s" "${CONCAT_LIST}" "${result// /\\ }")"
}}

IFS=$'\n'
for next in ${INCLUDE}; do
	extract "${next% *}" "${next##* }"
done

# check https://superuser.com/questions/1059245/ffmpeg-join-two-mp4-files-with-ffmpeg-on-command-line and https://stackoverflow.com/questions/7333232/how-to-concatenate-two-mp4-files-using-ffmpeg
# това не работи добре - получава се едно забиване между частите, [в следващата секция е решено това](#взимане-на-определени-моменти-от-клип-и-обединяването-им)
ffmpeg -f concat -safe 0 -i <(echo "${CONCAT_LIST}") -codec copy "${INPUT%.*}-final.${INPUT##*.}"

Взимане на определени моменти от клип и обединяването им

  • необходими са името на файла, от който ще вземем частите (ВИДЕО.mp4) и моментите, които трябва да останат (начало-край, в променливата INCLUDE)
  • записвам си такъв файл за всяко видео
set -xe

INPUT="${INPUT:-ВИДЕО.mp4}"
INCLUDE="${INCLUDE:-
00:00:17 00:01:22
00:04:10 00:05:10
}"

# тук обикновено слагам като коментар всички команди, които съм изпълнил за стабилизация, телеметрия, цветове, curve и т.н.

CONCAT_LIST=""


function extract() {
# based on https://stackoverflow.com/questions/18444194/cutting-the-videos-based-on-start-and-end-time-using-ffmpeg
	function toSeconds() {
		awk -F: 'NF==3 { print ($1 * 3600) + ($2 * 60) + $3 } NF==2 { print ($1 * 60) + $2 } NF==1 { print 0 + $1 }' <<< ${1}
	}

	local start="${1}"
	local end="${2}"
	local inputFile="$(realpath "${3:-${INPUT}}")"

	local startSeconds=$(toSeconds "${start}")
	local endSeconds=$(toSeconds "${end}")

	CONCAT_LIST="$(printf "%s\nfile %s\ninpoint %s\noutpoint %s" "${CONCAT_LIST}" "${inputFile// /\\ }" "${startSeconds}" "${endSeconds}")"
}

IFS=$'\n'
for next in ${INCLUDE}; do
	extract "${next% *}" "${next##* }"
done

# check https://superuser.com/questions/1059245/ffmpeg-join-two-mp4-files-with-ffmpeg-on-command-line and https://stackoverflow.com/questions/7333232/how-to-concatenate-two-mp4-files-using-ffmpeg
ffmpeg -f concat -safe 0 -fflags discardcorrupt -fflags fastseek -fflags +genpts -avoid_negative_ts 1 -i <(echo "${CONCAT_LIST}") -c copy "${INPUT%.*}-final2.${INPUT##*.}"
# дори и да липсват keyframes като се качи в youtube тези проблеми изчезват

Добавяне на музика за фон

Преглед frame-by-frame

  • mpv ВИДЕО.mp4 и след това . и ,
  • между 10 и 15 (вкл.) фрейм към файл: ffmpeg -i ВИДЕО.mp4 -vf select='between(n\,10\,15)' -vsync 0 frames%d.png

Сваляне на видео от youtube

  • docker run -it --rm -v "$(pwd):/data" vimagick/youtube-dl -i -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]' -i --audio-format=mp3 -o "%(title)s.%(ext)s" 'https://www.youtube.com/watch?v=(...)'
  • сваляне само на аудио: docker run -it --rm -v "$(pwd):/data" vimagick/youtube-dl -i -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]' -x -i --audio-format=mp3 -o "%(title)s.%(ext)s" 'https://www.youtube.com/watch?v=(...)'

  • ако не работи - обновете до най-новата версия: docker pull vimagick/youtube-dl

Забързване на видео

ffmpeg -i ВИДЕО.MP4 -filter:v "setpts=0.1*PTS" -an РЕЗУЛТАТ-fast.MP4

Графичен интерфейс

Повечето от нещата тук могат да се направят и през графични програми като kdenlive - например Overlay Video Picture in Picture и Stabilize Shaky Videos Using Kdenlive Free Editing Software, shotcut, pitivi, DaVinci Resolve


Натиснете бутона "watch" в github или се абонирайте с RSS четец ако искате да научавате за обновления или нови статии.

Written on November 7, 2020