Discussion:
[FFmpeg-user] How to get machine-friendly output from ffmpeg?
Grant Edwards
2010-02-08 20:49:22 UTC
Permalink
I've been working on some scripts/programs that use ffmpeg to
convert files from one video format to another.

I need to monitor the progress so I can calculate a %complete
and an ETA. (I was pretty surprised that ffmpeg doesn't do
that since most other similar programs seem to). I also need
to discard all of the preliminary stuff that ffmpeg spits out
on startup (configuration info and a table of codecs/formats).
All that stuff just makes the useful information harder to
spot.

I've got something working, but it's been painful using ffmpeg
programmatically because of the hostile output format -- mostly
the mixture of newlines and carriage returns for line endings.

It would also be a lot easier to handle output programmatically
if time values were displayed consistently rather than being a
mixture of hh:mm:ss.ss in some and just ssssss.ss in others.

I've searched the options and there doesn't seem to be any way
to get the output to use consistent line endings. I don't care
what line endings, but they need to be consistent.

The problem of not having readily parsable output seems to have
come up several times in the past, but all I could find were
hacks that attempt work around the issues by filtering the
output through "strings" or perl scripts to try to fix up the
output. [You can't use "tr" to fix the line ending problem
because it buffers it's output.]

Is there any proscpect of a real solution?
--
Grant Edwards grante Yow! I'm a fuschia bowling
at ball somewhere in Brittany
visi.com
Jan Stary
2010-02-08 21:30:17 UTC
Permalink
Post by Grant Edwards
I've been working on some scripts/programs that use ffmpeg to
convert files from one video format to another.
I need to monitor the progress so I can calculate a %complete
and an ETA. (I was pretty surprised that ffmpeg doesn't do
that since most other similar programs seem to).
How exactly are you gonna calculate the "%complete"?
A % of what? File size? Video stream(s) duration?
They are not necesarilly in a simple relation to
time spent encoding (more means more of course,
but the relation is not necesarilly even linear).
Post by Grant Edwards
I also need
to discard all of the preliminary stuff that ffmpeg spits out
on startup (configuration info and a table of codecs/formats).
What table of formats?
What version are you using?
Post by Grant Edwards
All that stuff just makes the useful information harder to
spot.
I've got something working, but it's been painful using ffmpeg
programmatically because of the hostile output format -- mostly
the mixture of newlines and carriage returns for line endings.
"Programatically" meaning what? Using ffmpeg(1) in a shell script?
Using the internal functions of the libraries in a C source?
Something else?
Post by Grant Edwards
It would also be a lot easier to handle output programmatically
if time values were displayed consistently rather than being a
mixture of hh:mm:ss.ss in some and just ssssss.ss in others.
Agreed.
Post by Grant Edwards
I've searched the options and there doesn't seem to be any way
to get the output to use consistent line endings. I don't care
what line endings, but they need to be consistent.
Can you give an example of the "mixture of newlines an CRs"?
Post by Grant Edwards
The problem of not having readily parsable output seems to have
come up several times in the past, but all I could find were
hacks that attempt work around the issues by filtering the
output through "strings" or perl scripts to try to fix up the
output.
So what exactly do you need to get from ffmpeg's output
(meaning the text messages, not video-file output)?
Grant Edwards
2010-02-08 22:27:03 UTC
Permalink
Post by Jan Stary
Post by Grant Edwards
I've been working on some scripts/programs that use ffmpeg to
convert files from one video format to another.
I need to monitor the progress so I can calculate a %complete
and an ETA. (I was pretty surprised that ffmpeg doesn't do
that since most other similar programs seem to).
How exactly are you gonna calculate the "%complete"?
A % of what? File size? Video stream(s) duration?
Video stream duration.
Post by Jan Stary
They are not necesarilly in a simple relation to
time spent encoding (more means more of course,
but the relation is not necesarilly even linear).
The "frame= " display a time value. Using that and the
"Duration" value that's printed in the stream identification to
calculate % complete and ETA works fine for everything I've
tested it on.
Post by Jan Stary
Post by Grant Edwards
I also need
to discard all of the preliminary stuff that ffmpeg spits out
on startup (configuration info and a table of codecs/formats).
What table of formats?
What version are you using?
$ ffmpeg
FFmpeg version SVN-r20373, Copyright (c) 2000-2009 Fabrice Bellard, et al.
built on Jan 11 2010 14:26:26 with gcc 4.1.2 (Gentoo 4.1.2 p1.1)
configuration: --prefix=/usr --libdir=/usr/lib --shlibdir=/usr/lib --mandir=/usr/share/man --enable-static --enable-shared --cc=i486-pc-linux-gnu-gcc --disable-debug --disable-ffplay --disable-network --enable-libmp3lame --enable-libvorbis --disable-indev=v4l2 --disable-indev=oss --disable-indev=jack --disable-outdev=oss --enable-x11grab --disable-vdpau --disable-vdpau --disable-altivec --cpu=athlon --enable-gpl --enable-version3 --enable-postproc --enable-avfilter --enable-avfilter-lavf --disable-stripping --enable-hardcoded-tables
libavutil 50. 3. 0 / 50. 3. 0
libavcodec 52.37. 1 / 52.37. 1
libavformat 52.39. 2 / 52.39. 2
libavdevice 52. 2. 0 / 52. 2. 0
libavfilter 1. 4. 1 / 1. 4. 1
libswscale 0. 7. 1 / 0. 7. 1
libpostproc 51. 2. 0 / 51. 2. 0
Post by Jan Stary
Post by Grant Edwards
All that stuff just makes the useful information harder to
spot.
I've got something working, but it's been painful using ffmpeg
programmatically because of the hostile output format -- mostly
the mixture of newlines and carriage returns for line endings.
"Programatically" meaning what? Using ffmpeg(1) in a shell script?
Shell or Python generally.
Post by Jan Stary
Post by Grant Edwards
It would also be a lot easier to handle output programmatically
if time values were displayed consistently rather than being a
mixture of hh:mm:ss.ss in some and just ssssss.ss in others.
Agreed.
Post by Grant Edwards
I've searched the options and there doesn't seem to be any way
to get the output to use consistent line endings. I don't care
what line endings, but they need to be consistent.
Can you give an example of the "mixture of newlines an CRs"?
Everything is terminated with a newline except except the
progress/status lines that start with "frame=". According to
gmane.org's archive, the problem of the mixed line ending has
been described several times in the past few years.
Post by Jan Stary
Post by Grant Edwards
The problem of not having readily parsable output seems to have
come up several times in the past, but all I could find were
hacks that attempt work around the issues by filtering the
output through "strings" or perl scripts to try to fix up the
output.
So what exactly do you need to get from ffmpeg's output
(meaning the text messages, not video-file output)?
The big issue is the line endings.
--
Grant Edwards grante Yow! I'm rated PG-34!!
at
visi.com
Artur Bodera
2010-02-09 09:30:18 UTC
Permalink
Post by Grant Edwards
Everything is terminated with a newline except except the
progress/status lines that start with "frame=". According to
gmane.org's archive, the problem of the mixed line ending has
been described several times in the past few years.
The big issue is the line endings.
Oh common! You're writing in python and can't work around \n vs \r ?
It seems you don't get the difference between these two. The frame=... line
deliberately ends with \r so when encoding in sell/command line the screen
doesn't get flooded with irrelevant data. \r = carrage return, so the
"cursor" gets back to the beginning of the terminal and the whole line is
overwritten with a new one.

It's easy as pie to parse too! What's wrong with exploding on \r|\n or regex
exploding with [\n\r] or something? So you redirect stderr stream to your
script, then parse everything up to "frame=" string, which denotes that
encoding has started. You use the previously extracted (via regex?) total
frame count to calculate progress and ETA. For ETA, you can even use the
(somewhat convenient) fps that ffmpeg calculates for you. Child's play.

A.
--
--
__
/.)\ +48 695 600 936
\(./ abodera at gmail.com
Walter Werner
2010-02-09 16:51:49 UTC
Permalink
Post by Artur Bodera
Post by Grant Edwards
Everything is terminated with a newline except except the
progress/status lines that start with "frame=". According to
gmane.org's archive, the problem of the mixed line ending has
been described several times in the past few years.
The big issue is the line endings.
Oh common! You're writing in python and can't work around \n vs \r ?
It seems you don't get the difference between these two. The frame=... line
deliberately ends with \r
This is NOT true with windows version of ffmpeg. sometimes (seems to
happen whenever q or fps changes) this line ends with \n. Funny enough I
havenot seen this with linux.
Walter.

----cut---
Grant Edwards
2010-02-09 19:21:09 UTC
Permalink
Post by Artur Bodera
Post by Grant Edwards
Everything is terminated with a newline except except the
progress/status lines that start with "frame=". According to
gmane.org's archive, the problem of the mixed line ending has
been described several times in the past few years.
The big issue is the line endings.
Oh common! You're writing in python and can't work around \n
vs \r ?
Sure. You can read the output one byte at a time and do your
own line buffering and EOL detection. Thats a lot of hassle
compared to reading output a line at a time like you can with
output from other programs.
Post by Artur Bodera
It seems you don't get the difference between these two.
Yes, I do.
Post by Artur Bodera
The frame=... line deliberately ends with \r so when encoding
in sell/command line the screen doesn't get flooded with
irrelevant data. \r = carrage return, so the "cursor" gets
back to the beginning of the terminal and the whole line is
overwritten with a new one.
I know. But it would be nice have an option to use normal line
endings and not do the carriate-return-overwrite trick.
Post by Artur Bodera
It's easy as pie to parse too! What's wrong with exploding on
\r|\n or regex exploding with [\n\r] or something?
The problem isn't _parsing_ it's _reading_. If you don't use
the standard "newline" you force people to read the output one
character at a time and doing their own line buffering instead
of reading and parsing a line at a time like one does with
"normal" Unix program output.
Post by Artur Bodera
So you redirect stderr stream to your script, then parse
everything up to "frame=" string, which denotes that encoding
has started. You use the previously extracted (via regex?)
total frame count to calculate progress and ETA.
No, I use the Duration and the time value.
Post by Artur Bodera
For ETA, you can even use the (somewhat convenient) fps that
ffmpeg calculates for you. Child's play.
Sure, you can calculate ETA they way I did, but other programs
(mencoder, wget, etc.) all seem to calculate it. I was
suggesting ffmpeg might do the same.

Other programs I've worked with that do the '\r' trick had
options to disable that and provide more easily parsed
status/progress output. See wget's --progress option for an
example. I was merely suggesting the ffmpeg offer something
similar. Apparently there's violent opposition to such an
option.

I promise I won't make any more suggestions.
--
Grant Edwards grante Yow! Do you guys know we
at just passed thru a BLACK
visi.com HOLE in space?
Jan Stary
2010-02-09 13:43:29 UTC
Permalink
Post by Grant Edwards
Post by Jan Stary
Post by Grant Edwards
I've been working on some scripts/programs that use ffmpeg to
convert files from one video format to another.
I need to monitor the progress so I can calculate a %complete
and an ETA. (I was pretty surprised that ffmpeg doesn't do
that since most other similar programs seem to).
How exactly are you gonna calculate the "%complete"?
A % of what? File size? Video stream(s) duration?
Video stream duration.
Post by Jan Stary
They are not necesarilly in a simple relation to
time spent encoding (more means more of course,
but the relation is not necesarilly even linear).
The "frame= " display a time value. Using that and the
"Duration" value that's printed in the stream identification to
calculate % complete and ETA works fine for everything I've
tested it on.
What's your problem then? The ffmpeg status line is already "monitoring"
it for you, once you master the fine art of multiplying by sixty
(because the Duration: is in HH:MM:SS, while time= is in SS, which
I agree is an inconvenience).

Except this doesn't take -t or -vframes into account,
but let's say you always convert the whole stream.

I agree that it would be convenient to have these calculations
in ffmpeg directly, something as "... time=123.54s (20%) ...".
Might be trivial to add. Also, inside ffmpeg, the -t/-vframes
values are already parsed.
Post by Grant Edwards
Post by Jan Stary
Post by Grant Edwards
[version numbers]
All that stuff just makes the useful information harder to
spot.
"grep Duration:"
How hard was that?
Post by Grant Edwards
Post by Jan Stary
Post by Grant Edwards
I've got something working, but it's been painful using ffmpeg
programmatically because of the hostile output format -- mostly
the mixture of newlines and carriage returns for line endings.
"Programatically" meaning what? Using ffmpeg(1) in a shell script?
Shell or Python generally.
Can you give us a shell example?
Post by Grant Edwards
Post by Jan Stary
Post by Grant Edwards
I've searched the options and there doesn't seem to be any way
to get the output to use consistent line endings. I don't care
what line endings, but they need to be consistent.
Can you give an example of the "mixture of newlines an CRs"?
Everything is terminated with a newline except except the
progress/status lines that start with "frame=". According to
gmane.org's archive, the problem of the mixed line ending has
been described several times in the past few years.
I just uses \r instead of \n to display the same line over,
as opposed to spitting out a new line of text every second.
What exactly is your problem with that?
Post by Grant Edwards
Post by Jan Stary
Post by Grant Edwards
The problem of not having readily parsable output seems to have
come up several times in the past, but all I could find were
hacks that attempt work around the issues by filtering the
output through "strings" or perl scripts to try to fix up the
output.
So what exactly do you need to get from ffmpeg's output
(meaning the text messages, not video-file output)?
The big issue is the line endings.
You mean, the big issue of " ffmpeg ... 2>&1 | tr \\r \\n " ?
Grant Edwards
2010-02-09 19:24:32 UTC
Permalink
Post by Jan Stary
I just uses \r instead of \n to display the same line over,
as opposed to spitting out a new line of text every second.
I know that's why it's done.
Post by Jan Stary
What exactly is your problem with that?
The problem is that Unix tools all are mostly all based on a
line-at-a-time model. Reading a line at a time in shell or
Python is trivial. Reading lines terminated by something other
than \n requires you to read one character at a time and do
line buffering in the program.
Post by Jan Stary
You mean, the big issue of " ffmpeg ... 2>&1 | tr \\r \\n " ?
Have you tried that?

Hint: it doesn't work.
--
Grant Edwards grante Yow! I want EARS! I want
at two ROUND BLACK EARS
visi.com to make me feel warm
'n secure!!
Jan Stary
2010-02-09 20:46:54 UTC
Permalink
Post by Grant Edwards
The problem is that Unix tools all are mostly all based on a
line-at-a-time model. Reading a line at a time in shell or
Python is trivial. Reading lines terminated by something other
than \n requires you to read one character at a time and do
line buffering in the program.
No. All it takes is piping the output through "tr \\r \\n"
which replaces th \r's with \n's which is all you need
to have standard \n-terminated lines.
Post by Grant Edwards
Post by Jan Stary
You mean, the big issue of " ffmpeg ... 2>&1 | tr \\r \\n " ?
Have you tried that?
Yes.
Post by Grant Edwards
Hint: it doesn't work.
Meaning what exactly?

# ffmpeg ... 2>&1 | tr \\r \\n
FFmpeg version SVN-r13839, Copyright (c) 2000-2008 Fabrice Bellard, et al.
configuration: --enable-shared --cc=cc --mandir=/usr/local/man --disable-debug --disable-optimizations --enable-gpl --enable-pthreads --enable-postproc --enable-swscale --enable-liba52 --enable-libfaac --enable-libfaad --enable-libmp3lame --enable-libtheora --enable-libvorbis --enable-libx264 --enable-x11grab --extra-libs=-L/usr/local/lib -L/usr/X11R6/lib --extra-cflags=-I/usr/local/include -I/usr/X11R6/include --disable-decoder=cavs --disable-altivec --disable-armv6 --disable-armvfp --disable-iwmmxt --disable-neon
libavutil version: 49.7.0
libavcodec version: 51.57.2
libavformat version: 52.16.0
libavdevice version: 52.0.0
built on Jul 1 2009 12:49:39, gcc: 3.3.5 (propolice)
Input #0, avi, from 'file.avi':
Duration: 00:42:37.44, start: 0.000000, bitrate: 6140 kb/s
Stream #0.0: Video: mpeg4, yuv420p, 768x576 [PAR 1:1 DAR 4:3], 25.00 tb(r)
Stream #0.1: Audio: mp3, 48000 Hz, stereo, 128 kb/s
Output #0, mpeg, to 'file.mpg':
Stream #0.0: Video: mpeg1video, yuv420p, 768x576 [PAR 1:1 DAR 4:3], q=2-31, 200 kb/s, 25.00 tb(c)
Stream #0.1: Audio: mp2, 48000 Hz, stereo, 64 kb/s
Stream mapping:
Stream #0.0 -> #0.0
Stream #0.1 -> #0.1
Press [q] to stop encoding
Compiler did not align stack variables. Libavcodec has been miscompiled
and may be very slow or crash. This is not a bug in libavcodec,
but in the compiler. You may try recompiling using gcc >= 4.2.
Do not report crashes to FFmpeg developers.
frame= 8 fps= 0 q=12.4 size= 4kB time=0.28 bitrate= 117.0kbits/s
frame= 16 fps= 14 q=31.0 size= 218kB time=0.60 bitrate=2976.4kbits/s
frame= 25 fps= 15 q=24.8 size= 342kB time=0.96 bitrate=2918.4kbits/s
frame= 34 fps= 16 q=31.0 size= 388kB time=1.32 bitrate=2408.0kbits/s
frame= 43 fps= 16 q=31.0 size= 460kB time=1.68 bitrate=2243.0kbits/s
frame= 52 fps= 16 q=31.0 size= 500kB time=2.04 bitrate=2007.8kbits/s
frame= 62 fps= 16 q=31.0 size= 542kB time=2.44 bitrate=1819.7kbits/s
frame= 69 fps= 16 q=31.0 size= 576kB time=2.72 bitrate=1734.8kbits/s
frame= 69 fps= 16 q=31.0 Lsize= 582kB time=2.72 bitrate=1752.8kbits/s

etc.

That's your script's input then, right there, with \n-terminated lines.
What part of it "does not work" on your system?
Grant Edwards
2010-02-09 21:13:39 UTC
Permalink
Post by Jan Stary
Post by Grant Edwards
The problem is that Unix tools all are mostly all based on a
line-at-a-time model. Reading a line at a time in shell or
Python is trivial. Reading lines terminated by something other
than \n requires you to read one character at a time and do
line buffering in the program.
No. All it takes is piping the output through "tr \\r \\n"
That doesn't work -- tr buffers it's output when it's output is
a pipe.
Post by Jan Stary
Post by Grant Edwards
Post by Jan Stary
You mean, the big issue of " ffmpeg ... 2>&1 | tr \\r \\n " ?
Have you tried that?
Yes.
Post by Grant Edwards
Hint: it doesn't work.
Meaning what exactly?
Meaning that tr buffers its output when its stdout is not a
tty. That means it's useless in shell-scripts where you want to
process lines as they are produced rather than in a big batch
after the program finishes.
Post by Jan Stary
That's your script's input then, right there,
No, not really. tr behaves differently when its stdout is not
a tty.
Post by Jan Stary
with \n-terminated lines. What part of it "does not work" on
your system?
Try this:

ffmpeg <options> 2>&1 | tr '\r' '\n' | cat

You'll see the problem.


You can do something like this:

perl -015l12pe 'BEGIN { $|++ }' |
(
# insert shell script here
)

But, now I've got a script that requires Perl be installed,
when the goal was to use as little as possible other than bash,
and ffmpeg. Sure, Perl is usually installed anyway, but it
seems like a pretty big dependancy to pull in just to fix some
line endings.
--
Grant Edwards grante Yow! I want to so HAPPY,
at the VEINS in my neck STAND
visi.com OUT!!
Jan Stary
2010-02-09 22:11:22 UTC
Permalink
Post by Grant Edwards
Post by Jan Stary
Post by Grant Edwards
The problem is that Unix tools all are mostly all based on a
line-at-a-time model. Reading a line at a time in shell or
Python is trivial. Reading lines terminated by something other
than \n requires you to read one character at a time and do
line buffering in the program.
No. All it takes is piping the output through "tr \\r \\n"
That doesn't work -- tr buffers it's output when it's output is
a pipe.
[...]
ffmpeg <options> 2>&1 | tr '\r' '\n' | cat
You'll see the problem.
Didn't know about the tr buffering. Thanks.
Post by Grant Edwards
perl -015l12pe 'BEGIN { $|++ }' |
(
# insert shell script here
)
But, now I've got a script that requires Perl be installed,
So let's just put it in ffmpeg itself; first attempt:


Index: ffmpeg.c
===================================================================
--- ffmpeg.c (revision 21731)
+++ ffmpeg.c (working copy)
@@ -1249,8 +1249,10 @@
bitrate = (double)(total_size * 8) / ti1 / 1000.0;

snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- "size=%8.0fkB time=%0.2f bitrate=%6.1fkbits/s",
- (double)total_size / 1024, ti1, bitrate);
+ "size=%8.0fkB time=%0.2f (%02d%%) bitrate=%6.1fkbits/s",
+ (double)total_size / 1024, ti1,
+ 100, /* FIXME: 100.0 * ti1 / inputduration? */
+ bitrate);

