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-fb5ebe64c474
sayer/1.4-spce2.6
Stefan Sayer 19 years ago
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

@ -171,6 +171,44 @@ typedef int (*amci_file_close_t)( FILE* fptr,
struct amci_codec_t *codec
);
/**
* File format handler's open function from memory area
* @param fptr [in] pointer to memory where file is loaded
* @param size [in] length of file in mem
* @param pos [out] position after open
* @param fmt_desc [out] file description
* @param options [in] options (see amci_inoutfmt_t)
* @param h_codec [in] handle of the codec
* @return if failure -1, else 0.
* @see amci_inoutfmt_t::open
*/
typedef int (*amci_file_mem_open_t)(unsigned char* mptr,
unsigned long size,
unsigned long* pos,
struct amci_file_desc_t* fmt_desc,
int options,
long h_codec
);
/**
* File format handler's mem close function (usually no-op)
* @param fptr [in] pointer to memory where file is loaded
* @param pos [in,out] position in memory
* @param fmt_desc [out] file description
* @param options [in] options (see amci_inoutfmt_t)
* @param h_codec [in] handle of the codec
* @param codec [in] codec structure
* @return if failure -1, else 0.
* @see amci_inoutfmt_t::on_close
*/
typedef int (*amci_file_mem_close_t)( unsigned char* mptr,
unsigned long* pos,
struct amci_file_desc_t* fmt_desc,
int options,
long h_codec,
struct amci_codec_t *codec
);
/**
* Codec's init function pointer.
* @param format_parameters [in] parameters as passed by fmtp tag, 0 if none
@ -285,6 +323,12 @@ struct amci_inoutfmt_t {
/** no options at the moment. */
amci_file_close_t on_close;
/** options: AMCI_RDONLY, AMCI_WRONLY. */
amci_file_mem_open_t mem_open;
/** no options at the moment. */
amci_file_mem_close_t mem_close;
/** NULL terminated subtype array. */
struct amci_subtype_t* subtypes;
@ -428,7 +472,7 @@ struct amci_exports_t {
* @hideinitializer
*/
#define END_FILE_FORMATS \
{ 0,0,0,0,0, \
{ 0,0,0,0,0,0,0, \
(struct amci_subtype_t[]) { {-1, 0, -1, -1, -1} } \
} \
}
@ -438,8 +482,8 @@ struct amci_exports_t {
* see example media plug-in 'wav' (plug-in/wav/wav.c).
* @hideinitializer
*/
#define BEGIN_FILE_FORMAT(name,ext,email_content_type,open,on_close) \
{ name,ext,email_content_type,open,on_close,
#define BEGIN_FILE_FORMAT(name,ext,email_content_type,open,on_close,mem_open,mem_close) \
{ name,ext,email_content_type,open,on_close,mem_open,mem_close,
/**
* Portable export definition macro

@ -91,7 +91,7 @@ BEGIN_EXPORTS( "ilbc" )
END_PAYLOADS
BEGIN_FILE_FORMATS
BEGIN_FILE_FORMAT( "iLBC", "ilbc", "audio/iLBC", iLBC_open, iLBC_close)
BEGIN_FILE_FORMAT( "iLBC", "ilbc", "audio/iLBC", iLBC_open, iLBC_close, 0, 0)
BEGIN_SUBTYPES
SUBTYPE( ILBC30, "iLBC30", 8000, 1, CODEC_ILBC )
SUBTYPE( ILBC20, "iLBC20", 8000, 1, CODEC_ILBC )

@ -57,7 +57,7 @@
END_PAYLOADS
BEGIN_FILE_FORMATS
BEGIN_FILE_FORMAT( "Wav", "wav", "application/x-wav", wav_open, wav_close)
BEGIN_FILE_FORMAT( "Wav", "wav", "application/x-wav", wav_open, wav_close, 0, 0)
BEGIN_SUBTYPES
SUBTYPE( WAV_PCM, "Pcm16", 2, -1, -1, CODEC_PCM16 )
SUBTYPE( WAV_ALAW, "A-Law", 1, -1, -1, CODEC_ALAW )
@ -105,7 +105,7 @@ BEGIN_EXPORTS( "wav" )
END_PAYLOADS
BEGIN_FILE_FORMATS
BEGIN_FILE_FORMAT( "Wav", "wav", "audio/x-wav", wav_open, wav_close)
BEGIN_FILE_FORMAT( "Wav", "wav", "audio/x-wav", wav_open, wav_close, wav_mem_open, wav_mem_close)
BEGIN_SUBTYPES
SUBTYPE( WAV_PCM, "Pcm16", 8000, 1, CODEC_PCM16 ) // we only support 8000/1 channel !
SUBTYPE( WAV_ALAW, "A-Law", 8000, 1, CODEC_ALAW )

@ -206,7 +206,110 @@ int wav_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_c
return 0;
}
#define SAFE_MEM_READ(buf,s,mptr,pos,size) \
if (*pos+s>size) return -1; \
memcpy(buf,mptr+*pos,s); \
*pos+=s;
int wav_mem_open(unsigned char* mptr, unsigned long size, unsigned long* pos,
struct amci_file_desc_t* fmt_desc, int options, long h_codec) {
if(options == AMCI_RDONLY){
char tag[4]={'\0'};
unsigned int file_size=0;
unsigned int chunk_size=0;
unsigned short fmt=0;
unsigned short channels=0;
unsigned int rate=0;
unsigned short bits_per_sample=0;
unsigned short sample_size=0;
if(!mptr)
return -1;
*pos=0;
DBG("trying to read WAV file from memory\n");
SAFE_MEM_READ(tag,4,mptr,pos,size);
DBG("tag = <%.4s>\n",tag);
if(strncmp(tag,"RIFF",4)){
DBG("wrong format !");
return -1;
}
SAFE_MEM_READ(&file_size,4,mptr,pos,size);
DBG("file size = <%u>\n",file_size);
SAFE_MEM_READ(tag,4,mptr,pos,size);
DBG("tag = <%.4s>\n",tag);
if(strncmp(tag,"WAVE",4)){
DBG("wrong format !");
return -1;
}
SAFE_MEM_READ(tag,4,mptr,pos,size);
DBG("tag = <%.4s>\n",tag);
if(strncmp(tag,"fmt ",4)){
DBG("wrong format !");
return -1;
}
SAFE_MEM_READ(&chunk_size,4,mptr,pos,size);
DBG("chunk_size = <%u>\n",chunk_size);
SAFE_MEM_READ(&fmt,2,mptr,pos,size);
DBG("fmt = <%.2x>\n",fmt);
SAFE_MEM_READ(&channels,2,mptr,pos,size);
DBG("channels = <%i>\n",channels);
SAFE_MEM_READ(&rate,4,mptr,pos,size);
DBG("rate = <%i>\n",rate);
/* do not read bytes/sec and block align */
*pos +=6;
SAFE_MEM_READ(&bits_per_sample,2,mptr,pos,size);
DBG("bits/sample = <%i>\n",bits_per_sample);
fmt_desc->subtype = fmt;
sample_size = bits_per_sample>>3;
fmt_desc->rate = rate;
fmt_desc->channels = channels;
if( (fmt == 0x01) && (sample_size == 1)){
ERROR("Sorry, we don't support PCM 8 bit\n");
return -1;
}
*pos+=chunk_size-16;
for(;;) {
SAFE_MEM_READ(tag,4,mptr,pos,size);
DBG("tag = <%.4s>\n",tag);
SAFE_MEM_READ(&chunk_size,4,mptr,pos,size);
DBG("chunk size = <%i>\n",chunk_size);
if(!strncmp(tag,"data",4))
break;
*pos += chunk_size;
}
return 0;
} else {
ERROR("write support for in-memory file not implemented!\n");
return -1;
}
}
int wav_mem_close(unsigned char* mptr, unsigned long* pos,
struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec) {
return 0;
}

@ -33,5 +33,11 @@
int wav_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec);
int wav_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec);
int wav_mem_open(unsigned char* mptr, unsigned long size, unsigned long* pos,
struct amci_file_desc_t* fmt_desc, int options, long h_codec);
int wav_mem_close(unsigned char* mptr, unsigned long* pos,
struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec);
#endif

Loading…
Cancel
Save