checkin of the examples from the application development tutorial

git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@294 8eb893ce-cfd4-0310-b710-fb5ebe64c474
sayer/1.4-spce2.6
Stefan Sayer 20 years ago
parent 067c0571ca
commit 2ff1e991de

@ -0,0 +1,22 @@
SEMS application development tutorial examples
These are the examples from the application development tutorial
which is available from the SEMS website:
http://www.iptel.org/sems/sems_application_development_tutorial
The examples here compile fine with 'make' in their respective
directories. Depending on your python version, you may want to use
e.g. 'make PYTHON_VERSION=2.4' for the python examples.
The tutorial consists of the PDF document
(http://www.iptel.org/files/semsng-app_module_tutorial.pdf) and
the examples, and there are installation instructions on the
website.
A sample configuration set is available at
http://www.iptel.org/files/sems-tutorial-config.tgz
For the calling card example, you may want to download some sample
wav files from http://www.iptel.org/files/ccard_wav.tgz

@ -0,0 +1,14 @@
NAME=annc_service
VERSION=0.0.1
PYTHON_VERSION=2.3
LIBDIR=
COREPATH=../../../../core
IVRPATH=../../../ivr
# general python app Makefile
include ../../../ivr/Makefile.ivr_application

@ -0,0 +1,104 @@
#
# Simple implementation of (a part) of RFC4240
# announcement service.
#
# supported parameters:
# play, repeat, duration, delay
from log import *
from ivr import *
from urlparse import urlparse, urlsplit
from urllib import urlretrieve
from os import unlink
TIMEOUT_TIMER_ID = 1
DELAY_TIMER_ID = 2
class IvrDialog(IvrDialogBase):
announcement=None
filename = ""
repeat="1"
delay=0
duration=-1
play=""
delete_onbye = False
repeat_left = 0
def onSessionStart(self,hdrs):
debug("configuration: %s" % repr(config))
debug("local_uri = " + self.dialog.local_uri);
# we use urlsplit as urlparse only returns the
# parameters of the last path
params = urlsplit(self.dialog.local_uri)[2].split(";")
debug("parameters are " + str(params))
for param in params[0:len(params)]:
if (param.startswith("play=")):
self.play=param[5:len(param)]
elif (param.startswith("repeat=")):
self.repeat=param[7:len(param)]
elif (param.startswith("delay=")):
self.delay=int(param[6:len(param)])
elif (param.startswith("duration=")):
self.duration=int(param[9:len(param)])
resource = urlparse(self.play)
if (resource[0] == "http"):
self.delete_onbye = True
self.filename = urlretrieve(self.play)[0]
debug("play: "+self.play+" repeat: "+self.repeat+" delay:"+
str(self.delay)+" duration: "+str(self.duration))
self.announcement = IvrAudioFile()
self.announcement.open(self.filename,AUDIO_READ)
if (self.repeat!="forever"):
self.repeat_left=int(self.repeat)-1
else:
self.repeat_left=500 # maximum
if (int(self.duration) > 0):
self.setTimer(TIMEOUT_TIMER_ID, self.duration/1000)
self.enqueue(self.announcement, None)
def onBye(self):
self.stopSession()
self.cleanup()
def onEmptyQueue(self):
if (self.repeat_left>0):
if (int(self.delay) > 0):
self.setTimer(DELAY_TIMER_ID, int(self.delay)/1000)
else:
self.repeat_left-=1
self.announcement.rewind()
self.enqueue(self.announcement, None)
else:
self.bye()
self.stopSession()
self.cleanup()
def onDtmf(self,key,duration):
pass
def onTimer(self, timer_id):
if (timer_id == TIMEOUT_TIMER_ID):
self.bye()
self.stopSession()
self.cleanup()
elif (timer_id == DELAY_TIMER_ID):
self.repeat_left-=1
self.announcement.rewind()
self.enqueue(self.announcement, None)
def cleanup(self):
if (self.delete_onbye):
unlink(self.filename)
debug("cleanup..." + self.filename + " deleted.")
self.removeTimers()

@ -0,0 +1,2 @@
import compileall
compileall.main()

@ -0,0 +1,84 @@
#include "AmPlugIn.h"
#include "log.h"
#include "CCAcc.h"
class CCAccFactory : public AmDynInvokeFactory
{
public:
CCAccFactory(const string& name)
: AmDynInvokeFactory(name) {}
AmDynInvoke* getInstance(){
return CCAcc::instance();
}
int onLoad(){
DBG("CCAcc calling card accounting loaded.\n");
return 0;
}
};
EXPORT_PLUGIN_CLASS_FACTORY(CCAccFactory,"cc_acc");
CCAcc* CCAcc::_instance=0;
CCAcc* CCAcc::instance()
{
if(!_instance)
_instance = new CCAcc();
return _instance;
}
CCAcc::CCAcc() {
// add some sample credits
credits["12345"] = 100;
}
CCAcc::~CCAcc() { }
void CCAcc::invoke(const string& method, const AmArgArray& args, AmArgArray& ret)
{
if(method == "getCredit"){
ret.push(getCredit(args.get(0).asCStr()));
}
else if(method == "subtractCredit"){
ret.push(subtractCredit(args.get(0).asCStr(),
args.get(1).asInt()));
}
else
throw AmDynInvoke::NotImplemented(method);
}
/* accounting functions... */
int CCAcc::getCredit(string pin) {
credits_mut.lock();
map<string, unsigned int>::iterator it = credits.find(pin);
if (it == credits.end()) {
DBG("PIN '%s' dies not exist.", pin.c_str());
credits_mut.unlock();
return -1;
}
unsigned int res = it->second;
credits_mut.unlock();
return res;
}
int CCAcc::subtractCredit(string pin, int amount) {
credits_mut.lock();
map<string, unsigned int>::iterator it = credits.find(pin);
if (it == credits.end()) {
ERROR("PIN '%s' dies not exist.", pin.c_str());
credits_mut.unlock();
return -1;
}
if (it->second - amount < 0) {
credits[pin] = 0;
} else {
credits[pin] = it->second - amount;
}
unsigned int res = credits[pin];
credits_mut.unlock();
return res;
}

@ -0,0 +1,27 @@
#include "AmApi.h"
/**
* accounting class for calling card.
* this illustrates the DI interface
* and component modules
*/
class CCAcc : public AmDynInvoke
{
/** returns credit for pin, -1 if pin wrong */
int getCredit(string pin);
/** returns remaining credit */
int subtractCredit(string pin, int amount);
map<string, unsigned int> credits;
// as this is used from various sessions,
// it must be protected by a mutex
AmMutex credits_mut;
static CCAcc* _instance;
public:
CCAcc();
~CCAcc();
static CCAcc* instance();
void invoke(const string& method, const AmArgArray& args, AmArgArray& ret);
};

@ -0,0 +1,7 @@
plug_in_name = cc_acc
module_ldflags =
module_cflags =
COREPATH ?=../../../../core
include $(COREPATH)/plug-in/Makefile.app_module

@ -0,0 +1,14 @@
NAME=ivr_announce
VERSION=0.0.1
PYTHON_VERSION=2.4
LIBDIR=
COREPATH=../../../../core
IVRPATH=../../../ivr
# general python app Makefile
include $(IVRPATH)/Makefile.ivr_application

@ -0,0 +1 @@
announcement=/usr/local/lib/sems/audio/default.wav

@ -0,0 +1,16 @@
from log import *
from ivr import *
class IvrDialog(IvrDialogBase):
announcement = None
def onSessionStart(self, hdrs):
debug("onSessionStart of ivr announcement app")
self.announcement = IvrAudioFile()
self.announcement.open(config['announcement'], ivr.AUDIO_READ)
self.enqueue(self.announcement, None)
def onEmptyQueue(self):
self.bye()
self.stopSession()

@ -0,0 +1,2 @@
import compileall
compileall.main()

@ -0,0 +1,7 @@
plug_in_name = myannounceapp
module_ldflags =
module_cflags =
COREPATH ?=../../../../core
include $(COREPATH)/plug-in/Makefile.app_module

@ -0,0 +1,66 @@
#include "MyAnnounceApp.h"
#include "log.h"
#include "AmConfigReader.h"
#include "AmUtils.h"
#define MOD_NAME "myannounceapp"
EXPORT_SESSION_FACTORY(MyAnnounceAppFactory,MOD_NAME);
string MyAnnounceAppFactory::AnnouncementFile;
MyAnnounceAppFactory::MyAnnounceAppFactory(const string& _app_name)
: AmSessionFactory(_app_name)
{
}
int MyAnnounceAppFactory::onLoad()
{
AmConfigReader cfg;
if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf")))
return -1;
AnnouncementFile = cfg.getParameter("announcement_file","/tmp/default.wav");
if(!file_exists(AnnouncementFile)){
ERROR("announcement file for configurableApp module does not exist ('%s').\n",
AnnouncementFile.c_str());
return -1;
}
return 0;
}
AmSession* MyAnnounceAppFactory::onInvite(const AmSipRequest& req)
{
return new MyAnnounceAppDialog();
}
MyAnnounceAppDialog::MyAnnounceAppDialog()
{
}
MyAnnounceAppDialog::~MyAnnounceAppDialog()
{
}
void MyAnnounceAppDialog::onSessionStart(const AmSipRequest& req)
{
DBG("MyAnnounceAppDialog::onSessionStart - file is '%s'\n",
MyAnnounceAppFactory::AnnouncementFile.c_str());
if(wav_file.open(MyAnnounceAppFactory::AnnouncementFile,AmAudioFile::Read))
throw string("MyAnnounceAppDialog::onSessionStart: Cannot open file\n");
setOutput(&wav_file);
}
void MyAnnounceAppDialog::onBye(const AmSipRequest& req)
{
DBG("onBye: stopSession\n");
setStopped();
}

@ -0,0 +1,35 @@
#ifndef _MYANNOUNCEAPP_H_
#define _MYANNOUNCEAPP_H_
#include "AmSession.h"
#include "AmAudio.h"
#include <string>
using std::string;
class MyAnnounceAppFactory: public AmSessionFactory
{
public:
static string AnnouncementFile;
MyAnnounceAppFactory(const string& _app_name);
int onLoad();
AmSession* onInvite(const AmSipRequest& req);
};
class MyAnnounceAppDialog : public AmSession
{
AmAudioFile wav_file;
public:
MyAnnounceAppDialog();
~MyAnnounceAppDialog();
void onSessionStart(const AmSipRequest& req);
void onBye(const AmSipRequest& req);
};
#endif

@ -0,0 +1,7 @@
MyAnnouceapp
This app plays a file back to the
caller. The file is configured
with the annoucement_file config
option.

@ -0,0 +1,3 @@
#CFGOPTION_SEMS_MYCONFIGURABLEAPP_ANNFILE
announcement_file=/tmp/test.wav
#ENDCFGOPTION

@ -0,0 +1,7 @@
plug_in_name = myapp
module_ldflags =
module_cflags =
COREPATH ?=../../../../core
include $(COREPATH)/plug-in/Makefile.app_module

@ -0,0 +1,41 @@
#include "MyApp.h"
#include "log.h"
#define MOD_NAME "myapp"
EXPORT_SESSION_FACTORY(MyAppFactory,MOD_NAME);
MyAppFactory::MyAppFactory(const string& _app_name)
: AmSessionFactory(_app_name)
{
}
int MyAppFactory::onLoad()
{
return 0;
}
AmSession* MyAppFactory::onInvite(const AmSipRequest& req)
{
return new MyAppDialog();
}
MyAppDialog::MyAppDialog()
{
}
MyAppDialog::~MyAppDialog()
{
}
void MyAppDialog::onSessionStart(const AmSipRequest& req)
{
DBG("MyAppDialog::onSessionStart: Hello World!\n");
}
void MyAppDialog::onBye(const AmSipRequest& req)
{
DBG("onBye: stopSession\n");
setStopped();
}

@ -0,0 +1,27 @@
#ifndef _MYAPP_H_
#define _MYAPP_H_
#include "AmSession.h"
class MyAppFactory: public AmSessionFactory
{
public:
MyAppFactory(const string& _app_name);
int onLoad();
AmSession* onInvite(const AmSipRequest& req);
};
class MyAppDialog : public AmSession
{
public:
MyAppDialog();
~MyAppDialog();
void onSessionStart(const AmSipRequest& req);
void onBye(const AmSipRequest& req);
};
#endif

@ -0,0 +1,6 @@
MyApp
This is an empty template for a SEMS 0.10.0
application plug-in written in C++.

@ -0,0 +1,7 @@
plug_in_name = mycc
module_ldflags =
module_cflags =
COREPATH ?=../../../../core
include $(COREPATH)/plug-in/Makefile.app_module

@ -0,0 +1,252 @@
#include "MyCC.h"
#include "log.h"
#include "AmConfigReader.h"
#include "AmUtils.h"
#include "AmAudio.h"
#include "AmPlugIn.h"
#include <sys/time.h>
#include <time.h>
#define MOD_NAME "mycc"
#define TIMERID_CREDIT_TIMEOUT 1
EXPORT_SESSION_FACTORY(MyCCFactory,MOD_NAME);
string MyCCFactory::InitialAnnouncement;
string MyCCFactory::IncorrectPIN;
string MyCCFactory::OutOfCredit;
string MyCCFactory::Dialing;
string MyCCFactory::DialFailed;
string MyCCFactory::EnterNumber;
string MyCCFactory::ConnectSuffix;
MyCCFactory::MyCCFactory(const string& _app_name)
: AmSessionFactory(_app_name), user_timer_fact(NULL)
{
}
int MyCCFactory::onLoad()
{
AmConfigReader cfg;
if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf")))
return -1;
InitialAnnouncement = cfg.getParameter("initial_announcement", "/tmp/hello.wav");
IncorrectPIN = cfg.getParameter("incorrect_pin", "/tmp/incorrect_pin.wav");
OutOfCredit = cfg.getParameter("out_of_credit", "/tmp/out_of_credit.wav");
Dialing = cfg.getParameter("dialing", "/tmp/dialing.wav");
DialFailed = cfg.getParameter("dial_failed", "/tmp/dial_failed.wav");
EnterNumber = cfg.getParameter("enter_number", "/tmp/enter_number.wav");
ConnectSuffix = cfg.getParameter("connect_suffix", "@127.0.0.1");
user_timer_fact = AmPlugIn::instance()->getFactory4Di("user_timer");
if(!user_timer_fact){
ERROR("could not load user_timer from session_timer plug-in\n");
return -1;
}
cc_acc_fact = AmPlugIn::instance()->getFactory4Di("cc_acc");
if(!cc_acc_fact){
ERROR("could not load cc_acc accounting, please provide a module\n");
return -1;
}
return 0;
}
AmSession* MyCCFactory::onInvite(const AmSipRequest& req)
{
AmDynInvoke* user_timer = user_timer_fact->getInstance();
if(!user_timer){
ERROR("could not get a user timer reference\n");
throw AmSession::Exception(500,"could not get a user timer reference");
}
AmDynInvoke* cc_acc = cc_acc_fact->getInstance();
if(!user_timer){
ERROR("could not get a cc acc reference\n");
throw AmSession::Exception(500,"could not get a cc acc reference");
}
return new MyCCDialog(cc_acc, user_timer);
}
MyCCDialog::MyCCDialog(AmDynInvoke* cc_acc, AmDynInvoke* user_timer)
: playlist(this),
state(CC_Collecting_PIN),
cc_acc(cc_acc), user_timer(user_timer),
AmB2BCallerSession()
{
sip_relay_only = false;
memset(&acc_start, 0, sizeof(struct timeval));
}
MyCCDialog::~MyCCDialog()
{
}
void MyCCDialog::addToPlaylist(string fname) {
AmAudioFile* wav_file = new AmAudioFile();
if(wav_file->open(fname,AmAudioFile::Read)) {
ERROR("MyCCDialog::onSessionStart: Cannot open file\n");
delete wav_file;
} else {
AmPlaylistItem* item = new AmPlaylistItem(wav_file, NULL);
playlist.addToPlaylist(item);
}
}
void MyCCDialog::onSessionStart(const AmSipRequest& req)
{
DBG("MyCCDialog::onSessionStart");
AmB2BCallerSession::onSessionStart(req);
setInOut(&playlist, &playlist);
addToPlaylist(MyCCFactory::InitialAnnouncement);
setDtmfDetectionEnabled(true);
}
void MyCCDialog::onDtmf(int event, int duration) {
DBG("MyCCDialog::onDtmf, got event %d, duration %d.\n", event, duration);
switch (state) {
case CC_Collecting_PIN:
if(event <10) {
pin +=int2str(event);
DBG("pin is now '%s'\n", pin.c_str());
} else {
AmArgArray di_args,ret;
di_args.push(pin.c_str());
cc_acc->invoke("getCredit", di_args, ret);
credit = ret.get(0).asInt();
if (credit < 0) {
addToPlaylist(MyCCFactory::IncorrectPIN);
pin = "";
} else if (credit == 0) {
addToPlaylist(MyCCFactory::OutOfCredit);
pin = "";
} else {
number = "";
state = CC_Collecting_Number;
addToPlaylist(MyCCFactory::EnterNumber);
}
} break;
case CC_Collecting_Number:
if(event <10) {
number +=int2str(event);
DBG("number is now '%s'\n", number.c_str());
} else {
if (getCalleeStatus() == None) {
state = CC_Dialing;
connectCallee(number+MyCCFactory::ConnectSuffix,
"sip:"+number+MyCCFactory::ConnectSuffix);
addToPlaylist(MyCCFactory::Dialing);
}
} break;
case CC_Dialing:
case CC_Connected:
default: break;
};
}
void MyCCDialog::process(AmEvent* ev)
{
DBG("MyCCDialog::process\n");
AmAudioEvent* audio_ev = dynamic_cast<AmAudioEvent*>(ev);
if(audio_ev && (audio_ev->event_id == AmAudioEvent::noAudio)){
DBG("MyCCDialog::process: Playlist is empty!\n");
return;
}
AmPluginEvent* plugin_event = dynamic_cast<AmPluginEvent*>(ev);
if(plugin_event && plugin_event->name == "timer_timeout") {
int timer_id = plugin_event->data.get(0).asInt();
if (timer_id == TIMERID_CREDIT_TIMEOUT) {
DBG("timer timeout: no credit...\n");
stopAccounting();
terminateOtherLeg();
terminateLeg();
ev->processed = true;
return;
}
}
AmB2BCallerSession::process(ev);
}
void MyCCDialog::onOtherReply(const AmSipReply& reply) {
DBG("OnOtherReply \n");
if (state == CC_Dialing) {
if (reply.code < 200) {
DBG("Callee is trying... code %d\n", reply.code);
} else if(reply.code < 300){
if (getCalleeStatus() == Connected) {
state = CC_Connected;
startAccounting();
setInOut(NULL, NULL);
// set the call timer
AmArgArray di_args,ret;
di_args.push(TIMERID_CREDIT_TIMEOUT);
di_args.push(credit); // in seconds
di_args.push(dlg.local_tag.c_str());
user_timer->invoke("setTimer", di_args, ret);
}
} else {
DBG("Callee final error with code %d\n",reply.code);
addToPlaylist(MyCCFactory::DialFailed);
number = "";
state = CC_Collecting_Number;
}
}
// we dont call
// AmB2BCallerSession::onOtherReply(reply);
// as it tears down the call if callee could
// not be reached
}
void MyCCDialog::onOtherBye(const AmSipRequest& req) {
DBG("onOtherBye\n");
stopAccounting();
AmB2BCallerSession::onOtherBye(req); // will stop the session
}
void MyCCDialog::onBye(const AmSipRequest& req)
{
DBG("onBye: stopSession\n");
if (state == CC_Connected) {
stopAccounting();
}
terminateOtherLeg();
setStopped();
}
void MyCCDialog::startAccounting() {
gettimeofday(&acc_start,NULL);
DBG("start accounting at %ld\n", acc_start.tv_sec);
}
void MyCCDialog::stopAccounting() {
if ((acc_start.tv_sec != 0) || (acc_start.tv_usec != 0)) {
struct timeval now;
gettimeofday(&now,NULL);
DBG("stop accounting at %ld\n", now.tv_sec);
timersub(&now,&acc_start,&now);
if (now.tv_usec>500000) now.tv_sec++;
DBG("Call lasted %ld seconds\n", now.tv_sec);
AmArgArray di_args,ret;
di_args.push(pin.c_str());
di_args.push((int)now.tv_sec);
cc_acc->invoke("subtractCredit", di_args, ret);
}
}

@ -0,0 +1,69 @@
#ifndef _MYJUKEBOX_H_
#define _MYJUKEBOX_H_
#include "AmB2BSession.h"
#include "AmPlaylist.h"
#include <string>
using std::string;
class MyCCFactory: public AmSessionFactory
{
AmDynInvokeFactory* user_timer_fact;
AmDynInvokeFactory* cc_acc_fact;
public:
static string InitialAnnouncement;
static string IncorrectPIN;
static string OutOfCredit;
static string EnterNumber;
static string Dialing;
static string DialFailed;
static string ConnectSuffix;
MyCCFactory(const string& _app_name);
int onLoad();
AmSession* onInvite(const AmSipRequest& req);
};
class MyCCDialog : public AmB2BCallerSession
{
enum {CC_Collecting_PIN = 0,
CC_Collecting_Number,
CC_Dialing,
CC_Connected
} CallerState;
AmPlaylist playlist;
int state;
string pin;
string number;
int credit;
void addToPlaylist(string fname);
void startAccounting();
void stopAccounting();
struct timeval acc_start;
AmDynInvoke* user_timer;
AmDynInvoke* cc_acc;
public:
MyCCDialog(AmDynInvoke* cc_acc, AmDynInvoke* user_timer);
~MyCCDialog();
void onSessionStart(const AmSipRequest& req);
void onDtmf(int event, int duration);
void process(AmEvent* ev);
void onBye(const AmSipRequest& req);
protected:
void onOtherReply(const AmSipReply& reply);
void onOtherBye(const AmSipRequest& req);
};
#endif

@ -0,0 +1,11 @@
MyCC
This is a calling card application.
Caller is asked for a PIN which
is verified against an accounting
function provided by a cc_acc interface.
If PIN is correct, the caller
may enter a number to call, and is
connected to it in b2bua mode.

@ -0,0 +1,8 @@
initial_announcement =/usr/local/lib/sems/audio/ccard_wav/hello.wav
incorrect_pin =/usr/local/lib/sems/audio/ccard_wav/incorrect_pin.wav
out_of_credit =/usr/local/lib/sems/audio/ccard_wav/out_of_credit.wav
dialing =/usr/local/lib/sems/audio/ccard_wav/dialing.wav
dial_failed =/usr/local/lib/sems/audio/ccard_wav/dial_failed.wav
enter_number =/usr/local/lib/sems/audio/ccard_wav/enter_number.wav
connect_suffix=@127.0.0.1

@ -0,0 +1,7 @@
plug_in_name = myconfigurableapp
module_ldflags =
module_cflags =
COREPATH ?=../../../../core
include $(COREPATH)/plug-in/Makefile.app_module

@ -0,0 +1,60 @@
#include "MyConfigurableApp.h"
#include "log.h"
#include "AmConfigReader.h"
#include "AmUtils.h"
#define MOD_NAME "myconfigurableapp"
EXPORT_SESSION_FACTORY(MyConfigurableAppFactory,MOD_NAME);
string MyConfigurableAppFactory::AnnouncementFile;
MyConfigurableAppFactory::MyConfigurableAppFactory(const string& _app_name)
: AmSessionFactory(_app_name)
{
}
int MyConfigurableAppFactory::onLoad()
{
AmConfigReader cfg;
if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf")))
return -1;
AnnouncementFile = cfg.getParameter("announcement_file","/tmp/default.wav");
if(!file_exists(AnnouncementFile)){
ERROR("announcement file for configurableApp module does not exist ('%s').\n",
AnnouncementFile.c_str());
return -1;
}
return 0;
}
AmSession* MyConfigurableAppFactory::onInvite(const AmSipRequest& req)
{
return new MyConfigurableAppDialog();
}
MyConfigurableAppDialog::MyConfigurableAppDialog()
{
}
MyConfigurableAppDialog::~MyConfigurableAppDialog()
{
}
void MyConfigurableAppDialog::onSessionStart(const AmSipRequest& req)
{
DBG("MyConfigurableAppDialog::onSessionStart - file is '%s'\n",
MyConfigurableAppFactory::AnnouncementFile.c_str());
}
void MyConfigurableAppDialog::onBye(const AmSipRequest& req)
{
DBG("onBye: stopSession\n");
setStopped();
}

@ -0,0 +1,32 @@
#ifndef _MYCONFIGURABLEAPP_H_
#define _MYCONFIGURABLEAPP_H_
#include "AmSession.h"
#include <string>
using std::string;
class MyConfigurableAppFactory: public AmSessionFactory
{
public:
static string AnnouncementFile;
MyConfigurableAppFactory(const string& _app_name);
int onLoad();
AmSession* onInvite(const AmSipRequest& req);
};
class MyConfigurableAppDialog : public AmSession
{
public:
MyConfigurableAppDialog();
~MyConfigurableAppDialog();
void onSessionStart(const AmSipRequest& req);
void onBye(const AmSipRequest& req);
};
#endif

@ -0,0 +1,7 @@
MyConfigurableApp
This is a template for a SEMS 0.10.0
application plug-in written in C++.
If can be configured with its own
configuration file.

@ -0,0 +1,3 @@
#CFGOPTION_SEMS_MYCONFIGURABLEAPP_ANNFILE
announcement_file=/tmp/test.wav
#ENDCFGOPTION

@ -0,0 +1,7 @@
plug_in_name = myjukebox
module_ldflags =
module_cflags =
COREPATH ?=../../../../core
include $(COREPATH)/plug-in/Makefile.app_module

@ -0,0 +1,88 @@
#include "MyJukebox.h"
#include "log.h"
#include "AmConfigReader.h"
#include "AmUtils.h"
#include "AmAudio.h"
#define MOD_NAME "myjukebox"
EXPORT_SESSION_FACTORY(MyJukeboxFactory,MOD_NAME);
string MyJukeboxFactory::JukeboxDir;
MyJukeboxFactory::MyJukeboxFactory(const string& _app_name)
: AmSessionFactory(_app_name)
{
}
int MyJukeboxFactory::onLoad()
{
AmConfigReader cfg;
if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf")))
return -1;
JukeboxDir = cfg.getParameter("jukebox_dir","/tmp/");
if( !JukeboxDir.empty()
&& JukeboxDir[JukeboxDir.length()-1] != '/' )
JukeboxDir += "/";
return 0;
}
AmSession* MyJukeboxFactory::onInvite(const AmSipRequest& req)
{
return new MyJukeboxDialog();
}
MyJukeboxDialog::MyJukeboxDialog()
: playlist(this)
{
}
MyJukeboxDialog::~MyJukeboxDialog()
{
}
void MyJukeboxDialog::onSessionStart(const AmSipRequest& req)
{
DBG("MyJukeboxDialog::onSessionStart - jukedir is '%s'\n",
MyJukeboxFactory::JukeboxDir.c_str());
setInOut(&playlist, &playlist);
setDtmfDetectionEnabled(true);
}
void MyJukeboxDialog::onDtmf(int event, int duration) {
DBG("MyJukeboxDialog::onDtmf, got event %d, duration %d.\n", event, duration);
AmAudioFile* wav_file = new AmAudioFile();
if(wav_file->open(MyJukeboxFactory::JukeboxDir + int2str(event) + ".wav",AmAudioFile::Read)) {
ERROR("MyJukeboxDialog::onSessionStart: Cannot open file\n");
delete wav_file;
return;
}
AmPlaylistItem* item = new AmPlaylistItem(wav_file, NULL);
playlist.addToPlaylist(item);
}
void MyJukeboxDialog::process(AmEvent* ev)
{
DBG("AmSession::process\n");
AmAudioEvent* audio_ev = dynamic_cast<AmAudioEvent*>(ev);
if(audio_ev && (audio_ev->event_id == AmAudioEvent::noAudio)){
DBG("MyJukeboxDialog::process: Playlist is empty!\n");
return;
}
AmSession::process(ev);
}
void MyJukeboxDialog::onBye(const AmSipRequest& req)
{
DBG("onBye: stopSession\n");
setStopped();
}

@ -0,0 +1,36 @@
#ifndef _MYJUKEBOX_H_
#define _MYJUKEBOX_H_
#include "AmSession.h"
#include "AmPlaylist.h"
#include <string>
using std::string;
class MyJukeboxFactory: public AmSessionFactory
{
public:
static string JukeboxDir;
MyJukeboxFactory(const string& _app_name);
int onLoad();
AmSession* onInvite(const AmSipRequest& req);
};
class MyJukeboxDialog : public AmSession
{
AmPlaylist playlist;
public:
MyJukeboxDialog();
~MyJukeboxDialog();
void onSessionStart(const AmSipRequest& req);
void onDtmf(int event, int duration);
void process(AmEvent* ev);
void onBye(const AmSipRequest& req);
};
#endif

@ -0,0 +1,3 @@
#CFGOPTION_SEMS_MYCONFIGURABLEAPP_ANNFILE
jukebox_dir=/tmp/jukebox/
#ENDCFGOPTION
Loading…
Cancel
Save