1

I'm writing a media server for my raspberry pi. I was able to create a program which uses popen to control omxplayer via a remote control.

I would now like to control mpg123 for music. I took the same code that worked in the omxplayer program with popen and applied it to mpg123, but it isn't working. It starts up, but won't acknowledge any input sent to it. I don't know why one would work and the other wouldn't.

Here is my code:

void play_music (char *list, int random)
{
    FILE *pp;
    char c;
    char command[501];
    struct stat buf;

    if(access(list, R_OK) == -1)
    {
        fprintf(stderr, "%s: play_music: access failed (%s) (%s)\n", program_name, strerror(errno), list);
        exit(EXIT_FAILURE);
    }

    if(stat(list, &buf) == -1)
    {
        fprintf(stderr, "%s: play_music: stat failed (%s) (%s)\n", program_name, strerror(errno), list);
        exit(EXIT_FAILURE);
    }

    strcpy(command, "/usr/bin/mpg123 -C ");

    if(random == 1)
        strcat(command, "-z ");

    if(S_ISREG(buf.st_mode) == 1)
    {
        strcat(command, "-@ ");
        strcat(command, list);
    }
    else if(S_ISDIR(buf.st_mode) == 1)
    {
        strcat(command, list);

        if(list[strlen(list) - 1] != '*')
        {
            if(list[strlen(list) - 1] != '/')
                strcat(command, "/");

            strcat(command, "*");
        }
    }
    else
    {
        fprintf(stderr, "%s: play_music: stat reported unknown (%s)\n", program_name, list);
        exit(EXIT_FAILURE);
    }

    strcat(command, " > /dev/null 2>&1");

    if((pp = popen(command, "w")) == NULL)
    {
        fprintf(stderr, "%s: play_music popen failed (%s)\n", program_name, strerror(errno));
        exit(EXIT_FAILURE);
    }

    while((c = get_code()))
    {
        if(system("pidof mpg123 > /dev/null") != 0)
            return;

        switch(c)
        {
            case 31:
                fputc('f', pp);
                break;
            case 32:
                fputc('d', pp);
                break;
            case 33:
                fputc('s', pp);
                break;
            case 34:
                fputc('q', pp);
        }

        if(fflush(pp) == EOF)
        {
            fprintf(stderr, "%s: play_music fflush failed (%s)\n", program_name, strerror(errno));
            exit(EXIT_FAILURE);
        }
    }
}

I've been trying to figure this out for far too long, can someone please help!

Notes:

  • get_code() is a working function that returns an int based on which remote control button is pressed.

  • The variable 'list' is either a directory path or a playlist filename.

  • The variable 'random' is an int flag (1 for random play).

