Re: [GnomeMeeting-devel-list] [BETA7] the "I'm in a real hurry" release



Le lun 11/08/2003 à 17:52, PUYDT Julien a écrit :

> * INPUT->Input & OUTPUT->Output;

Oh, well... I tested the new manager, and didn't see I had forgotten one
such in the dlopen one...

Snark
/* This is an implementation of the PDeviceManager class,
   that should work on any OS providing the dlopen&dlsym calls.

   It stores the list in a custom one-linked list of plugins, that
   is almost read-only: only one function writes to it, LoadPlugin,
   and it only adds plugins at the beginning of the list.

   Once a plugin is loaded, it is for the life of the program: the list
   is never, ever, explicitly freed.

   A plugin for this manager comes in the form of (those are AND):
   * shared lib;
   * the implementation of a class derived from PVideoInputDevice and/or
   PVideoOutputDevice and/or PSoundChannel (depending on the type of plugin);
   * some extern "C"-defined functions;

   To get more specific, a typical plugin comes a a .h, declaring some classes
   as public PVideoInputDevice/PVideoOutputDevice/PSoundChannel, and a .cxx,
   that defines the classes, and uses the following macros:
   * DECLARE_PLUGIN(NAME, TYPE): with the name of the plugin (example: "V4L"),
   and its type (example: PDeviceManager::VIDEOIN) (mandatory);
   * DECLARE_PLUGIN_VIDEOINPUT(CLASS): the name of the class inheriting from
   PVideoInputDevice (optional);
   * DECLARE_PLUGIN_VIDEOOUTPUT(CLASS): the name of the class inheriting from
   PVideoOutputDevice (optional);
   * DECLARE_PLUGIN_SOUNDINPUT(CLASS): the name of the class inheriting from
   PSoundChannel, that handles sound input (optional);
   * DECLARE_PLUGIN_SOUNDOUTPUT(CLASS): the name of the class inheriting from
   PSoundChannel, that handles sound output (optional);

*/

// system includes
#include <dlfcn.h>

// PWLib includes
#include <ptlib.h>
#include <ptlib/videoio.h>
#include <ptlib/vconvert.h>
#include <ptlib/pdirect.h>
#include <ptlib/plugins.h>
#include <ptbuildopts.h>

#define DRIVERNAMELEN 11 // FIXME! Only ten chars for the driver name...

struct device_plugin {
  // common to all types
  const char *name;
  int type;
  void *handle;
  struct device_plugin *next;
  // video input
  PVideoInputDevice *(*create_vidin)(void);
  PStringList (*devnames_vidin)(void);
  // video output
  PVideoOutputDevice *(*create_vidout)(void);
  PStringList (*devnames_vidout)(void);
  // sound input
  PSoundChannel *(*create_sndin)(void);
  PStringArray (*devnames_sndin)(void);
  // sound output
  PSoundChannel *(*create_sndout)(void);
  PStringArray (*devnames_sndout)(void);
};

// helper functions
static void LoadAllPlugins(); 
static PSoundChannel::Directions to_pscdir(PDeviceManager::Direction);

// plugin list variables
static struct device_plugin *plugins_list;  
static PMutex mutex;

PDeviceManager::PDeviceManager()
{
  PTRACE(3, "PDeviceManager::PDeviceManager called: useless...");
  mutex.Wait();
  if(plugins_list == NULL)
    {
      mutex.Signal();
      LoadAllPlugins();
    }
  else
    mutex.Signal();
}


PDeviceManager::~PDeviceManager()
{
  // well, you're not really supposed to create one anyway...
}

void
LoadAllPlugins()
{
  PTRACE(3, "Loading plugins...");
  if(PDeviceManager::LoadPluginDirectory((PString)SYSPLUGINDIR) == FALSE)
    {
      PTRACE(2, "Couldn't load any system plugin!");
    }
}

BOOL
PDeviceManager::LoadPluginDirectory(PDirectory plugdir)
{
  BOOL Result = FALSE;

  PTRACE(4, "Considering loading plugins in " << plugdir);
  
  if(!plugdir.Open())
    {
      PTRACE(4, "the dir isn't accessible");
      return Result; // no love, no hope
    }
  
  do
    {
      PString entry = plugdir+plugdir.GetEntryName();
      if(plugdir.IsSubDir())
	Result = LoadPluginDirectory(entry) || Result; // BEWARE of the...
      else
	Result = LoadPlugin(entry) || Result; // lazy evaluation!!!
    } 
  while(plugdir.Next());

  return Result;
  
}

