Can't use named pipe from C to communicate with shell script

你离开我真会死。 提交于 2019-12-01 12:53:29

Here's how you could emulate { sleep 10; echo -n p ; } | omxplayer command:

/* for clock_nanosleep() */
#if ! defined(_POSIX_C_SOURCE)  || _POSIX_C_SOURCE < 200112L
#define _POSIX_C_SOURCE 200112L
#endif

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <unistd.h>

#define Close(FD) do {                                          \
    const int Close_fd = (FD);                                  \
    if (close(Close_fd) == -1)                                  \
      fprintf(stderr, "%s:%d: close(" #FD ") %d: %s\n",         \
              __FILE__, __LINE__, Close_fd, strerror(errno));   \
  }while(0)

static void _report_error_and_exit(const char* msg) {
  perror(msg);
  _exit(EXIT_FAILURE);
}

static void report_error_and_exit(const char* msg) {
  perror(msg);
  exit(EXIT_FAILURE);
}

int main(void) {
  int fd[2]; /* pipe */
  pid_t pid = -1;

  if (pipe(fd) == -1)
    report_error_and_exit("pipe");

  if ((pid = fork()) == -1)
    report_error_and_exit("fork");
  else if (pid == 0)  { /* child: sleep, write */
    Close(fd[0]); /* close unused read end of the pipe */

    { /* sleep until specified time */
      struct timespec endtime = {0};
      int r = -1;

      /* set some time into the future (just as an example) */
      if (clock_gettime(CLOCK_REALTIME, &endtime) < 0)
        _report_error_and_exit("clock_gettime");
      endtime.tv_sec += 10; /* seconds */

      /* NOTE: use settable system-wide real-time clock */
      while ((r = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME,
                                  &endtime, NULL)) == EINTR)
        ; /* retry */

      if (r != 0)
        _report_error_and_exit("clock_nanosleep");
    }

    { /* write to the pipe */
      char c = 'p';
      ssize_t size = sizeof c, n = size, m = 0;
      while (n > 0) { /* until there is something to write */
        if ((m = write(fd[1], &c + (size - n), n)) > 0)
          n -= m;
        else if (errno != EINTR)
          _report_error_and_exit("write");
      }
    }
    _exit(EXIT_SUCCESS); /* child done */
  }

  /* parent: run `omxplayer < fd[0]` */
  Close(fd[1]); /* close unused write end of the pipe */

  /* redirect stdin */
  if (fd[0] != STDIN_FILENO) {
    if (dup2(fd[0], STDIN_FILENO) == -1)
      report_error_and_exit("dup2");
    else
      Close(fd[0]);
  }
  execlp("omxplayer", "omxplayer", NULL);
  _report_error_and_exit("execlp");
}

To try it, run:

$ gcc *.c -lrt && ./a.out

It should start omxplayer immediately and write to it p at current time plus 10 seconds. It is written to use an absolute time for the sleep.

It should help you to tick the next item on your omxplayer-sync list. But it won't solve the issue of synchronizing playback on multiple video players on different hosts.

It might be useful to use a program which is made for reading stdin.

echo does not do so, but it prints its command line arguments. That is a difference.

Try cat.

Floris

You could use

xargs < testpipe echo

This will pass the output of testpipe (one line at a time) as an argument to echo - and produces

We are not alone

to the screen...

Note - your comment "I want to modify the C code to work with echo", without considering how what echo coes (in particular, that it doesn't interact with pipes), seems backwards. Your real question should be "how do I get commands from C into omxplayer"?

I found the following code snippet for omxplayer control here

mkfifo /tmp/cmd

omxplayer -ohdmi mymedia.avi < /tmp/cmd

echo . > /tmp/cmd (Start omxplayer running as the command will initial wait for input via the fifo)

echo -n p > /tmp/cmd - Playback is paused

echo -n q > /tmp/cmd - Playback quits

This suggests to me that the following should work for you:

omxplayer -ohdmi mymedia.avi < testpipe

Where your C program is producing characters as needed to feed omxplayer. Just don't close the pipe, keep the C program running. However, when I tried writing a little test program to confirm this:

#include <stdio.h>
#include <fcntl.h>
#define PATH "testpipe"

int main(void) {
    int fd;
    char c;

    mkfifo(PATH, 0666);
    fd = open( PATH, O_WRONLY);
    char keyStroke[2] = " ";

    while((c = getchar())!='q') {
        keyStroke[0] = c;
        write(fd, keyStroke, 1);
    }

    close(fd);
    unlink(PATH);
    return 0;
}

I found the above did not get any response until I hit 'q' at the sending terminal - in other words, until the pipe was closed. A little searching go me to https://stackoverflow.com/a/4060641/1967396 where they suggested a lower level "pipe reading program", so I implemented that:

#include <stdio.h>
#define PIPE "testpipe"

int main(void) {
    FILE *fp;
    fp = fopen(PIPE, "r");
    int c;

    while((c = getc(fp)) != EOF)
    {
        printf("%c", c);
    }

    fclose(fp);
    return 0;
}

Running this in one terminal, and the earlier program in another terminal, I could now see the characters from one terminal appear in the other one. And this, I think, is what you wanted (although I still had to press "enter" after each character at the input terminal because I was using getchar(), but I am sure you can figure out how to fix that).

Let me know if this is helpful...

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!