/* * $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 #include #include #include #include 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::iterator it=dlls.begin();it!=dlls.end();++it) dlclose(*it); } AmPlugIn* AmPlugIn::instance() { if(!_instance) _instance = new AmPlugIn(); return _instance; } int AmPlugIn::load(const string& directory) { 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()); if( (err = loadPlugIn(plugin_file)) < 0 ) ERROR("while loading plug-in '%s'\n",plugin_file.c_str()); } for(map::iterator it = name2seh.begin(); it != name2seh.end(); it++){ err = it->second->onLoad(); if(err) break; } for(map::iterator it = name2di.begin(); it != name2di.end(); it++){ err = it->second->onLoad(); if(err) break; } map apps(name2app); for(map::iterator it = apps.begin(); it != apps.end(); it++){ err = it->second->onLoad(); if(err) break; } closedir(dir); return err; } int AmPlugIn::loadPlugIn(const string& file) { void* h_dl = dlopen(file.c_str(),RTLD_NOW | RTLD_GLOBAL); if(!h_dl){ ERROR("AmPlugIn::loadPlugIn: %s: %s\n",file.c_str(),dlerror()); return -1; } FactoryCreate fc = NULL; amci_exports_t* exports = (amci_exports_t*)dlsym(h_dl,"amci_exports"); bool has_sym=false; 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; has_sym=true; } if((fc = (FactoryCreate)dlsym(h_dl,FACTORY_SESSION_EVENT_HANDLER_EXPORT_STR)) != NULL){ if(loadSehPlugIn((AmPluginFactory*)fc())) goto error; has_sym=true; } if((fc = (FactoryCreate)dlsym(h_dl,FACTORY_PLUGIN_CLASS_EXPORT_STR)) != NULL){ if(loadDiPlugIn((AmPluginFactory*)fc())) goto error; has_sym=true; } if(!has_sym){ ERROR("Plugin type could not be detected (%s)(%s)\n",file.c_str(),dlerror()); goto error; } end: 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::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::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::iterator it = codecs.find(id); if(it != codecs.end()) return it->second; return 0; } amci_payload_t* AmPlugIn::payload(int payload_id) { map::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; } AmSessionFactory* AmPlugIn::getFactory4App(const string& app_name) { map::iterator it = name2app.find(app_name); if(it != name2app.end()) return it->second; return 0; } AmSessionEventHandlerFactory* AmPlugIn::getFactory4Seh(const string& name) { map::iterator it = name2seh.find(name); if(it != name2seh.end()) return it->second; return 0; } AmDynInvokeFactory* AmPlugIn::getFactory4Di(const string& name) { map::iterator it = name2di.find(name); if(it != name2di.end()) return it->second; return 0; } 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; } int AmPlugIn::loadAppPlugIn(AmPluginFactory* f) { AmSessionFactory* sf = dynamic_cast(f); if(!sf){ ERROR("invalid application plug-in!\n"); goto error; } if(name2app.find(sf->getName()) != name2app.end()){ ERROR("application '%s' already loaded !\n",sf->getName().c_str()); goto error; } name2app.insert(std::make_pair(sf->getName(),sf)); DBG("application '%s' loaded.\n",sf->getName().c_str()); return 0; error: return -1; } int AmPlugIn::loadSehPlugIn(AmPluginFactory* f) { AmSessionEventHandlerFactory* sf = dynamic_cast(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; } 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(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()); return 0; error: return -1; } 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; } bool AmPlugIn::registerFactory4App(const string& app_name, AmSessionFactory* f) { map::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; }