Post by Jan StaryPost by Grant EdwardsPost by Jan StaryPost by Grant EdwardsI spent hours and hours trying to figure out a way to do that
in bash, but failed. Can you point me toward a bash example?
Patch tr(1) to do no buffering and pipe through that.
Or patch ffmpeg to say " ... time=111s (23%) ..." and be done with it.
I'm writing a shell script. I really don't want to
distribute/maintain a custom version of tr/ffmpeg.
I mean get it into svn of course :-)
I'd be more than happy to submit a patch.
Post by Jan StarySo what is the goal really? Have ffmpeg pipe through some
script that would produce some "nicer" progress line
and strip the version numbers etc?
Exactly.
Post by Jan StaryAs in, 'ffmpeg -i in out 2>&1 | show-progress-nicely' ?
Can you show us your script?
Sure.
The main objective is to reformat recorded TV programs (in my
case ATSC MPEG2 transport streams), into a format that's
playable on a portable media player. In my case it's a SanDisk
Sansa e200 running rockbox. It crops 4:3 or 16:9 format video
to 220x176 5:4 aspect ratio to match the e200 screen (I think
some iPods are the same geometry/resolution). It also detects
letterboxed 16:9 inside a 4:3 stream and crops that
appropriately. [When you've only got a 1.7" 220x176 screen, you
don't want to waste any pixels.]
There is the main reformat script and a letterbox-detect
script. Both of them use ffmpeg and filter the output through
a third script.
Once I've cleaned them up a little more, they're intended for
the RockBox Wiki page.
The goal was to use nothing but bash, ffmpeg and the normal
core linux utilities, but I ended up having to use perl to
translate line endings and ImageMagick to detect letterboxing.
I thought up some ways to do letterbox detection using just
ffmpeg, but using ImageMagick was a lot more straight forward.
I may still play with letterbox detection using ffmpeg, but for
now I require ImageMagick be installed.
The ffmpeg-filter script allows you to override the duration
for the purposes of % complete and ETA calculation. This is
done because the letterbox detection only looks at the fist 15
minutes of the stream.
There is some jitter in the % complete and ETA results due to
the 1-second granularity of the $SECONDS value in bash that is
used to determine elapsed time. That could be imporoved by
getting the current time in a higher resolution using the
"date" command.
------------------------------ffmpeg-filter------------------------------
#!/bin/bash
# allow input duration to be passed in from command line if desired
Duration="$1"
# convert between seconds and hh:mm:ss
function parseHMS
{
hours=$(expr match "$1" '^0*\([0-9]*\)')
minutes=$(expr match "$1" '^[0-9]*:0*\([0-9]*\)')
seconds=$(expr match "$1" '^[0-9]*:[0-9]*:0*\([0-9]*\)')
echo $(( seconds + 60 * (minutes + 60*hours) ))
}
function formatHMS
{
seconds="$1"
hours=$((seconds/3600))
seconds=$((seconds-(hours*3600)))
minutes=$((seconds/60))
seconds=$((seconds-(minutes*60)))
printf "%d:%02d:%02d" $hours $minutes $seconds
}
# toss input until specific line is found, parsing for input duration
# value, but don't overwrite Duration value if it is already set
function ignoreTill
{
while read line
do
s=$(expr match "$line" '^Duration: \([^,]*\)')
test -n "$s" && test -z "$Duration" && Duration=$(parseHMS "$s")
test "$line" = "$1" && break
done
}
# Perl hackery to convert \r to \n. Can't use tr because it
# output. I copied the perl gobbledy-gook from the interweb, so
# don't ask me about it...
perl -015l12pe 'BEGIN { $|++ }' |
(
ignoreTill ""
ignoreTill "Press [q] to stop encoding"
startTime=$((SECONDS))
lastTime=$((startTime+4)) # don't do calcs until we're 5 seconds in
Duration=$(((Duration+1)*100))
ETA="?"
while read line
do
if expr match "$line" "^frame=" >/dev/null
then
# figure out percent completion and ETA
now=$SECONDS
done=$(expr match "$line" '.* time=0*\([0-9]*\.[0-9]*\)')
done=${done/\./}
remain=$((Duration-done))
percent=$(( (100*done)/Duration ))
deltaT=$((now-startTime))
if [ $now -gt $lastTime ]; then
lastTime=$now
if [ $percent -ge 100 ]; then
ETA=0
else
ETA=$(( (remain * deltaT)/done ))
fi
ETA=$(formatHMS $ETA)
fi
echo -ne "\r$line runtime: $(formatHMS $deltaT) $percent% ETA: $ETA "
needline=$'\n'
else
echo -n "$needline"
echo "$line"
needline=""
fi
done
)
-------------------------------------------------------------------------
----------------------------detectLetterboxed----------------------------
#!/bin/bash
# Script to detect whether or not a video file contents are
# nletterboxed. Assumes input aspect ratio is 4:3. Samples the
# input file every 15 seconds for the first 15 minutes. Uses
# ImageMagick's "trim" operation to get rid of black bars.
# Comparing trimmed and untrimmed image height tells us if the
# video is letterboxed. Won't work on streams from stations that
# put crap in the bars. Fuzz and border settings might need
# tweaking to work with files that were captured from analog
# feeds.
set -o nounset
set -o errexit
test "$#" != 1 && { echo "usage: $0 <infile>"; exit 1; }
infile="$1"
test -r "$infile" || { echo "file '$infile' not found/readable"; exit 2; }
tmpdir=tmp$$
mkdir $tmpdir
# grab some frames from first 15 minutes of video
ffmpeg -i "$infile" -r 0.066667 -vframes 60 -s qvga -f image2 $tmpdir/%03d.png 2>&1 | ffmpeg-filter 900
function imageHeight
{
expr match "$(identify $1)" '.* [0-9][0-9]*x\([0-9][0-9]*\) '
}
origHeight=$(imageHeight $tmpdir/001.png)
lbHeight=$(( (origHeight * 4 * 9) / (3 * 16) ))
threshold=$(( lbHeight + (origHeight-lbHeight) / 8 ))
n=0
total=0
# trim off the black bars and average the trimmed image heights
for f in $tmpdir/[0-9][0-9][0-9].png
do
convert $f -bordercolor black -border 1x1 -fuzz 1% -trim +repage $f-trimmed
rm $f
h=$(imageHeight $f-trimmed)
rm $f-trimmed
echo -en "$h \r"
total=$((total+h))
n=$((n+1))
done
avg=$((total/n))
echo
echo "orig $origHeight"
echo "lbht $lbHeight"
echo "thsh $threshold"
echo "avg $avg"
echo
rm -rf $tmpdir
if [ $avg -lt $threshold ]
then
exit 0
else
exit 9
fi
-------------------------------------------------------------------------
--------------------------------sansa-encode-----------------------------
#! /usr/bin/env bash
# Script to convert video to format suitable for sansa e200 running
# rockbox. Output aspect is 5:4 (fullscreen on e200). Input is
# assumed to be NTSC or ATSC, so we'll force output to 29.97fps and
# crop left/right edges to get 5:4 aspect. If input is "squarer" than
# 5:4, it will probably break since it'll pass negative left/right
# crop values to ffmpeg.
#
# based on the mkvid-ffmpeg script by Christopher Williams
# Portions Copyright 2008 Christopher Williams
# This is licensed under the GNU General Public License, version 2 or later
set -o nounset
set -o errexit
width=220; height=176 # full-screen on e200
framerate=29.97 # match NTSC/ATSC input -- probably want 25 for PAL
audiobr=64
videobr=256
srcIsLetterboxed=n
test $# != 2 && { echo "usage: $0 infile outfile"; exit 1; }
infile="$1"
outfile="$2"
test -r "$infile" || { echo "input file '$infile' not found/readable"; exit 2; }
# read characteristics of source file
VideoString=$(ffmpeg -i "$infile" 2>&1 | grep "Video:")
test -z "$VideoString" && { echo "couldn't read video info from file"; exit 3; }
srcWidth=$(expr match "$VideoString" '.* \([0-9]*\)x[0-9]* ')
srcHeight=$(expr match "$VideoString" '.* [0-9]*x\([0-9]*\) ')
srcAspect=$(expr match "$VideoString" '.* DAR \([0-9]*:[0-9]*\)],')
test -z "$srcHeight" -o -z "$srcWidth" -o -z "$srcAspect" &&
{
echo "couldn't find resolution/aspect info in source file"
exit 3
}
croppedSrcWidth=$srcWidth
croppedSrcHeight=$srcHeight
srcAspH=${srcAspect/[0-9]*:/}
srcAspW=${srcAspect/:[0-9]*/}
origSrcSpecs="${srcWidth}x${srcHeight} $srcAspect"
croptb=0
if [ $srcAspect = '4:3' ] && which detectLetterboxed &>/dev/null
then
echo "checking for letterboxed input..."
if detectLetterboxed "$infile"
then
echo "**** Detected letterboxed input ****"
croppedSrcHeight=$(( (srcHeight * 4 * 9) / (3 * 16) ))
croptb=$(( (srcHeight - croppedSrcHeight)/2 ))
srcHeight=$croppedSrcHeight
srcAspH=9
srcAspW=16
fi
fi
croppedSrcWidth=$(( (srcWidth * width * srcAspH) / (height * srcAspW) ))
croplr=$(( (srcWidth - croppedSrcWidth)/2 ))
echo
echo " dest: ${width}x${height}"
echo " source: ${origSrcSpecs}"
echo " cropped height: $croppedSrcHeight"
echo " cropped width: $croppedSrcWidth"
echo " cropped aspect: $srcAspW:$srcAspH"
echo " crop left/right: $croplr"
echo " crop top/bottom: $croptb"
echo
# set -x
ffmpeg -y -i "$infile" -aspect $width:$height -deinterlace \
-vcodec mpeg2video -ac 2 -ar 44100 -acodec libmp3lame -r $framerate \
-cropleft $croplr -cropright $croplr -croptop $croptb -cropbottom $croptb \
-s ${width}x${height} -vb ${videobr}k \
-ab ${audiobr}k -f mpeg "$outfile" 2>&1 | ffmpeg-filter
-------------------------------------------------------------------------
--
Grant Edwards grante Yow! The Korean War must
at have been fun.
visi.com