if (nb_frames_dup || nb_frames_drop)
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " dup=%d drop=%d",


Obviously, the "100" needs to be replaced with
100.0 * ti1 / duration_of_input, but I don't know
where to get that from within the print_report()
function.

You take it from here :-)
John Doe
2010-02-10 09:26:28 UTC
Permalink
From: Grant Edwards <grant.b.edwards at gmail.com>
Post by Grant Edwards
Post by Jan Stary
You mean, the big issue of " ffmpeg ... 2>&1 | tr \\r \\n " ?
Have you tried that?
Hint: it doesn't work.
What's so difficult about reading char by char, while contcatenating them to a string until you bump into a '\r'?
You analyse the string, then loop with an empty string...
That, or patch ffmpeg...

JD
Grant Edwards
2010-02-10 15:09:34 UTC
Permalink
Post by John Doe
From: Grant Edwards <grant.b.edwards at gmail.com>
Post by Grant Edwards
Post by Jan Stary
You mean, the big issue of " ffmpeg ... 2>&1 | tr \\r \\n " ?
Have you tried that?
Hint: it doesn't work.
What's so difficult about reading char by char, while
contcatenating them to a string until you bump into a '\r'?
I 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?
--
Grant Edwards grante Yow! I'm pretending I'm
at pulling in a TROUT! Am I
visi.com doing it correctly??
Jan Stary
2010-02-10 16:12:01 UTC
Permalink
Post by Grant Edwards
Post by John Doe
From: Grant Edwards <grant.b.edwards at gmail.com>
Post by Grant Edwards
Post by Jan Stary
You mean, the big issue of " ffmpeg ... 2>&1 | tr \\r \\n " ?
Have you tried that?
Hint: it doesn't work.
What's so difficult about reading char by char, while
contcatenating them to a string until you bump into a '\r'?
I 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.