BOOL
PDeviceManager::LoadPlugin(PString filename)
{
      void *handle = NULL;
      struct device_plugin *plugin = NULL;

      ostream & s = PTrace::Begin(0, "Plugin manager: ", 0);

      plugin = (struct device_plugin *)malloc(sizeof(struct device_plugin));
      if(plugin == NULL)
	{
	  s << "PDeviceManager::LoadPlugin: ENOMEM\n" << PTrace::End(s);
	  return FALSE;
	}

      s << "trying to load " << filename << '\n';

      // first, we try to get a handle on the plugin
      handle=dlopen(filename, RTLD_NOW);
      if(handle == NULL)
	{
	  s << "dlerror=" << dlerror() << "\n" << PTrace::End(s);
	  free(plugin);
	  return FALSE;
	}
      plugin->handle = handle;

      // second, we try to get its name
      char *(*getname)(void);
      getname = (char *(*)())dlsym(handle, "plugin_getname");
      if(getname == NULL)
	{
	  s << "dlerror=" << dlerror() << "\n";
	  goto wrong;
	}
      plugin->name = (*getname)();

      // third, we try to get its type
      int (*gettype)(void);
      gettype = (int (*)())dlsym(handle, "plugin_gettype");
      if(gettype == NULL)
	{
	  s << "dlerror=" << dlerror() << "\n";
	  goto wrong;
	}
      plugin->type = (*gettype)();

      // the rest depends on the type of plugin, so we do it step-by-step:
      // does it provide a video input?
      if(plugin->type & VIDIN)
	{
	  PVideoInputDevice *(*create)();
	  PStringList (*devnames)();
	  create = (PVideoInputDevice *(*)())dlsym(handle,
						   "plugin_create_vidin");
	  devnames = (PStringList (*)())dlsym(handle,
					      "plugin_getdevices_vidin");
	  if((create == NULL)
	     || (devnames == NULL))
	    {
	      s << "dlerror=" << dlerror() << "\n";
	      goto wrong;
	    }
	  
	  plugin->create_vidin = create;
	  plugin->devnames_vidin = devnames;
	}
      else
	{
	  plugin->create_vidin = NULL;
	  plugin->devnames_vidin = NULL;
	}
      // does it provide a video output?
      if(plugin->type & VIDOUT)
	{
	  PVideoOutputDevice *(*create)();
	  PStringList (*devnames)();
	  create = (PVideoOutputDevice *(*)())dlsym(handle,
						   "plugin_create_vidout");
	  devnames = (PStringList (*)())dlsym(handle,
					      "plugin_getdevices_vidout");
	  if((create == NULL)
	     || (devnames == NULL))
	    {
	      s << "dlerror=" << dlerror() << "\n";
	      goto wrong;
	    }
	  
	  plugin->create_vidout = create;
	  plugin->devnames_vidout = devnames;
	}
      else
	{
	  plugin->create_vidout = NULL;
	  plugin->devnames_vidout = NULL;
	}
      // does it provide a sound input?
      if(plugin->type & SNDIN)
	{
	  PSoundChannel *(*create)();
	  PStringArray (*devnames)();
	  create = (PSoundChannel *(*)())dlsym(handle,
						   "plugin_create_sndin");
	  devnames = (PStringArray (*)())dlsym(handle,
					      "plugin_getdevices_sndin");
	  if((create == NULL)
	     || (devnames == NULL))
	    {
	      s << "dlerror=" << dlerror() << "\n";
	      goto wrong;
	    }
	  
	  plugin->create_sndin = create;
	  plugin->devnames_sndin = devnames;
	}
      else
	{
	  plugin->create_sndin = NULL;
	  plugin->devnames_sndin = NULL;
	}
	
      // does it provide a sound output?
      if(plugin->type & SNDOUT)
	{
	  PSoundChannel *(*create)();
	  PStringArray (*devnames)();
	  create = (PSoundChannel *(*)())dlsym(handle,
						   "plugin_create_sndout");
	  devnames = (PStringArray (*)())dlsym(handle,
					      "plugin_getdevices_sndout");
	  if((create == NULL)
	     || (devnames == NULL))
	    {
	      s << "dlerror=" << dlerror() << "\n";
	      goto wrong;
	    }
	  
	  plugin->create_sndout = create;
	  plugin->devnames_sndout = devnames;
	}
      else
	{
	  plugin->create_sndout = NULL;
	  plugin->devnames_sndout = NULL;
	}
   
      // finally, since we got all we wanted, we just add it to the list!
      mutex.Wait();
      plugin->next = plugins_list;
      plugins_list = plugin;
      mutex.Signal();
 
      s << "success";
      PTrace::End(s);
     
      return TRUE;
 wrong: 
      free(plugin);
      (void)dlclose(handle);
      s << PTrace::End(s);
      return FALSE;
}

