core/AmPlugIn.cpp
a2a334f1
 /*
  * $Id: AmPlugIn.cpp,v 1.10.2.4 2005/08/31 13:54:29 rco Exp $
  *
  * Copyright (C) 2002-2003 Fhg Fokus
  *
  * This file is part of sems, a free SIP media server.
  *
  * sems 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
  *
  * For a license to use the ser software under conditions
  * other than those described here, or to purchase support for this
  * software, please contact iptel.org by e-mail at the following addresses:
  *    info@iptel.org
  *
  * sems is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License 
  * along with this program; if not, write to the Free Software 
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include "AmPlugIn.h"
 #include "AmConfig.h"
 #include "AmApi.h"
 
 #include "amci/amci.h"
 #include "amci/codecs.h"
 #include "log.h"
 
 #include <sys/types.h>
 #include <dirent.h>
 #include <dlfcn.h>
 #include <string.h>
 #include <errno.h>
 
 amci_codec_t _codec_pcm16 = { 
     CODEC_PCM16,
     2,
     NULL,
     NULL,
     NULL,
     NULL 
 };
 
 amci_codec_t _codec_tevent = { 
     CODEC_TELEPHONE_EVENT,
     1,
     NULL,
     NULL,
     NULL,
     NULL 
 };
 
 amci_payload_t _payload_tevent = { 
     -1,
     "telephone-event",
     8000,
     -1,
     CODEC_TELEPHONE_EVENT,
     -1 
 };
 
 AmPlugIn* AmPlugIn::_instance=0;
 
 AmPlugIn::AmPlugIn()
     : dynamic_pl(96) // range: 96->127, see RFC 1890
 {
     DBG("adding built-in codecs...\n");
     addCodec(&_codec_pcm16);
     addCodec(&_codec_tevent);
     addPayload(&_payload_tevent);
 }
 
 AmPlugIn::~AmPlugIn()
 {
     for(vector<void*>::iterator it=dlls.begin();it!=dlls.end();++it)
 	dlclose(*it);
 }
 
 AmPlugIn* AmPlugIn::instance()
 {
     if(!_instance)
 	_instance = new AmPlugIn();
 
     return _instance;
 }
 
 
37abd537
 int AmPlugIn::load(const string& directory)
a2a334f1
 {
     int err=0;
     struct dirent* entry;
     DIR* dir = opendir(directory.c_str());
 
     if(!dir){
 	ERROR("plug-ins loader (%s): %s\n",directory.c_str(),strerror(errno));
 	return -1;
     }
 
 
     while( ((entry = readdir(dir)) != NULL) && (err == 0) ){
 
 	string plugin_file = directory + "/" + string(entry->d_name);
 
 	if( plugin_file.find(".so",plugin_file.length()-3) == string::npos ){
 	    continue;
 	}
 
 	DBG("loading %s ...\n",plugin_file.c_str());
37abd537
 	if( (err = loadPlugIn(plugin_file)) < 0 )
a2a334f1
 	    ERROR("while loading plug-in '%s'\n",plugin_file.c_str());
     }
 
37abd537
     for(map<string,AmSessionEventHandlerFactory*>::iterator it = name2seh.begin();
 	it != name2seh.end(); it++){
 	err = it->second->onLoad();
 	if(err)
 	    break;
     }
14c69b16
 
     for(map<string,AmDynInvokeFactory*>::iterator it = name2di.begin();
 	it != name2di.end(); it++){
 	err = it->second->onLoad();
 	if(err)
 	    break;
     }
37abd537
     
     map<string,AmSessionFactory*> apps(name2app);
     for(map<string,AmSessionFactory*>::iterator it = apps.begin();
 	it != apps.end(); it++){
 
 	err = it->second->onLoad();
 	if(err)
 	    break;
     }
 
a2a334f1
     closedir(dir);
     return err;
 }
 
37abd537
 int AmPlugIn::loadPlugIn(const string& file)
a2a334f1
 {
37abd537
     void* h_dl = dlopen(file.c_str(),RTLD_NOW | RTLD_GLOBAL);
a2a334f1
 
     if(!h_dl){
37abd537
 	ERROR("AmPlugIn::loadPlugIn: %s: %s\n",file.c_str(),dlerror());
a2a334f1
 	return -1;
     }
 
37abd537
     FactoryCreate fc = NULL;
     amci_exports_t* exports = (amci_exports_t*)dlsym(h_dl,"amci_exports");
 
14c69b16
     bool has_sym=false;
37abd537
     if(exports){
  	if(loadAudioPlugIn(exports))
 	    goto error;
 	goto end;
     }
 
     if((fc = (FactoryCreate)dlsym(h_dl,FACTORY_SESSION_EXPORT_STR)) != NULL){
 	if(loadAppPlugIn((AmPluginFactory*)fc()))
 	    goto error;
14c69b16
 	has_sym=true;
37abd537
     }
14c69b16
     if((fc = (FactoryCreate)dlsym(h_dl,FACTORY_SESSION_EVENT_HANDLER_EXPORT_STR)) != NULL){
37abd537
 	if(loadSehPlugIn((AmPluginFactory*)fc()))
 	    goto error;
14c69b16
 	has_sym=true;
     }
     if((fc = (FactoryCreate)dlsym(h_dl,FACTORY_PLUGIN_CLASS_EXPORT_STR)) != NULL){
 	if(loadDiPlugIn((AmPluginFactory*)fc()))
 	    goto error;
 	has_sym=true;
37abd537
     }
14c69b16
 
     if(!has_sym){
37abd537
 	ERROR("Plugin type could not be detected (%s)(%s)\n",file.c_str(),dlerror());
 	goto error;
a2a334f1
     }
 
37abd537
  end:
a2a334f1
     dlls.push_back(h_dl);
     return 0;
 
  error:
     dlclose(h_dl);
     return -1;
 }
 
 
 amci_inoutfmt_t* AmPlugIn::fileFormat(const string& fmt_name, const string& ext)
 {
     if(!fmt_name.empty()){
 
 	map<string,amci_inoutfmt_t*>::iterator it = file_formats.find(fmt_name);
 	if ((it != file_formats.end()) &&
 	    (ext.empty() || (ext == it->second->ext)))
 	    return it->second;
     }
     else if(!ext.empty()){
 	
 	map<string,amci_inoutfmt_t*>::iterator it = file_formats.begin();
 	for(;it != file_formats.end();++it){
 	    if(ext == it->second->ext)
 		return it->second;
 	}
     }
 
     return 0;
 }
 
 amci_codec_t* AmPlugIn::codec(int id)
 {
     map<int,amci_codec_t*>::iterator it = codecs.find(id);
     if(it != codecs.end())
 	return it->second;
 
     return 0;
 }
 
 amci_payload_t*  AmPlugIn::payload(int payload_id)
 {
     map<int,amci_payload_t*>::iterator it = payloads.find(payload_id);
     if(it != payloads.end())
 	return it->second;
 
     return 0;
 }
 
 amci_subtype_t* AmPlugIn::subtype(amci_inoutfmt_t* iofmt, int subtype)
 {
     if(!iofmt)
 	return 0;
     
     amci_subtype_t* st = iofmt->subtypes;
     if(subtype<0) // default subtype wanted
 	return st;
 
     for(;;st++){
 	if(!st || st->type<0) break;
 	if(st->type == subtype)
 	    return st;
     }
 
     return 0;
 }
 
37abd537
 AmSessionFactory* AmPlugIn::getFactory4App(const string& app_name)
a2a334f1
 {
37abd537
   map<string,AmSessionFactory*>::iterator it = name2app.find(app_name);
a2a334f1
   if(it != name2app.end())
     return it->second;
   return 0;
 }
 
37abd537
 AmSessionEventHandlerFactory* AmPlugIn::getFactory4Seh(const string& name)
 {
   map<string,AmSessionEventHandlerFactory*>::iterator it = name2seh.find(name);
   if(it != name2seh.end())
     return it->second;
   return 0;
 }
 
14c69b16
 AmDynInvokeFactory* AmPlugIn::getFactory4Di(const string& name)
 {
   map<string,AmDynInvokeFactory*>::iterator it = name2di.find(name);
   if(it != name2di.end())
     return it->second;
   return 0;
 }
 
a2a334f1
 int AmPlugIn::loadAudioPlugIn(amci_exports_t* exports)
 {
     if(!exports){
         ERROR("audio plug-in doesn't contain any exports !\n");
         return -1;
     }
 
     for( amci_codec_t* c=exports->codecs; 
 	 c->id>=0; c++ ){
 
 	if(addCodec(c))
 	    goto error;
     }
 
     for( amci_payload_t* p=exports->payloads; 
 	 p->name; p++ ){
 
 	if(addPayload(p))
 	    goto error;
     }
 
     for(amci_inoutfmt_t* f = exports->file_formats; 
 	f->name; f++ ){
 
 	if(addFileFormat(f))
 	    goto error;
     }
     
     return 0;
 
  error:
     return -1;
 }
 
 
37abd537
 int AmPlugIn::loadAppPlugIn(AmPluginFactory* f)
a2a334f1
 {
37abd537
     AmSessionFactory* sf = dynamic_cast<AmSessionFactory*>(f);
a2a334f1
     if(!sf){
37abd537
         ERROR("invalid application plug-in!\n");
a2a334f1
         goto error;
     }
 
37abd537
     if(name2app.find(sf->getName()) != name2app.end()){
         ERROR("application '%s' already loaded !\n",sf->getName().c_str());
a2a334f1
         goto error;
     }
       
14c69b16
     name2app.insert(std::make_pair(sf->getName(),sf));
     DBG("application '%s' loaded.\n",sf->getName().c_str());
a2a334f1
 
37abd537
     return 0;
a2a334f1
 
  error:
     return -1;
 }
 
37abd537
 int AmPlugIn::loadSehPlugIn(AmPluginFactory* f)
 {
     AmSessionEventHandlerFactory* sf = dynamic_cast<AmSessionEventHandlerFactory*>(f);
     if(!sf){
         ERROR("invalid session component plug-in!\n");
         goto error;
     }
 
     if(name2seh.find(sf->getName()) != name2seh.end()){
         ERROR("session component '%s' already loaded !\n",sf->getName().c_str());
         goto error;
     }
       
14c69b16
     name2seh.insert(std::make_pair(sf->getName(),sf));
     DBG("session component '%s' loaded.\n",sf->getName().c_str());
 
     return 0;
 
  error:
     return -1;
 }
 
 int AmPlugIn::loadDiPlugIn(AmPluginFactory* f)
 {
     AmDynInvokeFactory* sf = dynamic_cast<AmDynInvokeFactory*>(f);
     if(!sf){
         ERROR("invalid component plug-in!\n");
         goto error;
     }
 
     if(name2di.find(sf->getName()) != name2di.end()){
         ERROR("component '%s' already loaded !\n",sf->getName().c_str());
         goto error;
     }
       
     name2di.insert(std::make_pair(sf->getName(),sf));
     DBG("component '%s' loaded.\n",sf->getName().c_str());
37abd537
 
     return 0;
 
  error:
     return -1;
 }
 
 
a2a334f1
 int AmPlugIn::addCodec(amci_codec_t* c)
 {
     if(codecs.find(c->id) != codecs.end()){
 	ERROR("codec id (%i) already supported\n",c->id);
 	return -1;
     }
     codecs.insert(std::make_pair(c->id,c));
     DBG("codec id %i inserted\n",c->id);
     return 0;
 }
 
 int AmPlugIn::addPayload(amci_payload_t* p)
 {
     amci_codec_t* c;
     if( !(c = codec(p->codec_id)) ){
 	ERROR("in payload '%s': codec id (%i) not supported\n",p->name,p->codec_id);
 	return -1;
     }
     if(p->payload_id != -1){
 	if(payloads.find(p->payload_id) != payloads.end()){
 	    ERROR("payload id (%i) already supported\n",p->payload_id);
 	    return -1;
 	}
 	payloads.insert(std::make_pair(p->payload_id,p));
 	DBG("payload '%s'inserted with id %i \n",p->name,p->payload_id);
     }
     else {
 	payloads.insert(std::make_pair(dynamic_pl,p));
 	DBG("payload '%s'inserted with id %i \n",p->name,dynamic_pl);
 	dynamic_pl++;
     }
 
     return 0;
 }
 
 int AmPlugIn::addFileFormat(amci_inoutfmt_t* f)
 {
     if(file_formats.find(f->name) != file_formats.end()){
 	ERROR("file format '%s' already supported\n",f->name);
 	return -1;
     }
 
     amci_subtype_t* st = f->subtypes;
     for(; st->type >= 0; st++ ){
 
 	if( !codec(st->codec_id) ){
 	    ERROR("in '%s' subtype %i: codec id (%i) not supported\n",
 		  f->name,st->type,st->codec_id);
 	    return -1;
 	}
 
 	if (st->sample_rate < 0) {
 	    ERROR("in '%s' subtype %i: rate must be specified!"
 		  " (ubr no longer supported)\n", f->name,st->type);
 	    return -1;
 	}
 	if (st->channels < 0) {
 	    ERROR("in '%s' subtype %i: channels must be specified!"
 		  "(unspecified channel count no longer supported)\n", f->name,st->type);
 	    return -1;
 	}
 
     }
     DBG("file format %s inserted\n",f->name);
     file_formats.insert(std::make_pair(f->name,f));
 
     return 0;
 }
37abd537
 
 bool AmPlugIn::registerFactory4App(const string& app_name, AmSessionFactory* f)
 {
   map<string,AmSessionFactory*>::iterator it = name2app.find(app_name);
   if(it != name2app.end()){
       WARN("Application '%s' has already been registered and cannot be registered a second time\n",
 	   app_name.c_str());
       return false;
   }
   
   name2app.insert(make_pair(app_name,f));
   return true;
 }