[Previous] [Contents] [Next]

Caution: This version of this document is no longer maintained. For the latest documentation, see http://www.qnx.com/developers/docs.

Appendix: A playAudioCd.c Example

#include <stdio.h>
#include <stdlib.h>
#include <string.h>	
#include <ncurses.h>
#include <pthread.h>
#include <dlfcn.h>
#include <photon/Mv.h>
#include <sys/asoundlib.h>
#include <sys/ioctl.h>
#include <errno.h>

typedef struct Item
{
  char* url;
  char* title;
  int   selectionIndex;
} Item_t;

typedef struct Playlist
{
  Item_t         *items;         // array  of play items
  unsigned short itemCount;      // number of items in the playlist
  unsigned short selectionCount; // number of selected items in the playlist
  unsigned short activeItem;     // active playing track
} Playlist_t;

typedef struct tag
{
  pthread_mutex_t    mutex;
  pthread_cond_t     cond;
  int                wakeUp;
  pthread_t          userInputThread_id;
  Playlist_t         playlist;
  MvPluginState_t    lastState;
  enum MvMediaFlags  mediaType; // AUDIO,VIDEO,SEEKABLE,SPEEDABLE,REMOVABLE,PLAYLIST,MULTITRACT
  MvTime_t           duration;
  int                endOfStream;
  char               *userInput;
} player_t;
player_t player;

int OpenUrl( MvPluginCtrl_t *pctrl, int index )
{
  if( index >= 0 && index < player.playlist.itemCount )
  {
   // open the url
   MvCommandData_t    cmdData        = {0};
   MvPlaybackParams_t playback_parms = {0};
   cmdData.pluginCtrl                           = pctrl;
   cmdData.cmdType                              = CMD_PLUGIN_OPEN_URLS;
   cmdData.which                                = MVP_DELTA;
   cmdData.urls                                 = (char**)& player.playlist.items[index].url;
   cmdData.nodeString                           = getenv("HOSTNAME");
   cmdData.displayString                        = getenv("PHOTON");
   cmdData.param                                = &playback_parms;
   playback_parms.delta                         = 1;
   pctrl->calls->command( &cmdData );
   player.playlist.activeItem = index;
   fprintf(stderr,"Playing %s\n",player.playlist.items[index].title);
   return TRUE;
  }
  return FALSE;
}	
		

int GetPluginList( MvPluginCtrl_t *pctrl )
{
  MvMediaInfo_t const *item = NULL;
  int                 index = 0;
  int                 count = player.playlist.itemCount;
  Item_t              *mem  = malloc( count * sizeof( Item_t ) );

  if( pctrl  && mem )
  {
   player.playlist.items = mem;
   fprintf(stderr,"PLUGIN PLAYLIST:\n");
   for( index = 0; index < count; index++ )
   {
    item = pctrl->calls->get_item( pctrl, MV_MEDIA_INFO, index );
    if( item )
    {
     // make sure this plugin item is a url and not general playlist info
     if( (item->type & MV_MEDIA_PLAYLIST) == 0 ) 
     {
      player.playlist.items[index].url            = strdup( item->url );
      player.playlist.items[index].title          = strdup( item->title );
      player.playlist.items[index].selectionIndex = ++player.playlist.selectionCount;
      fprintf(stderr,"%2d URL = %20s  TITLE = %s\n",index + 1,item->url,item->title);
     }
    }
   }
   return TRUE;
  }
  return FALSE;	
}

void DeletePluginList()
{
  int index;
  for( index = 0; index < player.playlist.itemCount; index++ )
  {
    free( player.playlist.items[index].url );
    free( player.playlist.items[index].title );
  }
  free( player.playlist.items );
  player.playlist.items          = NULL;
  player.playlist.itemCount      = 0;
  player.playlist.selectionCount = 0;
  player.playlist.activeItem     = 0;
}

