1

I have a C program like so (copied from here):

#include <fcntl.h>

#define PATH "testpipe"
#define MESSAGE "We are not alone"

int main()
{
   int fd;

   mkfifo ( PATH, 0666 );

   fd = open ( PATH, O_WRONLY );
   write ( fd, MESSAGE, sizeof ( MESSAGE ) );
   close ( fd );

   unlink ( PATH );
   return 0;
}

and a shell script like so:

echo < testpipe

Once I execute the C program, the echo statement returns, but We are not alone is not printed. I have also tried creating the pipe from the command line, and with mknod instead, and it makes no difference. Why is this not working?

EDIT:

A lot of people have pointed out that the problem is with echo and not with C, problem is, I need it to work with something like echo (omxplayer actually, as I am issuing video control commands to it, ie. p for pause or q for quit). Therefore, I would appreciate some answers indicating how to change the C code to make it work with the echo statement, as opposed to vice versa

EDIT:

I didn't include the full code as it uses omxplayer and is rather large, but some users requested it, therefore, here is as minimal as I could keep this MWE:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define PIPEPATH "testpipe"
#define VIDEOPATH "Matrix.mkv"
#define MESSAGE "q"
#define VIDEOPLAYER "omxplayer"

int main()
{
   int fd;
   pid_t pid;
   pid_t wpid;
   int status;
   char shellCmd [ 1000 ];
   struct timespec time1, time2; //used for sleeping

   //Make pipe BEFORE forking
   mkfifo ( PIPEPATH, 0666 );

   if ( ( pid = fork () ) < 0 )
   {
      perror ( "Fork Failed\n" );
      return -1;
   }
   else if ( pid == 0 )
   { //first child keeps pipe open
      sprintf ( shellCmd, "tail -f /dev/null > %s", PIPEPATH );
      if ( system ( shellCmd ) == -1 )
      {
         printf ( "Error: %s\n", shellCmd );
         fflush(stdout);
      }
   }
   else{
      time1.tv_sec = 1L; //sleep for 1 second to let first child issue its command
      time1.tv_nsec = 0L; //Dont worry about milli seconds

      nanosleep ( &time1, &time2 );

      if ( ( pid = fork () ) < 0 )
      {
         perror ( "Fork Failed\n" );
         return -1;
      }
      else if ( pid == 0 )
      { //second child launches the movie
         sprintf ( shellCmd,  "cat %s | %s %s 2>&1 > /dev/null", PIPEPATH, VIDEOPLAYER,  VIDEOPATH );
         if ( system ( shellCmd ) == -1 )
         {
            printf ( "Error: %s\n", shellCmd );
            fflush(stdout);
         }
      }
      else
      {
         if ( ( pid = fork () ) < 0 )
         {
            perror ( "Fork Failed\n" );
            return -1;
         }
         else if ( pid == 0 )
         { //third child waits 5 seconds then quits movie
            time1.tv_sec = 5L; //sleep for 5 seconds
            time1.tv_nsec = 0L; //Dont worry about milli seconds

            nanosleep ( &time1, &time2 );

            printf ( "Sleep over, quiting movie\n");
            fflush(stdout);

            fd = open ( PIPEPATH, O_WRONLY );
            write ( fd, MESSAGE, sizeof ( MESSAGE ) );
            close ( fd );
         }
      }
   }

   //Note the first child will never exit as it is a blocking shell script
   while ( ( wpid = wait ( &status ) ) > 0 )
   {
      printf ( "Exit status of %d was %d (%s)\n", ( int ) wpid, status, ( status == 0 ) ? "accept" : "reject" );
      fflush(stdout);
   }

   unlink ( PIPEPATH );

   return 0;

As I pointed out, the program will never exit, so just issue a Ctrl+C }

Edit 3:

Ok I am making progress, it seems that what you give to the pipe and what you get back are not the same thing, run this script and observe:

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

