Why won't popen communicate with mpg123?

2019-08-14 02:21发布

问题:

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).

回答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);
    }

}


标签: c linux popen