int LoadDll( const char *name, MvPluginCtrl_t *pctrl )
{
  char *path;
  if( ( path = strdup(name) ) != NULL )
  {
   void *dll;
   if( ( dll = dlopen( path, RTLD_LOCAL ) ) != NULL )
   {
    MvInitF_t *init;
    printf( "Loaded DLL %p '%s'\n", dll, name );
    pctrl->APIversion = MV_API_VERSION;
    pctrl->name = path;

    if( ( init = dlsym( dll, "MvInit" ) ) != NULL )
    {
     if(!MvGetMpSetting( &pctrl->setup) )
     {
      MvGetMpDefaultSetting( &pctrl->setup);
     }

     pctrl->setup.audio.mixerDevice = 1; // using CD_IN audiocard input

     if( init( pctrl ) == 0 ) // calling plugin init function
     {
      printf( "MvInit() succeeded\n" );
      pctrl->dll_handle = dll;
      return 0;
     }
     else
     {
      fprintf( stderr, "LoadDll(): MvInit() in \"%s\" failed\n", name );
     }
    }
    else
    {
     fprintf( stderr, "LoadDll(): MvInit not found in \"%s\" (%s)\n", name, dlerror() );
    }

    pctrl->name = NULL;
    dlclose( dll );
   }
   else
   {
    fprintf( stderr, " LoadDll(): dlopen(\"%s\") failed (%s)\n", name, dlerror() );
   }

   free( path );
  }
  else
  {
    fprintf( stderr, "LoadDll(): no memory\n" );
  }

  return -1;
}

// This thread gets user input from the keyboard
// and wakes up main().
static void* UserInputThread( void *arg )
{
  char buffer[4];
  //while( (c = getchar()) != EOF )
  while( fgets( buffer, 4, stdin ) != NULL )
  {
   fprintf(stderr,"In %s UserInputThread() You pressed %s\n",__FILE__,buffer);
   pthread_mutex_lock( &player.mutex );
   player.userInput = buffer;
   player.wakeUp = TRUE;
   //wake up main thread
   pthread_cond_signal( &player.cond );
   pthread_mutex_unlock( &player.mutex );
  }
  //fprintf(stderr,"In %s UserInputThread() returning\n",__FILE__);
  return arg;
}

// This function is called when the plugin has info
// to send back to the application.
// This function is executing from within the dll thread.
static void mpcallback( MvPluginCtrl_t *pctrl, MvEventFlags_t change, MvPluginStatus_t const *status )
{
  static int  hasPlayed = 0;
  pthread_mutex_lock( &player.mutex );
  if( change & MVS_PLUGIN_STATE )
  {
   player.lastState = status->state;
   switch( status->state )
   {
    case MV_DEAD:
    fprintf(stderr,"mpcallback() STATE = MV_DEAD\n");
    player.endOfStream = TRUE;
    break;

    case MV_CLOSED:
    fprintf(stderr,"mpcallback() STATE = MV_CLOSED\n");
    player.endOfStream = TRUE;
    break;
			
    case MV_OPENING:
    fprintf(stderr,"mpcallback() STATE = MV_OPENING\n");
    break;

    case MV_STOPPED:
    fprintf(stderr,"mpcallback() STATE = MV_STOPPED\n");
    if( hasPlayed && player.playlist.activeItem == player.playlist.itemCount )
    {
     // end of playlist detected
     // issue a stop command to the audiocd plugin
     MvCommandData_t cmdData      = {0};
     cmdData.pluginCtrl           = pctrl;
     cmdData.cmdType              = CMD_PLUGIN_START;
     cmdData.which                = MVP_NONE;
     pctrl->calls->command( &cmdData );
    }
    hasPlayed = FALSE;
    break;

    case MV_PAUSED:
    fprintf(stderr,"mpcallback() STATE = MV_PAUSED\n");
    break;

    case MV_PREFETCHING:
    fprintf(stderr,"mpcallback() STATE = MV_PREFETCHING\n");
    break;

    case MV_PLAYING:
    fprintf(stderr,"mpcallback() STATE = MV_PLAYING\n");
    hasPlayed = TRUE;
    break;

    default:
    break;
   }
  }

  if( change & MVS_MEDIA ) //  media_info is valid and points to new media
  {
   if( player.lastState >= MV_STOPPED )
   { // last state was MV_STOPPED or MV_PAUSED or MV_PREFETCHING or MV_PLAYING
     const char* title           = status->media_info->title;
     player.mediaType            = status->media_info->type;
     player.duration             = status->media_info->duration;
     if( status->media_info->type & MV_MEDIA_PLAYLIST )
     { // the  plugin new media info is a playlist
       player.playlist.itemCount = status->media_info->plcount;
       GetPluginList( pctrl );
       OpenUrl( pctrl, 0 ); 
     }
   }
   else
   { // last state was MV_DEAD or MV_CLOSED or MV_OPENING

   }
  }

  if( change & MVS_ERRORMSG )
  {
   fprintf(stderr,"%s\n", status->errormsg);
   player.endOfStream = TRUE;
  }

  if( change & MVS_POSITION )
  { // position is valid
    //fprintf(stderr,"position = %d\n",status->position);
  }

  if( player.endOfStream )
  {
   // wake up main thread
   player.wakeUp = TRUE;
   pthread_cond_signal( &player.cond );
  }

  pthread_mutex_unlock( &player.mutex );
		
}

