The following code takes the name of the plugin to load and
a pointer to the MvPluginCtrl_t structure and then tries to open the plugin.
When the plugin is opened,
MvInit()
is called to obtain the function
pointers to command(), terminate(), and get_item().
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;
pctrl->APIversion = MV_API_VERSION;
pctrl->MinorVersion = MV_MINOR_API_VERSION;
pctrl->name = path;
if( ( init = dlsym( dll, "MvInit" ) ) != NULL )
{
if( init( pctrl ) == 0 ) // calling plugin init function
{
// MvInit succeeded
pctrl->dll_handle = dll;
// Increment count of loaded plugins
atomic_add_value( &g_plugin_count, 1 );
return 0;
}
else
{
dprintf((1, "LoadDll(): MvInit() in \"%s\" failed\n", name ));
}
}
else
{
dprintf((1, "LoadDll(): MvInit not found in \"%s\" (%s)\n", name, dlerror() ));
}
pctrl->name = NULL;
dlclose( dll );
}
else
{
dprintf((1, " LoadDll(): dlopen(\"%s\") failed (%s)\n", name, dlerror() ));
}
free( path );
}
else
{
dprintf((1, "LoadDll(): no memory\n" ));
}
return -1;
}
To unload a plugin, you send the plugin a stop
(CMD_PLUGIN_STOP)
and a close command
(CMD_PLUGIN_CLOSE),
followed by a call to the terminate() function pointer.
After this, you can safely dlclose() the plugin.
// phplay's plugin termination structure.
typedef struct {
pthread_mutex_t mutex; // The mutex to block on before closing the dll
pthread_attr_t attr;
plugin_ctrl_t *pctrl; // plugin control structure
} plugin_terminate_t;
// workproc to invoke phplay's terminate_plugin() function
int invoke_terminate_method( void *data )
{
plugin_ctrl_t *pc = (plugin_ctrl_t *) data;
terminate_plugin( pc, (pc == (plugin_ctrl_t *) mplayer.pctrl) ? PLG_F_BASEINIT|PLG_F_BASECLEAN : 0 );
return Pt_END;
}
// dlclose-thread startup function
void *dlclose_plugin( void *data )
{
plugin_terminate_t *pt = (plugin_terminate_t *) data;
unsigned u;
if ( NULL == data )
return NULL;
pthread_mutex_lock( &pt->mutex );
pthread_mutex_unlock( &pt->mutex );
pthread_mutex_destroy( &pt->mutex );
// Permit the vcb (which caused this thread to be
// created) to safely return and the plugin to
// free the plugin control structure.
sched_yield();
delay( 50 );
dlclose( pt->pctrl->mv.dll_handle );
u = atomic_sub_value( &g_plugin_count, 1 );
free( (void *) pt->pctrl->mv.name );
free( (void *) pt->pctrl ); // NB: This frees the plugin_ctrl_t structure allocated by load_plugin()
free( data );
return NULL;
}
// pc - phplay's plugin control structure (different from MvPluginCtrl_t)
//
// flags
// PLG_F_DEAD - the plugin is known to be in the MV_DEAD state.
// PLG_F_BASECLEAN - clean up the base window if the plugin still controlls it
// PLG_F_BASEINIT - perform more base-window and playlist reinitializations
//
void terminate_plugin( plugin_ctrl_t *pc, int flags )
{
PtArg_t arg;
plugin_terminate_t *pt = NULL;
int ri=EINVAL, rl=EINVAL, rc=EINVAL, klozing=0;
if ( NULL == pc )
return;
if ( PLG_F_DEAD & flags )
{ // The plugin is dead. Unload it.
if ( !(PLG_F_DEAD & pc->flags) )
{
klozing = 1;
pc->flags |= PLG_F_DEAD|PLG_F_TERMINATE;
if ( pc->video_wgt )
{ // destroy the video window if necessary
PtDestroyWidget( pc->video_wgt );
mplayer.playback_parms.video_wgt = NULL;
pc->video_wgt = NULL;
}
if ( pt = (plugin_terminate_t *) calloc( 1, sizeof( plugin_terminate_t ) ) )
pt->pctrl = pc;
if ( pt &&
EOK == pthread_attr_init( &pt->attr ) &&
EOK == pthread_attr_setdetachstate( &pt->attr, PTHREAD_CREATE_DETACHED ) &&
EOK == (ri = pthread_mutex_init( &pt->mutex, NULL )) &&
EOK == (rl = pthread_mutex_lock( &pt->mutex ))
)
rc = pthread_create( NULL, &pt->attr, dlclose_plugin, (void *) pt ))
}
}
else if ( !(PLG_F_TERMINATE & pc->flags ) )
{ // Terminate the plugin.
MvCommandData_t cmdData;
memset( &cmdData, 0x0, sizeof( MvCommandData_t ) );
// Send stop command
mplayer.playIsSet = FALSE;
mplayer.played = FALSE;
cmdData.pluginCtrl = &pc->mv;
cmdData.cmdType = CMD_PLUGIN_STOP;
cmdData.which = MVP_NONE;
pc->mv.calls->command( &cmdData );
// Send close command
cmdData.pluginCtrl = &pc->mv;
cmdData.cmdType = CMD_PLUGIN_CLOSE;
cmdData.which = MVP_NONE;
pc->mv.calls->command( &cmdData );
// Invoke terminate method
pc->flags |= PLG_F_TERMINATE;
pc->mv.calls->terminate( (MvPluginCtrl_t *) pc );
} // end else if
//
// Clean up the base window if the plugin was controlling it.
//
if ( pc == mplayer.pctrl && (flags & PLG_F_BASECLEAN) && !(pc->flags & PLG_F_BASECLEAN))
{
pc->flags |= PLG_F_BASECLEAN;
mplayer.pctrl = NULL;
mplayer.pluginIsLoaded = FALSE;
mplayer.playIsSet = (mplayer.bStartupOnPlay) ? FALSE : TRUE;
// change mplayer icon
mplayer.iconImage = ApGetImageRes(mplayer.dbase,"mediaPane");
PtSetResource( ABW_icon_pane, Pt_ARG_LABEL_IMAGE, mplayer.iconImage, 0 );
// init ring buffer state info
PtSetResource( ABW_statusBar, Pt_ARG_TEXT_STRING, "", 0 );
HideClientArea( 0 );
InitAudio( FALSE );
// unlock Advance Dialog widgets if dialog box is displayed
if( mplayer.bAdvanceDlg )
OnAdvanceDlg(NULL,NULL,NULL);
// Reinitialize if necessary
if ( flags & PLG_F_BASEINIT )
{
mplayer.played = FALSE;
ControllerDeleteAll();
SetStopEjectButton( FALSE );
SetPlayPauseButton( TRUE );
// reset cursor position
PtSetResource( ABW_curpos, Pt_ARG_GAUGE_VALUE, 0, 0 );
// reset base Info Label
PtSetResource( ABW_baseInfo, Pt_ARG_TEXT_STRING, "Photon Media Player", 0 );
PtSetResource( ABW_playlistEditorDlg, Pt_ARG_WINDOW_TITLE, PL_TXT_TITLE, 0 );
}
}
// Handle the case where a dlclose thread may have been created.
if ( klozing )
{
if ( EOK == rl ) // mutex was locked
pthread_mutex_unlock( &pt->mutex ); // Wake up the dlclose thread.
if ( EOK == rc ) // dlclose thread was created
return; // The dlclose-thread will destroy the mutex and free the termination structure.
// Failed to create dlclose thread.
// Clean up as much as possible.
if ( EOK == ri ) // mutex was initialized
pthread_mutex_destroy( &pt->mutex );
free( (void *) pt->pctrl->mv.name );
free( (void *) pt->pctrl ); // NB: This frees the plugin_ctrl_t structure allocated by load_plugin()
free( pt );
}
}