PVideoInputDevice*
PDeviceManager::GetVideoInputDevice(PString name)
{ 
  mutex.Wait();
  struct device_plugin *ptr=plugins_list;
  mutex.Signal();
  for(;
      ptr != NULL;
      ptr = ptr->next)
    {
      if((ptr->type & VIDIN)  && strcmp(name, ptr->name) == 0)
	return ptr->create_vidin();
    }

  return NULL;
}

PVideoOutputDevice*
PDeviceManager::GetVideoOutputDevice(PString name)
{ 
  mutex.Wait();
  struct device_plugin *ptr=plugins_list;
  mutex.Signal();
  for(;
      ptr != NULL;
      ptr = ptr->next)
    {
      if((ptr->type & VIDOUT)  && strcmp(name, ptr->name) == 0)
	return ptr->create_vidout();
    }

  return NULL;
}

PVideoInputDevice*
PDeviceManager::GetOpenedVideoInputDevice(PString devName, BOOL flag)
{
  char driver[DRIVERNAMELEN];

  mutex.Wait();
  if(plugins_list == NULL)
    {
      mutex.Signal();
      LoadAllPlugins();
    }
  else
    mutex.Signal();


  if(sscanf(devName, "%s", driver) == 1)
    { // looks like a nice devName
      PString filename = devName.Right(strlen(devName)-strlen(driver)-1);
      // FIXME! this computation is awful!

      PVideoInputDevice *dev = GetVideoInputDevice(driver);

      if(dev != NULL && dev->Open(filename, flag))
	return dev;

      if(dev != NULL)
	delete dev;

      return NULL;
    }
  // doesn't look like a nice devName
  // (that can be a user on CLI, who wants us to open /dev/video3.14)
  
  // FIXME: there is a nicer solution to implement that,
  // but my pattern-matching abilities are quite small,
  // as the previous fixme must have proven: search through the list of
  // devices names we get from GetInputDeviceNames, and only ask to those
  // drivers that claimed they could handle devName.
  
  mutex.Wait();
  struct device_plugin *ptr=plugins_list;
  mutex.Signal();
  for(;
      ptr != NULL;
      ptr = ptr->next)
    {
      if(ptr->type & VIDIN)
	{
	  PVideoInputDevice *dev=ptr->create_vidin();
	  if(dev != NULL && dev->Open(devName, flag))
	    return dev;
	  
	  if(dev != NULL)
	    delete dev;
	}
    }
  
  return NULL;
}

PVideoOutputDevice*
PDeviceManager::GetOpenedVideoOutputDevice(PString devName, BOOL flag)
{
  char driver[DRIVERNAMELEN];

  mutex.Wait();
  if(plugins_list == NULL)
    {
      mutex.Signal();
      LoadAllPlugins();
    }
  else
    mutex.Signal();


  if(sscanf(devName, "%s", driver) == 1)
    { // looks like a nice devName
      PString filename = devName.Right(strlen(devName)-strlen(driver)-1);
      // FIXME! this computation is awful!

      PVideoOutputDevice *dev = GetVideoOutputDevice(driver);

      if(dev != NULL && dev->Open(filename, flag))
	return dev;

      if(dev != NULL)
	delete dev;
    }

  // doesn't look like a nice devName
  // (that can be a user on CLI, who wants us to open /dev/video3.14)
  
  // FIXME: there is a nicer solution to implement that,
  // but my pattern-matching abilities are quite small,
  // as the previous fixme must have proven: search through the list of
  // devices names we get from GetInputDeviceNames, and only ask to those
  // drivers that claimed they could handle devName.
  
  mutex.Wait();
  struct device_plugin *ptr=plugins_list;
  mutex.Signal();
  for(;
      ptr != NULL;
      ptr = ptr->next)
    {
      if(ptr->type & VIDOUT)
	{
	  PVideoOutputDevice *dev=ptr->create_vidout();
	  if(dev != NULL && dev->Open(devName, flag))
	    return dev;
	  
	  if(dev != NULL)
	    delete dev;
	}
    }
  
  return NULL;
}

PSoundChannel*
PDeviceManager::GetSoundDevice(PString name, Direction dir)
{ 
  mutex.Wait();
  struct device_plugin *ptr=plugins_list;
  mutex.Signal();
  for(;
      ptr != NULL;
      ptr = ptr->next)
    {
      if(dir == Input && ptr->type & SNDIN && strcmp(name, ptr->name) == 0)
	return ptr->create_sndin();
      if(dir == Output && ptr->type & SNDOUT && strcmp(name, ptr->name) == 0)
	return ptr->create_sndout();
    }

  return NULL;
}

