V4L je asi třetí nebo čtvrtá nejlepší věc na světě hned po večeři a obědě. Samozřejmě na světě existuje spoustu důvodů, ze kterých není použití Linuxu užitečné, nebo je dokonce škodlivé, ale jeho koncepce sjednocení práce s videovstupy na abstraktní vrstvě je zrovna jedním z moc hezkých příkladů, kdy to celé funguje pro automatickou otrockou práci bez nutnosti se složitě v něčem šťourat a nadávat u toho. A automatizovaná digitalizace čehokoliv, ať už přes focení mobilem nebo pomocí objektové klasifikace umělou inteligencí, je z nějakého důvodu pořád trendem.
VHSky jsou technologií, která můj život naštěstí kompletně minula a nemohu si to vynachválit. Před dávnými lety dost možná leckdo okolo používal tyhle kolosální analogové kazety a když se to tak vezme, bylo to docela k ničemu. Ale na druhou stranu to byla asi jediná dostupná technologie, na kterou bylo možné v té době zaznamenat a archivovat kromě televizních pořadů i významné domácí chvilky, osobní výročí, svatby a vůbec všechno, co s tím tak nějak souvisí. S technologií jde ruku v ruce i nějaká ta specifikace, která je tady pro nás v Evropě definovaná jako PAL, tedy nápal, který určuje spoustu vlastností kolem analogového signálu, tedy pro vysílání především charakter a frekvenci nosné vlny a pomocné nosné vlny, šířku pásma pro celé video a další vlastnosti, nicméně pro zpracování na koncovém zařízení také dobu trvání snímků a linek, jejich počet a všechno, co zkrátka potřebujeme vědět. Tahle videa si můžeme pak představit typicky jako 720x576 v 4:3 aspektovém rámci, čemuž se říká asi 576i, což je v analogovém podání 652 linek v 50Hz střídání polí, čímž se dostáváme na nějakých 25 FPS. Trik je v tom, že oněch 25 bude ve skutečnosti asi 24.978014, ale to je už zase trochu vyšší magie.
Moderní pojetí videa, tedy to plně digitální, se v dnešní době točí kolem kolem více či méně chytrých kontejnerů (vlastně “jen” MPEG-4 nebo Matroška), které pak teprve zahrnují v sobě konkrétní datové proudy videa, audia a třeba titulků. Dá se s tím dělat opravdu hodně, pro dnešní videa si přejeme hlavně proudy v kodeku x264 nebo x265 a pro zvuky podobně, většinou ztrátové AAC nebo klidně i MP3. K převodu analogové kazety je potřeba vál, co ji vůbec umí nějak rozjet, a k němu pak interní nebo USB grabber, který digitalizuje takový signál z analogového rozhraní SCARTu nebo kompozitního videa podle specifikace V4L2. Dneska je to vlastně jedno, protože moderní jádra umí pracovat vlastně se vším, ale důležité je nezapomenout, že zpracováno bude opravdu jen video a zvuk je potřeba sosat zase z jiného zařízení, totiž abstraktní zvukové karty, kterou grabber bude nejspíše obsahovat. Výpis takových je možné získat pomocí aktuálního správce zvuku, třeba v případě PulseAudio jako pactl list, nebo přímo jako ALSA, aplay -L. Pak už je docela snadné tohle všechno zkombinovat a nechat zachytávat kazetu z přehrávače. Asi nejlepší je použít mocný ffmpeg, ve kterém se dá udělat úplně všechno, jen je potřeba si dát pozor právě na framerate, protože přímá konverze jediným průchodem může rozhodit synchronizaci zvukové a audio stopy v řádech procent, což se pak velmi těžko až nemožně napravuje. Jako výhoda pak může sloužit duplikace výstupu na síť, což je příjemný bonus.
ffmpeg -rtbufsize 2.14748e+09 -probesize 128k -isync -standard pal -to 01:30:00.0 -f v4l2 -thread_queue_size 4096 -framerate pal -i /dev/video0 -f alsa -thread_queue_size 2048 -ac 2 -ar 48000 -strict -2 -i hw:1,0 -c:a aac -af aresample=async=1000 -c:v libx264 -preset medium -tune grain -g 10 -b:v 4200k video-01.mp4 -f flv -c:v libx264 -preset ultrafast -b:v 1000k rtmp://videoserver:1935/live/videoSamozřejmě to nemusí každému vyhovovat a dá se to dělat i postaru dvouprůchodovou cestou spolu s přímým zobrazením živého výstupu na monitor toho daného stroje, k čemu zle s výhodou využít přehrávač VLC, který má tuhle vlastnosti vyloženě v malíčku. Jeho nevýhodou je ale právě poměrně rozsáhlá sada komplikací souvisejících s přímým kódováním do x264, proto je takový hrubý záznam většinou lepší uložit tak, jak z grabberu teče - a výsledek si spolkne třeba i něco kolem jednoho gigabajtu za minutu záznamu, s čímž je potřeba počítat. Na druhou stranu se záznam uloží, pak se začne zpracovávat, kazeta sama dojede do konce, přetočí se a vše je znamenitě ukončeno.
vlc v4l:///dev/video0 :v4l-standard=PAL :v4l-norm=0 :input-slave=alsa://hw:1,0 :live-caching=300 '--sout=#transcode{vcodec=HFYU,acodec=s16l,channels=2,samplerate=48000}:duplicate{dst=display,dst=std{access-file{no-overwrite},mux=avi,dst=RAW_video-01.avi}}' --stop-time 5400 vlc://quit && ffmpeg -i RAW_video-01.avi -c:a aac -c:v libx264 -preset slow -tune grain -v:b 4200k -crf 22 video-01.mp4Na pomalých strojích s GUI, kde je potřeba prioritizovat tento proces před vším ostatním, se obvykle ještě vyplatí hodit nice o něco výše směrem k reálnému času, aby se to náhodou celé nepokazilo a nepropadlo do zatracení. Druhý hrubý výstup už ale není potřeba nijak složitě rekódovat, střih se dá zvládnout celkem pohodlně bez něj a protože doba je streamovací, nemusí být od věci hodit mov atomy dopředu.
ffmpeg -i video-01.mp4 -ss 00:00:05.30 -to 01:28:31.50 -c:v copy -c:a copy -movflags faststart video-01-final.mp4A to už tak trochu souvisí s archivací, protože kam jinam digitalizované video hodit, než do nějakého toho cloudu? DVDčka a všechny ostatní možnosti jsou stejně zastaralé a už jen málokdo je používá, takže třeba YouTube může být docela jasná volba, ačkoliv to třeba kvůli původní velikosti výsledného souboru nebude ze začátku tak vypadat. Ale od toho je přeci neinteraktivní workflow, žáno.
S tím souvisí tak trochu druhé téma posledních bezesných nocí, ve kterých běželo mnoho pokusů o opravu automatického zpracování záznamů z bezpečnostních kamer. Už nějakou dobu slouží ke střežení různých místností nejen v budníku V4L kamery, které jsou napojené na motion a další tajné analyzátory, ovšem motiond z nich v první řadě streamuje živě a za další také ukládá v nějakých 4 snímcích za sekundu statické obrázky pro pozdější zpracování. Proč je tomu tak, to je zase jiná věc, důležité je ovšem to, že v pravidelných intervalech, obvykle jednou denně nad ránem, je potřeba z různých míst přenést tyto obrázky ke zpracování na jedno centrální místo, což může vypadat například nějak takto:
#!/bin/bash
BASEDIR="/security"
cd $BASEDIR/sh
CAMERA=${1:-"cam0"}
YDAY=$(date -d "yesterday 13:00 " '+%Y-%m-%d')
DATEARR=(`echo $YDAY | sed -e 's/[:-]/ /g'`)
YR=${DATEARR[0]}
MO=${DATEARR[1]}
DA=${DATEARR[2]}
CURRENT=$BASEDIR/$CAMERA/$YR/$MO
mkdir -p $CURRENT
rsync --ignore-existing -az security@security.eida.cz:/security/$YR/$CAMERA/$MO/$DA $CURRENT
# VIDEO
./month.sh ../$CAMERA/$YR/$MO
Neděje se tam vlastně nic jiného, než že se obrázky přes rsync celkem bezpečně a spolehlivě dostanou do svého posledního umístění a pak s nimi začíná zajímavá magie. Když už je totiž vše hotové, sestříhá se z daného měsíce pro každý den živý časosběr, který byl lifrován na YouTube, ale osud tomu pak nějak nepřál.
#!/bin/bash
for m in "$@"
do
for d in $(ls "$PWD/$m" | grep -v "^\.")
do
for e in $(ls -1 "$m/$d" | grep event | grep -v mp4 | sort -t- -n -k2)
do
if [ -d "$m/$d/$e" ]; then
echo "timelapse $m/$d/$e"
./timelapse.sh "$m/$d/$e"
fi
done
if [ -d "$m/$d" ]; then
echo "day $m/$d"
#if [ ! -f "$m/$d.mp4" ]; then
./day.sh "$m/$d"
#fi
rm -r "$m/$d"
fi
done
done
Tvorba časosběru probíhá opět pomocí ffmpeg jako sekvence obrázků poslaná do MPEG-4 kontejneru s x264 kodekem v postačujícím formátu jako jednotlivé události zaznamenané v daném dni
#!/bin/bash for d in "$@" do if [ -d "$d" ]; then if [ ! -f "$d"/../$(basename "$d").mp4 ]; then ffmpeg -y -framerate 10 -pattern_type glob -i "$d"/'*.jpg' -c:v libx264 -movflags faststart -acodec none -vf "format=yuv420p" "$d"/../$(basename "$d").mp4 fi rm -r "$d" fi done
a následně jako celý den obyčejným spojovacím protokolem.
#!/bin/bash for d in "$@" do if [ -d "$d" ]; then ffmpeg -y -f concat -safe 0 -i <(for f in $(ls -1 "$d" | grep event-.*\.mp4 | sort -t- -n -k2); do echo "file '$PWD/$d/$f'"; done) -c copy "$d"/../$(basename "$d").mp4 fi done
Tohle samo o sobě byla výborná myšlenka, která ale generovala příliš velkou zátěž pro servery YouTube, které to na špatně ověřených účtech okamžitě zahazovaly kvůli duplicitnímu obsahu a nešlo to vydržet. Druhá myšlenka pak byla využít vlastnosti celého měsíce. V první řadě jsou soubory uspořádány tak, jak přišly, a vytvoří se z nich ISO obraz pro pozdější uchování na nějakém DVD, které stejně zapadne v archivech hluboko v podzemí a nikdo ho číst nebude.
Myšlenka zpracování právě pro použití v archivaci online pak tkví ve spojení jednotlivých dnů v měsíci takovým způsobem, že jsou do popisku k nahrávanému videu přidány časové značky v klikatelném formátu, tedy (hh:mm:ss). Ukázalo se, že takto zhuštěný záznam z jendoho měsíce zbare něco přes dvanáct hodin, což je bohužel příliš mnoho pro jedno video, nicméně už jen charakter názvu souborů umožňuje rozdělit měsíc na videa o více částech začínajících 0, 1, a 2|3. Tyto části jsou vytvořeny rekódováním, neboť v některých případech slučovací protokol, který je použitý ffmpegem, z nějakého důvodu na YouTube nefunguje tak, jak by asi úplně mohl a měl. Takto získané třetinové části potom už přímo přebírá skript youtube-upload a organizuje výsledky v konkrétních playlistech.
#!/bin/bash
if [ -z "$*" ] ; then
echo "Usage: $0 [ camera [\"0 1 23\"]]"
exit 0
fi
BASEDIR="/security"
cd $BASEDIR/sh
CAMERA=${1:-"cam0"}
YDAY=$(date -d "yesterday 13:00 " '+%Y-%m-%d')
DATEARR=(`echo $YDAY | sed -e 's/[:-]/ /g'`)
YR=${DATEARR[0]}
MO=${DATEARR[1]}
DA=${DATEARR[2]}
CURRENT=$BASEDIR/$CAMERA/$YR
# make ISO image
if [ ! -f "$CURRENT/$YR-$MO.iso" ]; then
genisoimage -V "$YR-$MO $CAMERA" -lJR -o "$CURRENT/$YR-$MO.iso" "$CURRENT/$MO"
fi
# split prepare
SPL=${2:-"0 1 23"}
# split
for s in $SPL
do
TOTAL_DURATION=0
DESCRIPTION=""
# read and fetch
for fc in $( ls -1 "$CURRENT/$MO" | grep [${s}].\.mp4 | sort );
do
FORMAT_DURATION=$( date -d@"$TOTAL_DURATION" -u +%H:%M:%S )
DESCRIPTION=$( echo "$DESCRIPTION"_\("$FORMAT_DURATION"\): "$fc" )
DURATION=$(ffmpeg -i "$CURRENT/$MO/${fc}" 2>&1 | grep Duration | egrep -o "[0-9]{2,}\:[0-9]{2}\:[0-9]{2}\.[0-9]{2}")
TIME=$(echo $DURATION | awk -F : '{ print (($1*3600)+($2*60)+($3))}')
SPLIT_SEC=$(echo $DURATION | awk -F . '{print $2}')
TOTAL_DURATION=$( echo ${TOTAL_DURATION}+${TIME}+${SPLIT_SEC}/100 | bc -l )
done
DESC=$( echo "$DESCRIPTION" | sed 's/_\+/\n/g' )
if [ ! -f "$CURRENT/upload/$YR-$MO.$s.mp4" ]; then
mkdir -p "$CURRENT/upload"
# concatenate
if [ -d "$CURRENT/$MO" ]; then
ffmpeg -y -f concat -safe 0 \
-i <(for f in $( ls -1 "$CURRENT/$MO" | grep [${s}].\.mp4 | sort ); do echo "file '$CURRENT/$MO/$f'"; done) \
-vf "scale=-1:480,pad=640:480:(640-iw)/2:(480-ih)/2,setsar=1" \
-movflags faststart \
"$CURRENT/upload/$YR-$MO.$s".mp4
fi
fi
# upload
if [ -f "$CURRENT/upload/$YR-$MO.$s.mp4" ]; then
/usr/local/bin/youtube-upload \
--title="${YR}-${MO} [${s}] ${CAMERA}" \
--description="SECURITY ${YR}/${CAMERA}/${MO} ${DESC}" \
--credentials-file="${BASEDIR}/credentials.json" \
--client-secrets="/usr/local/share/youtube_upload/client_secrets.json" \
--privacy="unlisted" \
--playlist="Security ${YR} ${CAMERA}" \
--tags="cz.eida.security.${CAMERA}" "$CURRENT/upload/$YR-$MO.$s.mp4"
fi
# /for
done
exit 0
Tohle je v tento okamžik asi vrchol automatizované archivace bezpečnostních záznamů z kamer a vypadá to, že to takhle vydrží i nějakou dobu do budoucna. Je pravda, že Google, který je vyloženě proti opensource, čas od času samovolně a zlovolně mění YouTube API, aby to vývojářům za každou cenu znepříjemnil, takže je třeba skript youtube-upload a s ním související pythonové balíčky udržovat aktuální, přičemž je pak nutné nezapomenout aktualizovat i samotný přístupový token, jinak nahrávání kolikrát skončí s nějakou nesmyslnou chybou a je hned vymalováno. Ale to už je ta nepatrná cena za dlouhé dny nebo i měsíce, kdy to všechno naštěstí jede samo a bez chyb. A tak by to mělo také být - jsou-li někde opakující se úlohy, nechť jsou plně automatizované a my máme čas si užívat hezké dny bez stresu.