(What was it again you wanted to do? Monitor the progress?
It is already monitored, kind of.)
Grant Edwards
2010-02-10 19:50:24 UTC
Permalink
Post by Jan Stary
Post by Grant Edwards
I 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. Nor would I
expect my "customers" to compile/install patched versions of
either.
Post by Jan Stary
(What was it again you wanted to do? Monitor the progress?
That and filter out the extraneous output from ffmpeg.
Post by Jan Stary
It is already monitored, kind of.)
Yes, it is.
--
Grant Edwards grante Yow! What I need is a
at MATURE RELATIONSHIP with a
visi.com FLOPPY DISK ...
Jan Stary
2010-02-10 20:29:15 UTC
Permalink
Post by Grant Edwards
Post by Jan Stary
Post by Grant Edwards
I 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 :-)
Post by Grant Edwards
Nor would I
expect my "customers" to compile/install patched versions of
either.
True.
Post by Grant Edwards
Post by Jan Stary
(What was it again you wanted to do? Monitor the progress?
That and filter out the extraneous output from ffmpeg.
Post by Jan Stary
It is already monitored, kind of.)
Yes, it is.
So what is the goal really? Have ffmpeg pipe through some
script that would produce some "nicer" progress line
and strip the version numbers etc?

As in, 'ffmpeg -i in out 2>&1 | show-progress-nicely' ?
Can you show us your script?
Grant Edwards
2010-02-10 22:27:26 UTC
Permalink
Post by Jan Stary
Post by Grant Edwards
Post by Jan Stary
Post by Grant Edwards
I 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 Stary
So 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 Stary
As 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
John Doe
2010-02-12 10:25:01 UTC
Permalink
From: Grant Edwards <grant.b.edwards at gmail.com>
Post by Grant Edwards
Post by John Doe
What's so difficult about reading char by char, while
contcatenating them to a string until you bump into a '\r'?
I 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?
You previously said "Shell or Python generally."
Python should be able to do that easily...
Otherwise, i tried this and it seems to work fine...
while :; do echo -n 'x'; sleep 1; printf "\r"; echo -n 'y'; sleep 1; printf "\r"; done | tr "\r" "\n"
There seems to be no buffering...