int main( int argc, char *argv[] ) {
   int fd;
   FILE *fp;
   char c;

   if ( atoi ( argv [ 1 ] ) == 1 )
   {
      printf ("Writer [%s]\n", argv[1]);

      mkfifo ( PIPE_PATH, 0666 );

      fd = open ( PIPE_PATH, O_WRONLY );
      c = getchar();
      write(fd, c, 1);

      close(fd);
   }
   else if ( atoi ( argv [ 1 ] ) == 2 )
   {
      printf ( "Reader [%s]\n", argv[1] );

      fp = fopen( PIPE_PATH, "r" );
      c = getc ( fp );
      putchar ( c );

      printf ( "\n" );
      fclose ( fp );

      unlink( PIPE_PATH );
   }

   return 0;
}

EDIT 4:

J.F. Sebastian asked some good questions, this is in response to those questions. What I am ultimately trying to do is to synchronize 2 or more instances of omxplayer playing the same movie on 2 or more raspberry pis. omxplayer-sync tries to achieve this, but it is not accurate, it is written in Python which is not suitable for this, and its approach is, IMO, not a good one. I am working my way up the food chain trying out the easiest solutions to see if they are viable. From easiest to hardest, here is what I have tried so far (or plan on trying)

  1. Launch omxplayer instances from the shell at an agreed upon time in future at the same time: FAIL
  2. Launch omxplayer instances from a Python script (more accurate time wise) at an agreed upon time in future at the same time: FAIL
  3. Launch omxplayer instances from a C program (even more accurate time wise) at an agreed upon time in future at the same time: FAIL
  4. Launch and immediately pause, then unpause omxplayer instances from a Python script (more accurate time wise) at an agreed upon time in future at the same time: FAIL
  5. Launch and immediately pause, then unpause (via echo -n p > namedpipe) omxplayer instances from a C program (more accurate time wise) at an agreed upon time in future at the same time: FAIL
  6. Launch and immediately pause, then unpause (via write ( fd_of_named_pipe, 'p', sizeof ( 'p') );) omxplayer instances from a C program (more accurate time wise) at an agreed upon time in future at the same time: PENDING
  7. Reimplement omxplayer and modulate play speeds to catch up with fastest player: FUTURE

Basically before investing a huge chunk of my life into understanding and then modifying the source code of omxplayer (7) I want to see if using C's native write ( fd_of_named_pipe, 'p', sizeof ( 'p') ) operation is faster than its system(echo -n p > namedpipe) call which forks a child and calls the shell to write to the named pipe (my hunch tells me it will be much faster, and hopefully more accurate). If this works and unpauses all instances to within 15ms, fantastic, I won't have to look at omxpleyer's source code ever. If not, as a last resort I will start modifying the source code.

Community
  • 1
  • 1
puk
  • 16,318
  • 29
  • 119
  • 199
  • 3
    Although it's not the primary problem here, **you need to check your function calls for errors**. If any of the `mkfifo`, `open` or `write` fail, nothing will be written without you noticing it. – Jo So Nov 22 '13 at 23:04
  • @Floris I am assuming someone deleted their comment, b/c as is your first comment seems a little bizarre – puk Nov 22 '13 at 23:41
  • @puk - yes, I was doing some clean up with wanted to give glglgl a chance to see my last comment. – Floris Nov 22 '13 at 23:47
  • Can you explain a little more what you want "echo" to do, and why `cat` cannot do the same thing for you? Are you piping the output from `echo` into something else? Are you trying to keep the pipe open? – Floris Nov 22 '13 at 23:49
  • @Floris the reason I didn't include it is because not everyone has omxplayer, but I will edit it into the question. Give me a minute to type it out – puk Nov 22 '13 at 23:51
  • Could you describe what do you want your C program to do using plain English and/or working shell commands? – jfs Nov 24 '13 at 04:28
  • @J.F.Sebastian I want to launch `omxplayer` with one child, then pause it (press `p`) with the other child – puk Nov 24 '13 at 06:14
  • @puk: Do you want to emulate `echo -n p | omxplayer` shell command? – jfs Nov 24 '13 at 08:01
  • @J.F.Sebastian sorry, I should have mentioned that one child launches the `omxplayer`, reading input from a named pipe, and the second child sleeps for, lets say, 10 s, then it pauses the player by issuing a `'p'` to the same named pipe. – puk Nov 24 '13 at 08:37
  • @puk: Do you want to emulate `{ sleep 10; echo -n p ; } | omxplayer` shell command? If you want a named pipe; it could be written as `omxplayer < <(sleep 10; echo -n p)` – jfs Nov 24 '13 at 10:22
  • @J.F.Sebastian I answered your question in the last edit – puk Nov 24 '13 at 11:08
  • 1
    Your last edit makes this a much more interesting and better (if completely different) question: how to synch two processes on different machines by emulating pressing a key at the same time. Do you have a spec on "same time"? Is 15 ms your limit? – Floris Nov 24 '13 at 12:47
  • One thing not clear: these are multiple instances on different devices, right? What you have been talking about so far is all happening on one device. A named pipe lives on one machine. The key here, I think, is "near real time" inter device communication. Or am I missing your point completely? – Floris Nov 24 '13 at 12:55
  • 1
    Just found http://stackoverflow.com/questions/19853557/what-algorithm-approach-to-use-to-synchronize-multiple-video-players - a very relevant earlier question that gives a lot of background. It seems you want to synch not just the start signal... Is that right? – Floris Nov 24 '13 at 13:44
  • @Floris "same time" here means the audio should be un-discernible, or about 15ms – puk Nov 24 '13 at 19:11
  • @Floris yes a named pipe lives on only one system (will fail in nfs), but I am using NTP to sync the times, therefore if it launches within 15ms locally, then it will do so globally – puk Nov 24 '13 at 19:12
  • @Floris I actually asked that question, and that pertains to reimplementing `omxplayer` – puk Nov 24 '13 at 19:15
  • What if you start every player "at an exact time" and rely on (well eatablished) network time synch protocols? – Floris Nov 25 '13 at 03:44
  • @Floris 'start' would not work as 'starting' a video player is a complicated process, however, you can 'unpause' at the same time (which is less complicated and more reliable) and use NTP to sync the times. This is what I am doing right now, and works pretty well. I have had a few problems which might be coding related, and in one case one client crashed. However, if for whatever reason the movie skips, you are screwed – puk Nov 25 '13 at 03:51