Deanie
  • 2,316
  • 2
  • 19
  • 35
  • Are you sure your mpg123 usage is correct (check it manually in the shell)? e.g., shouldn't `-@` option imply that the playlist is given on stdin, not in a file? – jfs Nov 12 '15 at 18:45
  • @J.F.Sebastian Yes, the usage is correct. I tested it by printing the full command used by popen, and running it on the command line. Works like it is supposed to. – Deanie Nov 12 '15 at 18:52
  • Did you input the commands from the keyboard or using `{ echo -n command; sleep 5; echo -n command; } | mpg123 ..`? does it read commands from the terminal directly or from its stdin? Unrelated: check for errors during I/O (after `fflush()`) – jfs Nov 12 '15 at 19:02
  • @J.F.Sebastian Yes it would have been from the keyboard as I just cut and paste what it outputted. Doing it with echo appears not to work. fflush() reports no errors. – Deanie Nov 12 '15 at 19:06
  • @J.F.Sebastian I just tried the echo test on omxplayer and it works with it, but mpg123 doesn't This makes it more confusing to me! – Deanie Nov 12 '15 at 19:12
  • 1
    read the first reason in: [Q: Why not just use a pipe (popen())?](http://pexpect.readthedocs.org/en/stable/FAQ.html#whynotpipe). Find out whether there is a command-line switch that would force `mpg123` to read the commands from stdin (or some other fd). Otherwise, you might need to provide a pseudo-tty yourself. [Here's an example in Python that shows how to *read* output printed to a tty directly (your case is *writing*)](http://stackoverflow.com/a/12471855/4279) – jfs Nov 12 '15 at 19:20

1 Answers1

1

As J.F. Sebastian stated in the comments, "provide a pseudo-tty yourself" turned out to be what I needed to do. Creating a pseudo terminal to act like the input came from the keyboard is what is needed to work.

Here is the revised code:

void play_music (char *playlist, int random)
{
    char c;
    int fdm;
    int fds = 0;
    int x = 0;
    pid_t pid;
    char *flag1 = "/usr/bin/mpg123";
    char *flag2 = "-C";
    char *flag3 = "-z";
    char *flag4 = "-@";
    char *flag5 = TEMP_FILE; // /tmp/temp_playlist.pls
    char *slave_name;
    char *argv[6];
    struct stat buf;

    if(stat(playlist, &buf) == -1)
    {
        fprintf(stderr, "%s: play_music: stat failed (%s) (%s)\n", program_name, strerror(errno), playlist);
        exit(EXIT_FAILURE);
    }

    if((fdm = posix_openpt(O_RDWR)) == -1)
    {
        fprintf(stderr, "%s: posix_openpt failed (%s)\n", program_name, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if(grantpt(fdm) == -1)
    {
        fprintf(stderr, "%s: grantpt failed (%s)\n", program_name, strerror(errno));
        close(fdm);
        exit(EXIT_FAILURE);
    }

    if(unlockpt(fdm) == -1)
    {
        fprintf(stderr, "%s: unlockpt failed (%s)\n", program_name, strerror(errno));
        close(fdm);
        exit(EXIT_FAILURE);
    }

    if((slave_name = ptsname(fdm)) == NULL)
    {
        fprintf(stderr, "%s: ptsname failed\n", program_name);
        close(fdm);
        exit(EXIT_FAILURE);
    }

    if((pid = fork()) == -1)
    {
        fprintf(stderr, "%s: fork failed (%s)\n", program_name, strerror(errno));
        close(fdm);
        exit(EXIT_FAILURE);
    }

    if(pid == 0) // Child
    {
        if(setsid() == -1)
        {
            fprintf(stderr, "%s: setsid failed (%s)\n", program_name, strerror(errno));
            close(fdm);
            exit(EXIT_FAILURE);
        }

        if((fds = open(slave_name, O_RDWR)) == -1)
        {
            fprintf(stderr, "%s: open failed (%s)\n", program_name, strerror(errno));
            close(fdm);
            exit(EXIT_FAILURE);
        }

        if(dup2(fds, STDIN_FILENO) == -1)
        {
            fprintf(stderr, "%s: dup2(1) failed (%s)\n", program_name, strerror(errno));
            close(fdm);
            close(fds);
            exit(EXIT_FAILURE);
        }

        if(dup2(fds, STDOUT_FILENO) == -1)
        {
            fprintf(stderr, "%s: dup2(2) failed (%s)\n", program_name, strerror(errno));
            close(fdm);
            close(fds);
            exit(EXIT_FAILURE);
        }

        if(dup2(fds, STDERR_FILENO) == -1)
        {
            fprintf(stderr, "%s: dup2(3) failed (%s)\n", program_name, strerror(errno));
            close(fdm);
            close(fds);
            exit(EXIT_FAILURE);
        }

        argv[x++] = flag1;
        argv[x++] = flag2;

        if(random == 1)
            argv[x++] = flag3;

        argv[x++] = flag4;

        if(S_ISREG(buf.st_mode) == 1)
            argv[x++] = playlist;
        else if(S_ISDIR(buf.st_mode) == 1)
        {
            if(make_playlist(playlist) == RETURN_FAILURE)
            {
                fprintf(stderr, "%s: play_music: make_playlist failed (%s)\n", program_name, playlist);
                close(fdm);
                close(fds);
                exit(EXIT_FAILURE);
            }

            argv[x++] = flag5;
        }
        else
        {
            fprintf(stderr, "%s: play_music: stat reported unknown (%s)\n", program_name, playlist);
            close(fdm);
            close(fds);
            exit(EXIT_FAILURE);
        }

        argv[x++] = NULL;

        if(execv("/usr/bin/mpg123", argv) == -1)
        {
            fprintf(stderr, "%s: execv failed (%s)\n", program_name, strerror(errno));
            close(fdm);
            close(fds);
            exit(EXIT_FAILURE);
        }
    }
    else // Parent
    {
        while((c = get_code()) != 31)
        {
            switch(c)
            {
                case 30:
                case 19:
                    write(fdm, " ", 1);
                    break;
                case 20: // Up
                    write(fdm, "+", 1);
                    break;
                case 22: // Down
                    write(fdm, "-", 1);
                    break;
                case 21: // Right
                    write(fdm, "f", 1);
                    break;
                case 23: // Left
                    write(fdm, "d", 1);
                    break;
            }
        }

        write(fdm, "q", 1);
        wait(&pid);
        close(fdm);

        if(fds != 0)
            close(fds);
    }

}
Deanie
  • 2,316
  • 2
  • 19
  • 35