Portál AbcLinuxu, 30. dubna 2025 09:09
Během několika málo týdnů se mi dostane do rukou maličká HD kamera od Sony, která mi bude mít za úkol zpříjemňovat pořizování videa na cestách. Sony Webbie PM1 sice dokáže ve svých 120 gramech i s baterií pojmout CMOS o progresivním rozlišení 1440x1080, bohužel už nikoli stabilizátor obrazu. Antiklepku jsem se tedy rozhodl řešit až v postprocessingu. A víte co? Výsledky jsou o dost lepší, než jsem čekal!
Zabralo to několik desítek minut googlení a nadávání nad nedostupností rozumného softwaru pro Linux, nakonec jsem se však rozhodl zvolit poněkud krkolomné řešení -- použít freeware stabilizační plugin do VirtualDubu, oblíbeného editoru videa pro Windows. Tento přístup má bohužel tu nevýhodu, že téměř veškerý navazující sofware existuje jen pro Windows. Musel jsem tedy sáhnout po řešení, které se, zvláště v poslední době, ukazuje být velmi zdatným konkurentem Windows spuštěných ve VirtualBoxu, QEMU, VMWare či jiném virtualizačním software -- totiž Wine.
K mému překvapení se po dlouhém dni stráveném střídavou prací na desktopu s Windows XP a notebooku s openSUSE podařilo všechen potřebný software rozchodit a mohu tudíž prohlásit, že je možné od dnešního dne stabilizovat obraz videa i bez Windows či Macu (aspoň co se běžného domácího uživatele týče). A jaký software k této úloze budeme potřebovat?
quartz.dll
je všechen software dostupný zdarma (na quartz.dll musíte mít licenci Windows). Postup pro zprovoznění není úplně triviální ale na druhou stranu to není ani nic, co by průměrný uživatel Linuxu nezvládl quartz.dll
překopírujeme do ~/.wine/drive_c/windows/system32/
~/.wine/drive_c/Program Files/VirtualDub/plugins/
(nebo tam, kam jsme VirtualDub rozbalili)regsvr32 C:\\windows\\system32\\quartz.dll
winecfg
a na záložce Knihovny vybereme jako Novou náhradu pro DLL quartz
s nastavením (nativní, vestavěná)Nyní máme systém připraven pro vytvoření řetězce programů, na jejichž konci bude linuxový Avidemux, z nějž poleze stabilizované video.
Co tedy dále? Dále si spustíme wine ~/.wine/drive_c/Program Files/VirtualDub/VirtualDub.exe
. Teď můžeme, stejně jako na Windows použít filtr Deshaker pro stabilizaci obrazu. Poměrně obsáhlý, leč poněkud starší, návod lze nalézt na stránkách Johna Meyera, veškeré volby jsou popsány na stránce pluginu. Vzhledem k omezení VirtualDubu je však třeba myslet na to, že pro načtení jakéhokoli jiného kontejneru než AVI je nutné vytvořit AviSynth skript, který bude obsahovat jediný příkaz -- DirectShowSource(cesta_k_videu_v_jiném_kontejneru)
. Tento se poté uloží s příponou AVS a otevře stejně jako klasické video ve VirtualDubu.
Potom, co jsme si vyzkoušeli práci s Deshakerem ve VirtualDubu a jsme spokojeni s výsledky, kterých jsme dosáhli (= máme odladěné nastavení stabilizace), použijeme volbu File > Save processing settings. Následně si otevřeme v libovolném editoru uložený soubor a hledáme řádek, na kterém se nachází něco jako VirtualDub.video.filters.instance[0].Config(...)
. Zkopírujeme si volby v závorkách, budou se nám za moment hodit. Jde totiž o nastavení Deshakeru pro stabilizaci videa.
To, že nám funguje VirtualDub je pěkné, bohužel to neřeší náš problém -- totiž editovat video v programu spuštěném ve Wine není příliš pohodlné, hlavně to ale neřeší problém jediného výstupního kontejneru, který VirtualDub zvládá -- AVI. Pokud máme v úmyslu video uložit například do OGM či Matrošky, nevyhneme se použití nějakého chytřejšího programu. Jenže dostat stabilizované video někam jinam než do VirtualDubu není jen tak. Naštěstí existuje chytrý program -- AviSynth. Jde o frameserver pro Windows (od verze 3.0 i pro Linux, ještě není vydána), který dokáže načíst pluginy pro VirtualDub.
A jaké je další pozitivum tohoto přístupu? Avidemux, multiplatformní videoeditační program, má ve verzi pro Windows možnost spuštění skriptů AviSynthu. Tedy umí vytvořit proxy server (pomocí programu avsproxy.exe
, ke kterému se následně Avidemux připojí. A zde je skulina, která nám umožní dostat video z Wine do Linuxu -- linuxová verze Avidemuxu se umí připojit k Windows verzi proxy serveru. Nyní nám tedy zbývá vytvořit skripty pro AviSynth, resp. AvsProxy, které zpřístupní stabilizované video.
Jelikož Deshaker potřebuje pro svoji funkci dva průchody videem, musíme vytvořit dva skripty, které spustíme jeden po druhém v AvsProxy. První skript má následující obsah:
$ cat /tmp/video/deshaker-pass1.avs LoadPlugin("C:\Program Files\AviSynth 2.5\plugins\DirectShowSource.dll") LoadVirtualDubPlugin("C:\Program Files\VirtualDub\plugins\Deshaker.vdf", "deshaker", 0) src = ConvertToRGB32(DirectShowSource("Z:\tmp\video\webbie.mp4",fps=29.97,convertfps=true,pixel_type="AUTO",audio=false)) # pass 1 ConvertToYV12(deshaker(src,"12|1|30|4|1.33333|7|1|0|640|480|1|2|1|400|400|400|1500|4|1|0|2|5|40|300|4|Z:\\tmp\\deshaker.log|0|0|0|0|0|0|0|0|0|0|0|0|0|1|15|15|5|10|1|1|30|30|0|0|1|0|1|1|1|10|1|15|1000|1|88"))
První řádek žádá AviSynth (který taktéž umí načíst video jen z AVI) o načení pluginu pro DirectShowSource. Druhým načteme Deshaker a nastavíme mu funkční název deshaker
. Dalším řádkem otevřeme video v kontejneru MP4 a převedeme jej do RGB32. Tato konverze je nutná jelikož pluginy VirtualDubu s žádným jiným barevným prostorem pracovat neumějí. Ná posledním řádku provedeme první průchod Deshakerem (to je druhá číslice v parametru funkce) a výsledek převedeme na YV12. Tento barevný prostor na výstupu si zase vynucuje AvsProxy.
Obsah skriptu pro druhý průchod (Deshakeru, ne kodéru videa jako je třeba x264 či XviD) je v podstatě stejný, až na to, že druhé čislo parametru funkce deshaker
je 2.
$ cat /tmp/video/deshaker-pass2.avs LoadPlugin("C:\Program Files\AviSynth 2.5\plugins\DirectShowSource.dll") LoadVirtualDubPlugin("C:\Program Files\VirtualDub\plugins\Deshaker.vdf", "deshaker", 0) src = ConvertToRGB32(DirectShowSource("Z:\tmp\video\webbie.mp4",fps=29.97,convertfps=true,pixel_type="AUTO",audio=false)) # pass 2 ConvertToYV12(deshaker(src,"12|2|30|4|1.33333|7|1|0|640|480|1|2|1|400|400|400|1500|4|1|0|2|5|40|300|4|Z:\\tmp\\deshaker.log|0|0|0|0|0|0|0|0|0|0|0|0|0|1|15|15|5|10|1|1|30|30|0|0|1|0|1|1|1|10|1|15|1000|1|88"))
Zřejmě se ptáte, kde jsem vzal parametr pro funkci deshaker
, že? Je to jednoduché, tento parametr je identický s tím, který jsme si zapamatovali z kapitoly Už nám funguje VirtualDub s Deshakerem.
Posledním krokem je zpřístupnení videa pro Avidemux. Jakmile máme hotové skripty, je to snadné. Spustíme si wine ~/.wine/drive_c/Program Files/Avidemux 2.4/avsproxy.exe Z:\\tmp\\video\\deshaker-pass1.avs
. Nyní si spustíme linuxový Avidemux (Qt verze příkazem avidemux2_qt4
) a ve File vybereme Connect to avsproxy. Pokud se nám v okně zobrazí první snímek vstupního videa, máme vyhráno. Pokud ne, prozkoumáme výstup Wine. Prvním problémem může být to, že zabudovaná verze quartz.dll
na přehrání videa nestačí a my jsme nestáhli tu nativní; druhou možností je obvykle nějaký problém s kodeky (ach ty Windows).
Pokud vše funguje jak má, postupujeme podobně jako ve VirtualDubu -- přetočíme video na začátek a necháme jej jen tak přehrát až do konce. Tím se nám vytvoří log soubor Deshakeru (v našem případě /tmp/deshaker.log
), který bude následně použit v druhém průchodu.
Druhý průchod spustíme tak, že místo deshaker-pass1.avs
spustíme přes AvsProxy deshaker-pass2.avs
. Opět se připojíme Avidemuxem. Tentokrát už na výstupu máme finální stabilizovaný obraz. Nastavíme tedy parametry převodu, jak nám vyhovují a spustíme převod. Soubor, který na konci získáme však není hotový. Chybí mu totiž zvuk. Jak je to možné? No, jednak jsme ve skriptech načítání zvuku zakázali a jednak existuje zřejmě ještě nějaký nedostatek ve Wine. Ani když je zvuk povolený, tak se na výstupu (rozuměj Avidemuxu) neobjeví.
Jestliže bylo vstupní video v rozumném formátu, nebudeme muset se zvukem nic moc dělat, jen si v Avidemuxu otevřeme původní video soubor (nikoli *.avs nebo avsproxy) a audio z něj vyextrahujeme (Audio > Save). Můžeme také použít Mplayer či jiný software, kterou má obdobnou funkci. Pokud máme zvuk v MP3, AC3 či WAVu, je možné jej připojit v Avidemuxu k výslednému videu rovnou.
V našem případě je však zvuk ve formátu AAC, což je problém jednak proto, že soubor exportovaný Avidemuxem ani Mplayerem není přehratelný a jednak proto, že Avidemux nedokáže zvuk v AAC k videu připojit. To však nevadí, zvuk převedeme do kontejneru, který už přehrát lze a poté jej připojíme specializovaným nástrojem.
Uložený výstup v AAC převedeme do AAC ADTS, které lze již normálně přehrát a připojit k videu. Budeme k tomu potřebovat program faad
(balík faad2), příkaz je faad -a vystup.aac vstup.aac
.
Následně je nutné zvuk připojit k videu. Já zvolil jako výstupní kontejner videa Matrošku, použiji tudíž mkvmerge
(balík mkvtoolnix). Příkazem mkvmerge -o vystupni-video.mkv video-bez-zvuku.mkv vystup.aac
připojíme zvuk a dostaneme výsledný soubor se stabilizovaným videem a původním zvukem. Používáte-li, tak jako zde v příkladu, AAC, prohlédněte si prosím v manuálu od mkvmerge
volbu --aac-is-sbr, může mít vliv na přehratelnost zvuku výstupního souboru.
Po poměrně komplikovaném postupu se nám podařilo napojit stabilizační software Deshaker na linuxový Avidemux a získat stabilní video a to včetně původního zvuku. Nepředpokládám, že bude někdo tento postup ručně provádět pokaždé, když bude chtít stabilizovat nějaké to video. To je ostatně hlavní důvod, proč jsem chtěl realizovat celou tuto věc na Linuxu -- pohodlnost. Dokážu si dost dobře představit, že se najde mnoho uživatelů, kteří si celý postup naskriptují (například za pomoci avidemux2_cli
) a ve výsledku budou video stabilizovat jediným příkazem. A to je ostatně i můj cíl. Jen si budu muset najít chvíli času mezi školními a pracovními povinnostmi
UPDATE 13.4.2009: Přikládám 720p videa (zdroj: YouTube -- Sony Webbie HD Test #2) z modelu Webbie CM1 (větší verze, se zoomem, stejný čip) -- webbie.mp4 (originál přímo z kamery) a webbie.mkv (stabilizované). Obě mají 25 MiB a jsou komprimována v H264/AAC. S nastavením stabilizace jsem si zatím moc nehrál, tohle je jen takový první nástřel.
UPDATE 4.9.2010: Na disku jsem objevil skript, kterým jsem minulý rok prováděl automatickou stabilizaci videa ze zmíněné kamery od Sony. Na vstupu žere MP4 s tím, že na výstupu je identické video (v H.264) ve stabilizované podobě. Za funkčnost neruším, ladil jsem to jen aby vše fungovalo na mém počítači.
#!/bin/bash echo "Deshaker for Linux 1.1" # unique dir name time=$(date +%s) ddir="/tmp/deshaker$time" if [ -d $ddir ]; then rm -rf "$ddir/*" else mkdir $ddir fi if [ $# -eq 0 ]; then echo "ERROR: Please supply a filename to deshake" exit 1 fi if [ $2 eq '']; then date=$(date --rfc-3339=date) moviename="Movie ($date)" else moviename="$2" fi if [ -r $1 ]; then # got to find absolute path to the file if [ "${1:0:1}" = "/" ] || [ "$PWD" = "/" ]; then ABS="" else ABS="$PWD" fi # loop thru path IFS="/" for DIR in $1; do if [ -n "$DIR" ]; then if [ "$DIR" = ".." ]; then ABS="${ABS%/*}" elif [ "$DIR" != "." ]; then ABS="$ABS/$DIR" fi fi done IFS=":" echo "Deshaking file $ABS" ln -s $ABS "$ddir/input_video" else echo "ERROR: $1 is not readable!" fi # now we have a symlink to our file in the working directory # create avsproxy echo "ADAP" > "$ddir/avsproxy.avs" echo "thisisavsproxyfile" >> "$ddir/avsproxy.avs" # create deshaker first pass file echo "LoadPlugin(\"C:\\Program Files\\AviSynth 2.5\\plugins\\DirectShowSource.dll\")" > "$ddir/d-1p.avs" echo "LoadVirtualDubPlugin(\"C:\\Program Files\\VirtualDub\\plugins\\Deshaker.vdf\", \"deshaker\", 0)" >> "$ddir/d-1p.avs" echo "src = ConvertToRGB32(DirectShowSource(\"Z:\\tmp\\deshaker$time\\input_video\",fps=29.97,convertfps=true,pixel_type=\"AUTO\",audio=false))" >> "$ddir/d-1p.avs" # duplicate the first file cp "$ddir/d-1p.avs" "$ddir/d-2p.avs" # extract audio from input file and get file video resolution echo "Extracting audio..." resolution=$(mplayer -identify -dumpaudio -dumpfile "$ddir/input_audio.aac" "$ddir/input_video" |grep VIDEO:|sed s/\:/\ /g|sed s/\ \ \ /\ /g|sed s/\ \ /\ /g|cut -d " " -f 3) echo "Converting audio..." faad -q -a "$ddir/output_audio.aac" "$ddir/input_audio.aac" > /dev/null echo "Audio conversion done" # continue with deshaker, set rolling shutter and pixel aspect according to the resolution if [ "$resolution" = "1440x1080" ]; then rolling_shutter=86; pixel_aspect=1.333333; elif [ "$resolution" = "1280x720" ]; then rolling_shutter=73; pixel_aspect=1; else rolling_shutter=88; pixel_aspect=1; fi echo "ConvertToYV12(deshaker(src,\"12|1|30|4|$pixel_aspect|7|1|0|640|480|1|2|1|400|400|400|1500|4|1|0|2|5|40|300|4|Z:\\\\tmp\\\\deshaker$time\\\\deshaker.log|0|0|0|0|0|0|0|0|0|0|0|0|0|1|15|15|5|10|1|1|30|30|0|0|1|0|1|1|1|10|1|15|1000|1|$rolling_shutter\"))" >> "$ddir/d-1p.avs" echo "ConvertToYV12(deshaker(src,\"12|2|30|4|$pixel_aspect|7|1|0|640|480|1|2|1|400|400|400|1500| 4|1|0|2|5|40|300|4|Z:\\\\tmp\\\\deshaker$time\\\\deshaker.log|0|0|0|0|0|0|0|0|0|0|0|0|0|1|15|15|5|10|1|1|30|30|0|0|1|0|1|1|1|10|1|15|1000|1|$rolling_shutter\"))" >> "$ddir/d-2p.avs" # create 1st pass script for avidemux ad1="$ddir/a-1p.js" echo "//AD <- Needed to identify//" > "$ad1" echo "var app = new Avidemux();" >> "$ad1" echo "app.load(\"$ddir/avsproxy.avs\");" >> "$ad1" echo "app.video.codec(\"Copy\",\"CQ=4\",\"0 \");" >> "$ad1" echo "app.audio.reset();" >> "$ad1" echo "app.audio.codec(\"copy\",128,0,\"\");" >> "$ad1" echo "app.audio.mixer(\"NONE\");" >> "$ad1" echo "app.setContainer(\"AVI\");" >> "$ad1" echo "setSuccess(app.save(\"/dev/null\"));" >> "$ad1" # calculate original bitrate length=$(faad -i "$ddir/output_audio.aac" 2>&1 |grep ADTS|cut -d " " -f 2) size=$(ls -Ls "$ddir/input_video"|cut -d " " -f 1) bitrate=$(echo "scale=10; ($size/$length)*8 - 128"|bc -l|xargs printf "%1.0f") # 128 -- sound # 1.333 pixel ratio if [ "$resolution" = "1440x1080" ]; then dimensions="1:1920x1080" else dimensions="1:$resolution" fi # second pass script using the input bitrate ad2="$ddir/a-2p.js" echo "//AD <- Needed to identify//" > "$ad2" echo "var app = new Avidemux();" >> "$ad2" echo "app.load(\"$ddir/avsproxy.avs\");" >> "$ad2" echo "app.video.setPostProc(3,3,0);" >> "$ad2" echo "app.video.setFps1000(29970);" >> "$ad2" if [ "$resolution" = "1440x1080" ]; then echo "app.video.codec(\"X264\",\"CBR=$bitrate\",\"200 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 1e 00 00 00 3c 00 00 00 0a 00 00 00 33 00 00 00 04 00 00 00 03 00 00 00 28 00 00 00 1e 00 00 00 2c 01 00 00 01 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 08 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 06 00 00 00 18 00 00 00 02 00 00 00 64 00 00 00 85 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5a 00 00 00 01 00 00 00 64 00 00 00 \");" >> "$ad2" else echo "app.video.codec(\"X264\",\"CBR=$bitrate\",\"200 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 1e 00 00 00 3c 00 00 00 0a 00 00 00 33 00 00 00 04 00 00 00 03 00 00 00 28 00 00 00 1e 00 00 00 2c 01 00 00 01 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 08 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 06 00 00 00 18 00 00 00 02 00 00 00 64 00 00 00 85 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5a 00 00 00 01 00 00 00 64 00 00 00 \");" >> "$ad2" fi echo "app.audio.reset();" >> "$ad2" echo "app.audio.codec(\"copy\",128,0,\"\");" >> "$ad2" echo "app.audio.mixer(\"NONE\");" >> "$ad2" echo "app.setContainer(\"MATROSKA\");" >> "$ad2" echo "setSuccess(app.save(\"$ddir/avidemux_pass2.mkv\"));" >> "$ad2" # now we have everything set up and ready to run # run first pass echo "Running first pass..." export LD_PRELOAD="" WINEDEBUG=-all wine C:\\Program\ Files\\Avidemux\ 2.4\\avsproxy.exe "Z:\\tmp\deshaker$time\\d-1p.avs" 2>&1 >/dev/null & avsproxypid=$(pgrep -f d-1p.avs) sleep 10 avidemux2_cli --run "$ddir/a-1p.js" --load "$ddir/avsproxy.avs" > /dev/null pkill -9 $avsproxypid 2>&1 >/dev/null # once completed, run second pass echo "Running second pass..." WINEDEBUG=-all wine C:\\Program\ Files\\Avidemux\ 2.4\\avsproxy.exe "Z:\\tmp\deshaker$time\\d-2p.avs" 2>&1 >/dev/null & avsproxypid=$(pgrep -f d-2p.avs) sleep 10 avidemux2_cli --run "$ddir/a-2p.js" --load "$ddir/avsproxy.avs" 2>&1 > /dev/null kill -9 $avsproxypid 2>&1 >/dev/null # now process audio mkvmerge -q --display-dimensions "$dimensions" --title "$moviename" -o "$ddir/output_video.mkv" "$ddir/avidemux_pass2.mkv" "$ddir/output_audio.aac" # copy video to final destination mv "$ddir/output_video.mkv" "$ABS.deshaked.mkv" rm -rf $ddir
Tiskni
Sdílej:
ahóóóój! já jsem se dneska moc fajn ožral
stabilizácia docela dobre funguje aj v cinellerre.
To sice ano, musí se však specifikovat (podobně jako v Adobe Premiere) body, vůči kterým se bude stabilizovat (aspoň tak to na mě z manuálu působí ) Mě jde spíše o plně automatickou stabilizaci, kde jen nastavím parametry kamery a dál se o to nemusím starat... Program si sám rozhodne, co je třes ruky a co žádaný pohyb.
kde to v ní prosím najdu?
Ta vypada za tu cenu zajimave, navic takovy skoro fotak... Jak se to snasi s Linuxem?
Já jsem objevil Handbrake ( http://handbrake.fr/ ) a nemusím nic řešit.
Odzkoušeno na MTS souborech ze SONY HD kamery. Výstup např. do MP4 kontejneru a na PS3 krásně přehratelné.
Zkoušel jsem mnoho skriptů (mencoder, ffmpeg), ale žádný nebyl tak spolehlivý - buď byl skvělý obraz, ale rozcházel se zvuk, nebo byl obraz k nekoukání.
Omlouvám se, že jsem sem plácnul odkaz na Handbrake - ten samozřejmě se stabilizací obrazu nemá nic společného.
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.