MvPluginCtrl_t* load_plugin( const char *name )
{
  MvPluginCtrl_t *pc;
  if( ( pc = malloc(sizeof(MvPluginCtrl_t)) ) != NULL )
  {
   memset( pc, 0, sizeof(MvPluginCtrl_t) );
   pc->prio = getprio( 0 );
   pc->cb   = mpcallback;
   if( LoadDll( name, pc ) == 0 )
   {
     // InitAudio( &audio);
   }
   else
   {
    free( pc );
    pc = NULL;
   }
  }
  return pc;
}


int ChangeTrack( MvPluginCtrl_t *pctrl, int delta )
{
  int track = delta - 1; // first track start at zero in playlist
  if(  track >= 0 && track < player.playlist.itemCount  )
  {
   MvCommandData_t cmdData              = {0};
   MvPlaybackParams_t playback_parms    = {0};
   player.playlist.activeItem           = track;
   if( pctrl && player.mediaType & MV_MEDIA_MULTITRACK )
   {
    cmdData.pluginCtrl                  = pctrl;
    cmdData.cmdType                     = CMD_PLUGIN_SEEK_RELATIVE;
    cmdData.which                       = MVP_SEEK_UNIT | MVP_DELTA;
    cmdData.param                       = &playback_parms;
    playback_parms.seekUnit             = MVS_TRACK;
    playback_parms.delta                = track;
    pctrl->calls->command( &cmdData );
   }
   else
   {
    // close plugin
    cmdData.cmdType                   = CMD_PLUGIN_CLOSE;
    cmdData.which                     = MVP_NONE;
    pctrl->calls->command( &cmdData );
    // open the url
    cmdData.pluginCtrl                = pctrl;
    cmdData.cmdType                   = CMD_PLUGIN_OPEN_URLS;
    cmdData.which                     = MVP_DELTA;
    cmdData.urls                      = (char**)&player.playlist.items[track].url;
    cmdData.nodeString                = getenv("HOSTNAME");
    cmdData.displayString             = getenv("PHOTON");
    cmdData.param                     = &playback_parms;
    playback_parms.delta              = 1;
    pctrl->calls->command( &cmdData );
   }
   // restart the plugin
   cmdData.cmdType                 = CMD_PLUGIN_START;
   cmdData.which                   = MVP_NONE;
   pctrl->calls->command( &cmdData );
   fprintf(stderr,"Playing %s\n",player.playlist.items[track].title);
   return TRUE;
  }
  else
  {
   fprintf(stderr,"Unable to go to track %d  ( range = 1 to %d )\n", track + 1, player.playlist.itemCount );
  } 
  return FALSE;
}

