mirror of https://github.com/sipwise/sems.git
mem cached audio files implementation - improves performance 4-5% over announcement from file (G711)
see apps/examples/cacheannounce for an example how to use git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@247 8eb893ce-cfd4-0310-b710-fb5ebe64c474sayer/1.4-spce2.6
parent
50c51dcd54
commit
6c8fe2c0bd
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* $Id: CacheAnnounce.cpp 206 2007-02-05 14:45:07Z sayer $
|
||||
*
|
||||
* Copyright (C) 2002-2003 Fhg Fokus
|
||||
* Copyright (C) 2007 iptego GmbH
|
||||
*
|
||||
* 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 sems 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 "CacheAnnounce.h"
|
||||
#include "AmConfig.h"
|
||||
#include "AmUtils.h"
|
||||
|
||||
#include "sems.h"
|
||||
#include "log.h"
|
||||
|
||||
#define MOD_NAME "cacheannounce"
|
||||
|
||||
EXPORT_SESSION_FACTORY(CacheAnnounceFactory,MOD_NAME);
|
||||
|
||||
string CacheAnnounceFactory::AnnouncePath;
|
||||
string CacheAnnounceFactory::AnnounceFile;
|
||||
|
||||
CacheAnnounceFactory::CacheAnnounceFactory(const string& _app_name)
|
||||
: AmSessionFactory(_app_name)
|
||||
{
|
||||
}
|
||||
|
||||
int CacheAnnounceFactory::onLoad()
|
||||
{
|
||||
AmConfigReader cfg;
|
||||
if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf")))
|
||||
return -1;
|
||||
|
||||
// get application specific global parameters
|
||||
configureModule(cfg);
|
||||
|
||||
AnnouncePath = cfg.getParameter("announce_path",ANNOUNCE_PATH);
|
||||
if( !AnnouncePath.empty()
|
||||
&& AnnouncePath[AnnouncePath.length()-1] != '/' )
|
||||
AnnouncePath += "/";
|
||||
|
||||
AnnounceFile = cfg.getParameter("default_announce",ANNOUNCE_FILE);
|
||||
|
||||
string announce_file = AnnouncePath + AnnounceFile;
|
||||
if(!file_exists(announce_file)){
|
||||
ERROR("default file for cacheannounce module does not exist ('%s').\n",
|
||||
announce_file.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ann_cache.load(announce_file)) {
|
||||
ERROR("file '%s' could not be cached.\n",
|
||||
announce_file.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
AmSession* CacheAnnounceFactory::onInvite(const AmSipRequest& req)
|
||||
{
|
||||
return new CacheAnnounceDialog(&ann_cache);
|
||||
}
|
||||
|
||||
CacheAnnounceDialog::CacheAnnounceDialog(AmFileCache* announce)
|
||||
: announce(announce)
|
||||
{
|
||||
}
|
||||
|
||||
CacheAnnounceDialog::~CacheAnnounceDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void CacheAnnounceDialog::onSessionStart(const AmSipRequest& req)
|
||||
{
|
||||
DBG("CacheAnnounceDialog::onSessionStart\n");
|
||||
startSession();
|
||||
}
|
||||
|
||||
void CacheAnnounceDialog::onSessionStart(const AmSipReply& rep)
|
||||
{
|
||||
DBG("CacheAnnounceDialog::onSessionStart (SEMS originator mode)\n");
|
||||
startSession();
|
||||
}
|
||||
|
||||
void CacheAnnounceDialog::startSession(){
|
||||
setDtmfDetectionEnabled(false);
|
||||
|
||||
wav_file.reset(new AmCachedAudioFile(announce));
|
||||
if (!wav_file->is_good())
|
||||
throw AmSession::Exception(500, "Internal Err");
|
||||
|
||||
setOutput(wav_file.get());
|
||||
}
|
||||
|
||||
void CacheAnnounceDialog::onBye(const AmSipRequest& req)
|
||||
{
|
||||
DBG("onBye: stopSession\n");
|
||||
setStopped();
|
||||
}
|
||||
|
||||
|
||||
void CacheAnnounceDialog::process(AmEvent* event)
|
||||
{
|
||||
|
||||
AmAudioEvent* audio_event = dynamic_cast<AmAudioEvent*>(event);
|
||||
if(audio_event && (audio_event->event_id == AmAudioEvent::cleared)){
|
||||
dlg.bye();
|
||||
setStopped();
|
||||
return;
|
||||
}
|
||||
|
||||
AmSession::process(event);
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* $Id: CacheAnnounce.h 145 2006-11-26 00:01:18Z sayer $
|
||||
*
|
||||
* Copyright (C) 2002-2003 Fhg Fokus
|
||||
* Copyright (C) 2007 iptego GmbH
|
||||
*
|
||||
* 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 sems 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
|
||||
*/
|
||||
|
||||
#ifndef _CACHEANNOUNCE_H_
|
||||
#define _CACHEANNOUNCE_H_
|
||||
|
||||
#include "AmSession.h"
|
||||
#include "AmConfigReader.h"
|
||||
|
||||
#include "AmCachedAudioFile.h"
|
||||
|
||||
#include <memory>
|
||||
using std::auto_ptr;
|
||||
|
||||
#include <string>
|
||||
using std::string;
|
||||
|
||||
class CacheAnnounceFactory: public AmSessionFactory
|
||||
{
|
||||
|
||||
AmFileCache ann_cache;
|
||||
|
||||
public:
|
||||
static string AnnouncePath;
|
||||
static string AnnounceFile;
|
||||
|
||||
CacheAnnounceFactory(const string& _app_name);
|
||||
|
||||
int onLoad();
|
||||
AmSession* onInvite(const AmSipRequest& req);
|
||||
};
|
||||
|
||||
class CacheAnnounceDialog : public AmSession
|
||||
{
|
||||
auto_ptr<AmCachedAudioFile> wav_file;
|
||||
AmFileCache* announce;
|
||||
|
||||
public:
|
||||
CacheAnnounceDialog(AmFileCache* announce);
|
||||
~CacheAnnounceDialog();
|
||||
|
||||
void onSessionStart(const AmSipRequest& req);
|
||||
void onSessionStart(const AmSipReply& rep);
|
||||
void startSession();
|
||||
void onBye(const AmSipRequest& req);
|
||||
void onDtmf(int event, int duration_msec) {}
|
||||
|
||||
void process(AmEvent* event);
|
||||
};
|
||||
|
||||
#endif
|
||||
// Local Variables:
|
||||
// mode:C++
|
||||
// End:
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
plug_in_name = cacheannounce
|
||||
|
||||
module_ldflags =
|
||||
module_cflags =
|
||||
|
||||
extra_install = $(plug_in_name)_audio
|
||||
|
||||
COREPATH ?=../../../core
|
||||
include $(COREPATH)/plug-in/Makefile.app_module
|
||||
@ -0,0 +1,13 @@
|
||||
cacheannounce always plays the default announce to the caller.
|
||||
|
||||
This is a example of how to use the mem cached versions of AmAudioFile:
|
||||
AmFileCache/AmCachedAudioFile.
|
||||
|
||||
The AmFileCache class can load a file once (AmFileCache::load),
|
||||
and the AmCachedAudioFile then uses the file from memory. AmFileCache
|
||||
objects can e.g. be created at startup and passed to the session object
|
||||
which creates AmCachedAudioFile objects to play back the files.
|
||||
|
||||
Measurements with 100 calls playing back default_en.wav encoded as PCMU
|
||||
have shown a 4-5% performance increase (192,607,161 Ir's vs. 201,569,407 Ir's)
|
||||
over the normal not cached announcement application.
|
||||
@ -0,0 +1,7 @@
|
||||
#CFGOPTION_SEMS_ANNOUNCEPATH
|
||||
announce_path=/usr/local/lib/sems/audio/
|
||||
#ENDCFGOPTION
|
||||
|
||||
#CFGOPTION_SEMS_ANNOUNCEMENT
|
||||
default_announce=default_en.wav
|
||||
#ENDCFGOPTION
|
||||
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* $Id: AmFileCache.cpp 145 2006-11-26 00:01:18Z sayer $
|
||||
*
|
||||
* Copyright (C) 2007 iptego GmbH
|
||||
*
|
||||
* 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 sems 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 "AmCachedAudioFile.h"
|
||||
#include "AmUtils.h"
|
||||
#include "log.h"
|
||||
#include "AmPlugIn.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
|
||||
|
||||
using std::string;
|
||||
|
||||
AmFileCache::AmFileCache()
|
||||
: data(NULL),
|
||||
data_size(0)
|
||||
{ }
|
||||
|
||||
AmFileCache::~AmFileCache() {
|
||||
if ((data != NULL) &&
|
||||
munmap(data, data_size)) {
|
||||
ERROR("while unmapping file.\n");
|
||||
}
|
||||
}
|
||||
|
||||
int AmFileCache::load(const string& filename) {
|
||||
int fd;
|
||||
struct stat sbuf;
|
||||
|
||||
name = filename;
|
||||
|
||||
if ((fd = open(name.c_str(), O_RDONLY)) == -1) {
|
||||
ERROR("while opening file '%s' for caching.\n",
|
||||
filename.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fstat(fd, &sbuf) == -1) {
|
||||
ERROR("cannot stat file '%s'.\n",
|
||||
name.c_str());
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ((data = mmap((caddr_t)0, sbuf.st_size, PROT_READ, MAP_PRIVATE,
|
||||
fd, 0)) == (caddr_t)(-1)) {
|
||||
ERROR("cannot mmap file '%s'.\n",
|
||||
name.c_str());
|
||||
return -3;
|
||||
}
|
||||
|
||||
data_size = sbuf.st_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AmFileCache::read(void* buf,
|
||||
size_t* pos,
|
||||
size_t size) {
|
||||
|
||||
if (*pos >= data_size)
|
||||
return -1; // eof
|
||||
|
||||
size_t r_size = size;
|
||||
if (*pos+size > data_size)
|
||||
r_size = data_size-*pos;
|
||||
|
||||
if (r_size>0) {
|
||||
memcpy(buf, (unsigned char*)data + *pos, r_size);
|
||||
*pos+=r_size;
|
||||
}
|
||||
return r_size;
|
||||
}
|
||||
|
||||
inline size_t AmFileCache::getSize() {
|
||||
return data_size;
|
||||
}
|
||||
|
||||
inline const string& AmFileCache::getFilename() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
AmCachedAudioFile::AmCachedAudioFile(AmFileCache* cache)
|
||||
: cache(cache), loop(false), fpos(0), begin(0), good(false)
|
||||
{
|
||||
if (!cache) {
|
||||
ERROR("Need open file cache.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
AmAudioFileFormat* f_fmt = fileName2Fmt(cache->getFilename());
|
||||
if(!f_fmt){
|
||||
ERROR("while trying to determine the format of '%s'\n",
|
||||
cache->getFilename().c_str());
|
||||
return;
|
||||
}
|
||||
fmt.reset(f_fmt);
|
||||
|
||||
amci_file_desc_t fd;
|
||||
int ret = -1;
|
||||
|
||||
fd.subtype = f_fmt->getSubtypeId();
|
||||
fd.channels = f_fmt->channels;
|
||||
fd.rate = f_fmt->rate;
|
||||
|
||||
long unsigned int ofpos = fpos;
|
||||
|
||||
if( iofmt->mem_open &&
|
||||
!(ret = (*iofmt->mem_open)((unsigned char*)cache->getData(),cache->getSize(),&ofpos,
|
||||
&fd,AmAudioFile::Read,f_fmt->getHCodecNoInit())) ) {
|
||||
f_fmt->setSubtypeId(fd.subtype);
|
||||
f_fmt->channels = fd.channels;
|
||||
f_fmt->rate = fd.rate;
|
||||
|
||||
begin = fpos = ofpos;
|
||||
}
|
||||
else {
|
||||
if(!iofmt->mem_open)
|
||||
ERROR("no mem_open function\n");
|
||||
else
|
||||
ERROR("mem_open returned %d\n",ret);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
good = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
AmCachedAudioFile::~AmCachedAudioFile() {
|
||||
}
|
||||
|
||||
AmAudioFileFormat* AmCachedAudioFile::fileName2Fmt(const string& name)
|
||||
{
|
||||
string ext = file_extension(name);
|
||||
if(ext == ""){
|
||||
ERROR("fileName2Fmt: file name has no extension (%s)",name.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iofmt = AmPlugIn::instance()->fileFormat("",ext);
|
||||
if(!iofmt){
|
||||
ERROR("fileName2Fmt: could not find a format with that extension: '%s'",ext.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return new AmAudioFileFormat(iofmt->name);
|
||||
}
|
||||
|
||||
void AmCachedAudioFile::rewind() {
|
||||
fpos = begin;
|
||||
}
|
||||
|
||||
/** Closes the file. */
|
||||
void AmCachedAudioFile::close() {
|
||||
fpos = 0;
|
||||
}
|
||||
|
||||
/** Executes the handler's on_close. */
|
||||
void on_close() {
|
||||
}
|
||||
|
||||
int AmCachedAudioFile::read(unsigned int user_ts, unsigned int size) {
|
||||
|
||||
if(!good){
|
||||
ERROR("AmAudioFile::read: file is not opened\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = cache->read((void*)((unsigned char*)samples),&fpos,size);
|
||||
|
||||
//DBG("s = %i; ret = %i\n",s,ret);
|
||||
if(loop.get() && (ret <= 0) && fpos==cache->getSize()){
|
||||
DBG("rewinding audio file...\n");
|
||||
rewind();
|
||||
ret = cache->read((void*)((unsigned char*)samples),&fpos, size);
|
||||
}
|
||||
|
||||
if(ret > 0 && (unsigned int)ret < size){
|
||||
DBG("0-stuffing packet: adding %i bytes (packet size=%i)\n",size-ret,size);
|
||||
memset((unsigned char*)samples + ret,0,size-ret);
|
||||
return size;
|
||||
}
|
||||
|
||||
return (fpos==cache->getSize() && !loop.get() ? -2 : ret);
|
||||
}
|
||||
|
||||
int AmCachedAudioFile::write(unsigned int user_ts, unsigned int size) {
|
||||
ERROR("AmCachedAudioFile writing not supported!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* $Id: AmFileCache.h 145 2006-11-26 00:01:18Z sayer $
|
||||
*
|
||||
* Copyright (C) 2007 iptego GmbH
|
||||
*
|
||||
* 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 sems 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
|
||||
*/
|
||||
|
||||
#ifndef _AMFILECACHE_H
|
||||
#define _AMFILECACHE_H
|
||||
|
||||
#include "AmAudio.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* The AmFileCache class loads a file once into memory
|
||||
* to be used e.g. by AmCachedAudioFile.
|
||||
*/
|
||||
class AmFileCache
|
||||
{
|
||||
void* data;
|
||||
size_t data_size;
|
||||
std::string name;
|
||||
|
||||
public:
|
||||
AmFileCache();
|
||||
~AmFileCache();
|
||||
|
||||
/** load filename into memory
|
||||
* @return 0 if everything's OK
|
||||
*/
|
||||
int load(const std::string& filename);
|
||||
/** get the size of the file */
|
||||
size_t getSize();
|
||||
/** read size bytes from pos into buf */
|
||||
int read(void* buf, size_t* pos, size_t size);
|
||||
/** get the filename */
|
||||
const string& getFilename();
|
||||
/** get a pointer to the file's data - use with caution! */
|
||||
void* getData() { return data; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief AmAudio implementation for cached file
|
||||
*
|
||||
* This uses an AmFileCache instance to read the data
|
||||
* rather than a file.
|
||||
*/
|
||||
class AmCachedAudioFile
|
||||
: public AmAudio
|
||||
{
|
||||
AmFileCache* cache;
|
||||
/** current position */
|
||||
size_t fpos;
|
||||
/** beginning of data in file */
|
||||
size_t begin;
|
||||
bool good;
|
||||
|
||||
/** @see AmAudio::read */
|
||||
int read(unsigned int user_ts, unsigned int size);
|
||||
|
||||
/** @see AmAudio::write */
|
||||
int write(unsigned int user_ts, unsigned int size);
|
||||
|
||||
/** get the file format from the file name */
|
||||
AmAudioFileFormat* fileName2Fmt(const string& name);
|
||||
|
||||
/** Format of that file. @see fp, open(). */
|
||||
amci_inoutfmt_t* iofmt;
|
||||
|
||||
public:
|
||||
AmCachedAudioFile(AmFileCache* cache);
|
||||
~AmCachedAudioFile();
|
||||
|
||||
/** loop the file? */
|
||||
AmSharedVar<bool> loop;
|
||||
|
||||
/**
|
||||
* Rewind the file.
|
||||
*/
|
||||
void rewind();
|
||||
|
||||
/** Closes the file. */
|
||||
void close();
|
||||
|
||||
/** everything ok? */
|
||||
bool is_good() { return good; }
|
||||
};
|
||||
#endif //_AMFILECACHE_H
|
||||
Loading…
Reference in new issue