Andy's CarPlayer

/***************************************************************************
                          mpg_interface.c  -  description
                             -------------------
    begin                : Tue Feb 3 2004
    copyright            : (C) 2004 by Andy Clews
    email                : andy  clews  blueyonder  co  uk
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include 
 #include 
 #include 

// #include "get_songs.h"
 #include "mpg_interface.h"
 #include "log.h"
 #include "tools.h"

/***************************************************************************
 *                                                                         *
 *                      Start of Procedures                                *
 *                                                                         *
 ***************************************************************************/

 // Catch signal of exiting player...
 void mpg123_child(pid_t pid, int status)
 {
   if (pid == mpg123_pid)
   {
     log_printf(LOG_NORMAL, "mpg123_child(): player %d exited with code %d\n", pid, WEXITSTATUS(status));
     mpg123_pid = 0;
   }
 }

 void SignalHandler(int sig)
 {
    sig = sig;
    log_printf(LOG_NORMAL, "Caught CTRL-C, exiting\n");
    mpg123_quit();
//    terminate = 1;
//    endwin();
    exit(0);
 }
 void SignalHandler1(int sig)
 {
    sig = sig;
    log_printf(LOG_NORMAL, "Caught Segmentation Fault, exiting\n");
    mpg123_quit();
//    terminate = 1;
//    endwin();
    printf("Caught Segmentation Fault - Exiting!\n");
    exit(0);
 }

 void mpg123_quit(void)
 {
   int retval;
   retval = sendmesg(mpg123_fdsend, "QUIT\n");
   if (retval < 0)
   {
      log_printf(LOG_NORMAL, "mpg123_quit ; send error : %s\n", strerror(errno));
   }
   else
   {
     log_printf(LOG_NORMAL, "mpg123 Shutdown\n");
   }
   kill (mpg123_pid, SIGKILL);
//   sleep(1);
 }
 
 // Start of player process...
 int mpg123_start(void)
 {
   int fd_send[2], fd_recv[2];
   int i;
   
   char *args[50];

   // Check that mpg123 is not already running...
   if (mpg123_pid)
    return 0;

   if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd_send) < 0)
     return -1;

   if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd_recv) < 0)
     return -1;

   // Handle signals...
   signal(SIGPIPE, SIG_IGN);

   // Get additional options...
   if ((args[0] = (char *)calloc(0, 4)) == NULL)
     perror("Calloc failed");
   strcpy(args[0], "mpg123");

   // start mpg123 in remote
   if ((args[1] = (char *)calloc(0, 3)) == NULL)
     perror("Calloc failed");
   strcpy(args[1], "-R");

   
   // set an output buffer of 0 Mb
   if ((args[2] = (char *)calloc(0, 4)) == NULL)
     perror("Calloc failed");
   strcpy(args[2], "-b 0");
      
   i = 3;
   while (args[i])
     args[++i] = strtok(NULL, " ");
   args[i] = "-";
   args[++i] = NULL;

   // fork child process
   mpg123_pid = fork();
   if (mpg123_pid == 0)
   {
     // pipe in/ out though socket to parent
     dup2(fd_send[0], STDIN_FILENO);
     close(fd_send[1]);
     dup2(fd_recv[0], STDOUT_FILENO);
     dup2(fd_recv[0], STDERR_FILENO);
     close(fd_recv[0]);
     close(fd_recv[1]);

     //spawn player
     execvp("mpg123", args);

     // this is never reached if exec was OK...
     log_printf(LOG_NORMAL, "oops...\n");
     exit(-10);
   }

   // Parent continues here...

   free(args[0]);
   free(args[1]);

   close(fd_send[0]);
   mpg123_fdsend = fd_send[1];
   close(fd_recv[0]);
   mpg123_fdrecv = fd_recv[1];

   // check if player is running

   sleep(1);
   if (!mpg123_pid)
   {
     log_printf(LOG_NORMAL, "mpg123_start : Player_process didn't start\n");
     return -1;
   }
   else
   {
     log_printf(LOG_NORMAL, "mpg123_start : Player spawned on PID %d, piped on fd %d and %d\n", mpg123_pid, mpg123_fdsend, mpg123_fdrecv);
     return 0;
   }
 }

 //  Load song & start playing
 int mpg123_load(char *filename)
 {
   int retval;

   char buf[132];

   sprintf(buf, "LOAD %s\n", filename);
   retval = sendmesg(mpg123_fdsend, buf);

   if (retval < 0)
     log_printf(LOG_NORMAL, "mpg123_load() : send error : %s\n", strerror(errno));

   return retval;
 }

 //  Pause / unpause player.

 int mpg123_pause(void)
 {
   int retval;

   retval = sendmesg(mpg123_fdsend, "PAUSE\n");

   if (retval < 0)
     log_printf(LOG_NORMAL, "mpg123_pause() : send error : %s\n", strerror(errno));

   return retval;
 }

  //  Stop playing a track, without killing mpg123.

 int mpg123_stop(void)
 {
   int retval;

   retval = sendmesg(mpg123_fdsend, "STOP\n");

   if (retval < 0)
     log_printf(LOG_NORMAL, "mpg123_stop() : send error : %s\n", strerror(errno));

   return retval;
 }

 // Jump forwards / backwards

 int mpg123_jump(char *dir)
 {
   int retval;

   char buf[10];

   log_printf(LOG_NOISY_DEBUG, "%s\n", dir);
   if (dir == "0")
     sprintf(buf, "JUMP %s\n", dir);
   else
     sprintf(buf, "JUMP %s500\n", dir);

   retval = sendmesg(mpg123_fdsend, buf);

   if (retval < 0)
     log_printf(LOG_NORMAL, "mpg123_jump() : send error : %s\n", strerror(errno));
   return retval;
 }

 int mpg123_big_jump(char *dir)
 {
   int retval;

   char buf[10];

   log_printf(LOG_NOISY_DEBUG, "%s\n", dir);
   if (dir == "0")
     sprintf(buf, "JUMP %s\n", dir);
   else
     sprintf(buf, "JUMP %s2000\n", dir);

   retval = sendmesg(mpg123_fdsend, buf);

   if (retval < 0)
     log_printf(LOG_NORMAL, "mpg123_jump() : send error : %s\n", strerror(errno));
   return retval;
 }
  int mpg123_position(int framepos)
 {
   int retval;

   char buf[10];

   log_printf(LOG_DEBUG, "mpg123_position() : Jump to %i\n", framepos);
     sprintf(buf, "JUMP %i\n", framepos);

   retval = sendmesg(mpg123_fdsend, buf);

   if (retval < 0)
     log_printf(LOG_NORMAL, "mpg123_position() : send error : %s\n", strerror(errno));
   return retval;
 }

 //  Read mpg123 input stream.

 int mpg123_read(void)
 {
   static int lastframeremain;
   static int lastsecpos, lastsecremain;

   char s[512], *c1, *c2, *c3, *c4, *c5;

   int rc, secpos, secremain;

   // read the response from the player.
   rc = readline(mpg123_fdrecv, s, sizeof(s));
   if (rc < 0)
   {
      log_printf(LOG_NORMAL, "mpg123 closed the connection!!\n");
      return -10;
   }

   if (!s || !*s) {
//     log_printf(LOG_NOISY_DEBUG, "No data from mpg123 pipe\n");
     return -2;
   }

     c1 = s ? strtok(s, " \t") : NULL;

     if (c1 && !strcasecmp(c1, "@R"))
     {
        // player version received
        c2 = c1 ? strtok(NULL, "") : NULL;
        log_printf(LOG_NORMAL, "Version : %s\n", c2);
        return PLAYER_VERSION;
     }
     else if (c1 && !strcasecmp(c1, "@P"))
     {
        // playing status received
        c2 = c1 ? strtok(NULL, " \t") : NULL;
        c3 = c2 ? strtok(NULL, " \t") : NULL;
        if (atoi(c2) == 0)
        {
           // EOF can be flagged many ways.  "@P 0 EOF" should be the norm
           if ((c3 && !strcasecmp(c3, "EOF")) || (lastframepos > 10 && lastframeremain < 10 ))
           {
              log_printf(LOG_NORMAL, "End of song.\n");
              return SONG_END;
           }
           else
           {
              log_printf(LOG_NORMAL, "Song stopped.\n");
              //return SONG_STOP;  // commented out to replace with a simpler SONG_END
              return SONG_END;
           }
        }
        else if (atoi(c2) == 1)
        {
          log_printf(LOG_NORMAL, "Player paused\n");
          return SONG_PAUSE;
        }
        else if (atoi(c2) == 2)
        {
          log_printf(LOG_NORMAL, "Player unpaused.\n");
          return SONG_PLAY;
        }
        else if (atoi(c2) == 3)
        {
          log_printf(LOG_NORMAL, "End of song.\n");
          return SONG_END;
        }
     }
     else if (c1 && !strcasecmp(c1, "@I"))
     {
       // TAG data received.
       c2 = c1 ? strtok(NULL, "") : NULL;
       if (!strncasecmp(c2, "ID3:", 4))
       {
         // ID3 tag
         return SONG_INFO;
       }
       else
       {
         log_printf(LOG_NORMAL, "No Tag Data...\n");
       }
     }
     else if (c1 && !strcasecmp(c1, "@S"))
     {
       // Status message after loading a song (stream info)
     }
     else if (c1 && !strcasecmp(c1, "@F"))
     {
       // Frame info
//log_printf(LOG_NOISY_DEBUG, "Frame info message : %s %s \n", c1, c2);
       c2 = c1 ? strtok(NULL, " \t") : NULL;
       c3 = c2 ? strtok(NULL, " \t") : NULL;
       c4 = c3 ? strtok(NULL, " \t") : NULL;
       c5 = c4 ? strtok(NULL, " \t") : NULL;
       lastframepos = atoi(c2);
       lastframeremain = atoi(c3);
       secpos = (int)atof(c4);
       secremain = (int)atof(c5);

       play_time = secpos;
       play_time_remaining = secremain;
log_printf(LOG_NOISY_DEBUG, "Play times: lastframepos-> %i lastframeremain-> %i secpos-> %i secremain-> %i\n", lastframepos, lastframeremain, secpos, secremain);
       return SONG_TIME;
     }
     else
     {
       // Unknown message - probably an error or noise...
       c2 = c1 ? strtok(NULL, "") : NULL;
       log_printf(LOG_DEBUG, "unknown message ; \" %s %s \"\n", c1, c2);
       return PLAYER_ERROR;
     }
     return -1;
   }