#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;
}