// This function is called from the main() and
// sends the user input translated command to the plugin dll.
void SendCommand( MvPluginCtrl_t *pctrl, char *input )
{
  MvCommandData_t cmdData = {0};
  cmdData.pluginCtrl      = pctrl;

  if( !input )
  {
   return;
  }

  switch( input[0] )
  {
   case 'p':
   case 'P':
   // pause/play command
   if( player.lastState == MV_PAUSED ||  player.lastState == MV_STOPPED )
   {
    cmdData.cmdType = CMD_PLUGIN_START;
    cmdData.which = MVP_NONE;
    pctrl->calls->command( &cmdData );
   }
   else
   {
    cmdData.cmdType = CMD_PLUGIN_PAUSE;
    cmdData.which = MVP_NONE;
    pctrl->calls->command( &cmdData );
   }
   break;

   case 'q':
   case 'Q':
   // quit command
   cmdData.cmdType = CMD_PLUGIN_CLOSE;
   cmdData.which = MVP_NONE;
   pctrl->calls->command( &cmdData );
   pctrl->calls->terminate( pctrl );
   break;

   case 's':
   case 'S':
   // stop command
   cmdData.cmdType = CMD_PLUGIN_STOP;
   cmdData.which = MVP_NONE;
   pctrl->calls->command( &cmdData );
   break;

   case 'b':
   case 'B':
   // go back command
   ChangeTrack( pctrl, player.playlist.activeItem  );
   break;

   case 'n':
   case 'N':
   // go next command
   ChangeTrack( pctrl, player.playlist.activeItem + 2 );
   break;

   default:
   if( isdigit( input[0] ) )
   {	
    int track;
    input[1] = ( isdigit(input[1]) ) ? input[1]: 0;
    input[2] = 0;
    track = atoi( input );
    ChangeTrack( pctrl, track );
   }
   break;
  }
}


int main( int argc, char *argv[] )
{
  MvPluginCtrl_t *pctrl;
  char* file;
  pthread_cond_init( &player.cond, NULL);
  pthread_mutex_init( &player.mutex, NULL );
  if(argc < 2)
  {
   printf("Usage: playAudioCd /fs/cdXX\n");
   exit(1);
  }
  else
  {
   file = argv[argc -1];
   fprintf(stderr,"file = %s\n",file);

   if( !strstr( file, "file://" ))
   {
    // append "file://" to string
    file = alloca( strlen( file) + 8 );
    sprintf( file,"%s%s","file://",argv[argc -1] );
   }
  }
	
  pctrl = load_plugin( "audiocd_noph.so");
  if( pctrl )
  {
   // success
   // send open_url command to plugin
   MvCommandData_t    cmdData        = {0};
   MvPlaybackParams_t playback_parms = {0};

   cmdData.pluginCtrl           = pctrl;
   cmdData.cmdType              = CMD_PLUGIN_OPEN_URLS;
   cmdData.which                = MVP_DELTA;
   cmdData.nodeString           = getenv("HOSTNAME");
   cmdData.displayString        = getenv("PHOTON");
   cmdData.urls                 = (char**) &file;
   cmdData.param                = &playback_parms;
   playback_parms.delta         = 1;
   pctrl->calls->command( &cmdData );
		
   sleep(1);
   // send start command to plugin
   cmdData.cmdType              = CMD_PLUGIN_START;
   cmdData.which                = MVP_NONE;
   pctrl->calls->command( &cmdData );	
  }

  // create the user input thread	
  pthread_create( &player.userInputThread_id, NULL, UserInputThread, NULL ) ;
	
  // main loop
  while( !player.wakeUp )
  {
   pthread_mutex_lock( &player.mutex );
   pthread_cond_wait( &player.cond, &player.mutex );

   if( player.endOfStream )
   {
    break;
   }
		
   if( player.userInput )
   {
    player.wakeUp = 0;
    SendCommand( pctrl, player.userInput );
    player.userInput = 0;
   }
   pthread_mutex_unlock( &player.mutex );
  }

  dlclose( pctrl->dll_handle );
  pthread_mutex_destroy( &player.mutex );
  pthread_cond_destroy( &player.cond );
  DeletePluginList();
  return 0;
}

[Previous] [Contents] [Next]