JD
Grant Edwards
2010-02-12 15:15:01 UTC
Permalink
Post by John Doe
From: Grant Edwards <grant.b.edwards at gmail.com>
Post by Grant Edwards
Post by John Doe
What's so difficult about reading char by char, while
contcatenating them to a string until you bump into a '\r'?
I 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?
You previously said "Shell or Python generally." Python should
be able to do that easily...
Yes, but in this particular case, I'm using bash, not Python.
Post by John Doe
Otherwise, i tried this and it seems to work fine...
while :; do echo -n 'x'; sleep 1; printf "\r"; echo -n 'y'; sleep 1; printf "\r"; done | tr "\r" "\n"
There seems to be no buffering...
As I've noted several times so far in this thread, tr buffers
its output when its stdout is not a tty.

Try this:

while :; do echo -n 'x'; sleep 1; printf "\r"; echo -n 'y'; sleep 1; printf "\r"; done | tr "\r" "\n" | cat

A solution that doesn't work when connected to a pipe isn't
very useful in a bash script for providing input to a "read"
command. Yes, I could probably figure out how to use a pty pair
in a bash script, but that's even less portable than the other
options.
--
Grant Edwards grante Yow! if it GLISTENS,
at gobble it!!
visi.com
Phil Rhodes
2010-02-12 16:11:45 UTC
Permalink
I did quite a bit of parsing of ffmpeg output in Javascript a while ago. I
didn't find a clean solution; it was pretty much a case of implementing a
reader for every item of information I needed, and praying it didn't break
on new versions.

P

Loading...