#!/bin/bash set -e fail () { echo >&2 "bikecams-unify: error: $1"; exit 127; } # example usages: # ~/an-things/general/bikecams-unify --offset-file=/home/ian/junk/d/offset --pixelate=7800-8010:380x240@260x632:40 --tmpdir=/volatile/ian/tmp/d {front,rear}/avi_0005.avi ~/keep/bikecams/demo/there.ogg # ~/an-things/general/bikecams-unify --offset-file=/home/ian/junk/d/offset2 --tmpdir=/volatile/ian/tmp/d {front,rear}/avi_0006.avi ~/keep/bikecams/demo/back.ogg basis=0 offset=true video_setting=-v0 audio_setting='' shrink='' offset_file='' max='' pixelates='' suppress_audio=false while [ $# != 0 ]; do val="${1#*=}" case "$1" in --tmpdir=*) tmpdir="$val" ;; --frames=*) max="$val" ;; --suppress-audio) suppress_audio=true ;; --start-frame=*) basis="$val" ;; --no-offset) offset=false ;; --offset-file=*) offset_file="$val" ;; --video-setting=*) video_setting="$val" ;; --audio-setting=*) audio_setting="$val" ;; --shrink=*) shrink="$val" ;; --pixelate=*-*:*x*@*x*:*) pixelates="$pixelates $val" ;; # -:x@x: # where sizes are in pre-shrunk pixels (ie out of 640x970) # and a good value for macropixel is 10-50ish. # and frames are of course 1/30s --) shift; break ;; -*) fail "unknown option \`$1'" ;; *) break ;; esac shift done test $# = 4 || \ fail 'usage: .../bikecams-unify [--tmpdir=T] FRONT.avi REAR.avi|none OUTPUT.ogg OUTPUT.mpg' front="$1" rear="$2" output="$3" moutput="$4" if [ "x$rear" = xnone ]; then no_rear=true offset=false else no_rear=false fi if [ "x$tmpdir" = x ]; then trap 'set +e; wait; rm -rf -- "$tmpdir"; exit 127' 0 tmpdir="`mktemp -dt`" rm_tmpdir=true else rm -rf -- "$tmpdir" mkdir -- "$tmpdir" rm_tmpdir=false fi frame=frame%06d.jpg oframe=frame%06d.ppm if [ "$max" ]; then min_explode=$(( 40 * 30 )) explode=$(( $max + $basis + 30 )) if [ $explode -lt $min_explode ]; then explode=$min_explode; fi t_max="-t $(( ($explode + 29) / 30 ))" fi x () { echo >&2 "+ $*"; "$@"; } dmjpeg () { mkdir -p "$tmpdir/$2" x ffmpeg $t_max \ -i "$1" -f image2 "$tmpdir/$2/$frame" \ -f wav "$tmpdir/$2-in.wav" x sox -V3 "$tmpdir/$2-in.wav" "$tmpdir/$2.sb" } dmjpeg "$front" front $no_rear || dmjpeg "$rear" rear if $offset; then if [ "x$offset_file" = x ]; then offset_file="$tmpdir"/offset; fi if [ -f "$offset_file" ]; then echo "reading offset from $offset_file" else x ${0/unify/audio} --not-interactive \ <"$tmpdir"/front.sb \ 3<"$tmpdir"/rear.sb \ >"$offset_file" fi read <"$offset_file" offset_ausamples offset_seconds offset_frames else offset_ausamples=0 offset_seconds=0 offset_frames=0 fi # weird noise thing: # sox -c1 -r8000 d/tmp/rear.sb d/tmp/t.wav noiseprof prof # sox -v0.5 -c1 -r8000 d/tmp/rear.sb d/tmp/t.wav noisered prof 0.1 basis_ausamples=$(( ($basis * 800) / 3 )) initoffsets () { if [ $offset_ausamples -lt 0 ]; then atrimfront=$(( -$offset_ausamples )) atrimrear=0 ifront=$(( $basis-$offset_frames )) irear=$basis else atrimfront=0 atrimrear=$(( $offset_ausamples )) ifront=$basis irear=$(( $basis+$offset_frames )) fi icomposed=0 } perframeloop () { ifile front $no_rear || ifile rear icomposed=$(( $icomposed + 1 )) if [ "$max" ] && [ "$max" -a "$icomposed" -gt "$max" ]; then break; fi f=`printf "%s/composed/$oframe" "$tmpdir" "$icomposed"` } ifile () { eval ' i'$1'=$(( $i'$1' + 1)) f=`printf "%s/$frame" "$tmpdir"/'$1' $i'$1'` if ! test -f "$f"; then break; fi f'$1'="$f" ' } pbmmake -black 1 10 >"$tmpdir"/border mkpipe () { rm -f -- "$1" mkfifo -m600 -- "$1" } rm -rf "$tmpdir"/composed mkdir "$tmpdir"/composed if [ "$max" ]; then sox_trim_len=$(( ($max * 800 + 2) / 3 ))s fi trimaudio () { trim=$(( $2 + $basis_ausamples )) if $suppress_audio; then x sox -V3 -c1 -r8000 -n "$tmpdir/$1-out".wav trim 0 800s else x sox -V3 "$tmpdir/$1-in".wav "$tmpdir/$1-out".wav \ trim ${trim}s $sox_trim_len fi } initoffsets trimaudio front $atrimfront $no_rear || trimaudio rear $atrimrear if $no_rear; then ln "$tmpdir"/front-out.wav "$tmpdir"/output.wav total_height=480 else x sox -V3 -M "$tmpdir"/rear-out.wav "$tmpdir"/front-out.wav \ -c2 -2 "$tmpdir"/output.wav total_height=970 fi mkpipe "$tmpdir"/result.ppm mkpipe "$tmpdir"/resultm.ppm forked () { eval ' '$1'_pid=$! forked="$forked '$1'" ' } x ${0/bikecams-unify/ppm2theora-derivative} \ $audio_setting $video_setting \ -o "$output" -f 30 -F 1 \ "$tmpdir"/output.wav "$tmpdir"/result.ppm & forked theora x ffmpeg -y -r 30 \ -f image2pipe -i "$tmpdir"/resultm.ppm \ -i "$tmpdir/output.wav" \ -b 500 -ar 44100 "$moutput" \ & forked ffmpeg exec 3>"$tmpdir"/result.ppm exec 4>"$tmpdir"/resultm.ppm pipe_mangle () { x "$@" <"$wf" >"$wf.new" mv -f "$wf.new" "$wf" } p_delim () { lhs=${rhs%%${1}*} rhs=${rhs#*${1}} } while true; do perframeloop printf "[%d]" $icomposed pfront="$ffront.ppm" prear="$frear.ppm" wf="$tmpdir/composed/work$icomposed.ppm" tf1="$tmpdir/composed/temp1-$icomposed.ppm" tf2="$tmpdir/composed/temp2-$icomposed.ppm" djpeg -pnm "$ffront" >"$pfront" $no_rear || djpeg -pnm "$frear" >"$prear" if $no_rear; then ln -f "$pfront" "$wf" else pnmcat -tb -black "$pfront" "$tmpdir"/border "$prear" >"$wf" fi for p in $pixelates; do rhs=$p p_delim -; if [ $icomposed -lt $lhs ]; then continue; fi p_delim :; if [ $icomposed -gt $lhs ]; then continue; fi p_delim x; p_width=$lhs; p_delim @; p_height=$lhs p_delim x; p_left=$lhs; p_delim :; p_top=$lhs p_macropixel=$rhs pnmcut $p_left $p_top $p_width $p_height <"$wf" >"$tf1" pnmscale -reduce $p_macropixel <"$tf1" >"$tf2" pnmscale -xsize $p_width -ysize $p_height <"$tf2" >"$tf1" pipe_mangle pnmpaste "$tf1" $p_left $p_top rm -f -- "$tf" done if [ "$shrink" ]; then pipe_mangle pnmscale -nomix -reduce $shrink fi cat "$wf" >&3 cat "$wf" >&4 rm -f -- "$pfront" "$prear" "$wf" done echo '(done-compose)' exec 3<&- exec 4<&- sleep 1 echo for f in $forked; do eval "pid=\$${f}_pid" printf "... %s: " $f set +e; wait $pid; rc=$?; set -e if [ $rc = 0 ]; then echo 'ok' else echo "failed: $rc" fail "$f failed" fi done if $rm_tmpdir; then trap '' 0 rm -rf -- "$tmpdir" fi printf '==== completed ok - wrote %s %s ====\n' "$output" "$moutput"