PSoundChannel*
PDeviceManager::GetOpenedSoundDevice(PString devName,
				     Direction dir,
				     unsigned numChannels,
				     unsigned sampleRate,
				     unsigned bitsPerSample)
{
  char driver[DRIVERNAMELEN];

  mutex.Wait();
  if(plugins_list == NULL)
    {
      mutex.Signal();
      LoadAllPlugins();
    }
  else
    mutex.Signal();

  ostream & s = PTrace::Begin(0, "GetOpenedSoundDevice: ", 0);
  s << "Opening: " << devName << "\n";

  if(sscanf(devName, "%s", driver) == 1)
    { // looks like a nice devName
      PString filename = devName.Right(strlen(devName)-strlen(driver)-1);
      // FIXME! this computation is awful!

      s << "Driver: " << driver << " File: " << filename << '\n';
      PSoundChannel *dev = GetSoundDevice(driver, dir);
	
      if(dev != NULL && dev->Open(filename, to_pscdir(dir), numChannels,
				  sampleRate, bitsPerSample))
	{
	  s << "success!\n";
	  PTrace::End(s);
	  return dev;
	}
      
      if(dev != NULL)
	delete dev;
    }

  // doesn't look like a nice devName
  // (that can be a user on CLI, who wants us to open /dev/video3.14)
  
  // FIXME: there is a nicer solution to implement that,
  // but my pattern-matching abilities are quite small,
  // as the previous fixme must have proven: search through the list of
  // devices names we get from GetInputDeviceNames, and only ask to those
  // drivers that claimed they could handle devName.
  
  s << "fallback\n";
  mutex.Wait();
  struct device_plugin *ptr=plugins_list;
  mutex.Signal();
  for(;
      ptr != NULL;
      ptr = ptr->next)
    {
      PSoundChannel *dev;
      
      if(dir == Input && ptr->type & SNDIN)
	dev = ptr->create_sndin();
      else if(dir == Output && ptr->type & SNDOUT)
	dev = ptr->create_sndout();
      else
	dev = NULL;
      
      if(dev != NULL && dev->Open(devName, to_pscdir(dir), numChannels,
				  sampleRate, bitsPerSample))
	return dev;
      
      if(dev != NULL)
	delete dev;
    }
  
  return NULL;
}

PStringList
PDeviceManager::GetVideoDeviceNames(Direction dir)
{ 
  mutex.Wait();
  if(plugins_list == NULL)
    {
      mutex.Signal();
      LoadAllPlugins();
    }
  else
    mutex.Signal();

  // we get through all the plugin lists, and add the name as a prefix,
  // to be able to track down which takes care of what
  
  PStringList Result;
 
  mutex.Wait();
  struct device_plugin *ptr = plugins_list;
  mutex.Signal();
  for(;
      ptr != NULL;
      ptr=ptr->next)
    {
      PStringList tmplist;
      
      if(dir == Input && ptr->type & VIDIN)
	tmplist = ptr->devnames_vidin();
      else if(dir == Output && ptr->type & VIDOUT)
	tmplist = ptr->devnames_vidout();
	  
      for (PINDEX i = 0; i < tmplist.GetSize(); i++)
	{
	  Result.AppendString((PString)ptr->name+" "+tmplist[i]);
	}
    }
  
  return Result;
  
}

PStringArray
PDeviceManager::GetSoundDeviceNames(Direction dir)
{ 
  mutex.Wait();
  if(plugins_list == NULL)
    {
      mutex.Signal();
      LoadAllPlugins();
    }
  else
    mutex.Signal();

  // we get through all the plugin lists, and add the name as a prefix,
  // to be able to track down which takes care of what
  
  PStringList Result;
 
  mutex.Wait();
  struct device_plugin *ptr = plugins_list;
  mutex.Signal();
  for(;
      ptr != NULL;
      ptr=ptr->next)
    {
      PStringList tmplist;
      if(dir == Input && ptr->type & SNDIN)
	tmplist = PStringList::PStringList(ptr->devnames_sndin());
      else if(dir == Output && ptr->type & SNDOUT)
	tmplist = PStringList::PStringList(ptr->devnames_sndout());
      
      for (PINDEX i = 0; i < tmplist.GetSize(); i++)
	{
	  Result.AppendString((PString)ptr->name+" "+tmplist[i]);
	}
      
    }
  
  return PStringArray::PStringArray(Result);
  
}

PSoundChannel::Directions
to_pscdir(PDeviceManager::Direction dir)
{
  if(dir == PDeviceManager::Output)
    return PSoundChannel::Player;
  else
    return PSoundChannel::Recorder;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]