It seems that -C
causes mpg123 to read from the terminal, not from stdin. I see this, however, in my version of mpg123's man page:
-R, --remote
Activate generic control interface. mpg123 will then read and
execute commands from stdin. Basic usage is ``load <filename> ''
to play some file and the obvious ``pause'', ``command. ``jump
<frame>'' will jump/seek to a given point (MPEG frame number).
Issue ``help'' to get a full list of commands and syntax.
This may be what you are looking for; try mpg123 -vR <pipe
. The
interaction in your example would become something like the following
(this sets the volume to 30%):
$ cat >pipe
load /some/song.mp3
volume 30
But then, what does -C
do that -R
doesn't that results in the
former mode failing to read from stdin when a named pipe, rather than
a terminal, is connected?
A quick look at the mpg123 source code indicates that it uses the
termios facilities to read keypresses from the terminal, using
tcsetattr
to put it in the so-called "non-canonical mode", where
keypresses are transmitted to the reader without further processing
(in particular, without waiting for a complete line to have been
typed):
struct termios tio = *pattern;
(...)
tio.c_lflag &= ~(ICANON|ECHO);
(...)
return tcsetattr(0,TCSANOW,&tio);
(This is the same as the GNU libc code sample.)
Then, in a loop, a function get_key
is called, which uses select
to tell whether file descriptor 0 (stdin) has data available and, if
so, reads one byte from it (read(0,val,1)
). But this still doesn't
explain why a terminal works but a pipe doesn't! The answer lies in
the terminal initialization code:
/* initialze terminal */
void term_init(void)
{
debug("term_init");
term_enable = 0;
if(tcgetattr(0,&old_tio) < 0)
{
fprintf(stderr,"Can't get terminal attributes\n");
return;
}
if(term_setup(&old_tio) < 0)
{
fprintf(stderr,"Can't set terminal attributes\n");
return;
}
term_enable = 1;
}
Note that if either tcgetattr
or term_setup
fails, then
term_enable
is set to 0. (The function to read keys from the terminal starts with if(!term_enable) return 0;
.) And, indeed, when stdin isn't a terminal,
tcgetattr
fails, the corresponding error message is printed, and the
keypress-handling code is skipped:
$ mpg123 -C ~/input.mp3 <pipe
(...)
Can't get terminal attributes
This explains why attempting to send commands by piping into mpg123
-C
fails. That's a debatable choice by the implementors; presumably
by simply allowing tcgetattr
/ tcsetattr
to fail (perhaps by using
a switch for that purpose), instead of disabling the keypress-handling
code handling, your attempt would have worked.
(I am trying to understand what is happening better so I can apply this to future, non-mpg123 problems)
Thanks!
– virosa Apr 29 '15 at 01:27-C
is specified, and just ignores commands if something else (such as a pipe) is connected to stdin. – dhag Apr 29 '15 at 02:21