3 Answers3

2

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.

glglgl
  • 89,107
  • 13
  • 149
  • 217
1

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

Community
  • 1
  • 1
Floris
  • 45,857
  • 6
  • 70
  • 122
  • One thing, I don't think `-ohdmi` is ncessary, it just redirects the audio – puk Nov 23 '13 at 00:50
  • I find your last two examples confusing, the last one for example does not even create the pipe. Is one writing to the pipe and the other reading from? – puk Nov 23 '13 at 01:13
  • Yes the last pair work together - write to pipe, read from pipe (and echo to screen). Taking the output of the second as input to `omxplayer` might be the solution you need - but I had to do a lot of guessing about what you really wanted... – Floris Nov 23 '13 at 23:01
  • I think You have made a lot of mistakes. For example, why are you writing `Keystroke`, instead of `c`, also, you are closing `fd` but you never opened it. – puk Nov 23 '13 at 23:21
  • You are right. I wrote this on a computer without internet access and had to re-type the code into a browser on another computer in order to answer. I made a lot of mistakes - my bad. My original code ran fine. I have edited the code - it now runs ok. – Floris Nov 24 '13 at 01:35
  • use `int c` otherwise you might get infinite loop. `EOF` is a negative integer and `char` may be unsigned. – jfs Nov 24 '13 at 15:18
  • @JFSebastian - thanks for the suggestion. I had copied that snippet (see reference) and not given that much thought. Agree it is safer (although it worked for me ... But that is a risky position to take, I know). – Floris Nov 24 '13 at 15:35
0

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.

jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • @puk: btw, why do you think python is not suitable to [start the player, wait, unpause it with milliseconds accuracy](https://gist.github.com/zed/7770492)? – jfs Dec 03 '13 at 15:01
  • I ran a few tests and I found noticeable sound delays with Python, but not with C. I concluded that C's timing was more accurate than Python's – puk Dec 03 '13 at 23:04
  • do you hear noticable delay with [the script I provided](https://gist.github.com/zed/7770492)? – jfs Dec 03 '13 at 23:06
  • if memory serves me right, this is the version which worked flawlessly. I abandoned it when I realized that about 30mins in there would be a noticeable drift. I had planned to keep this script, launch the clients at the same time and then use tcp to synchronize continuously, but, as I mentioned to you somewhere else, setting up a tcp connection is actually easier and less error prone than forks and pipes – puk Dec 03 '13 at 23:28