diff --git a/core/AmAudio.cpp b/core/AmAudio.cpp index df4e229f..d7582bbb 100644 --- a/core/AmAudio.cpp +++ b/core/AmAudio.cpp @@ -142,35 +142,24 @@ AmAudioSimpleFormat::AmAudioSimpleFormat(int codec_id) channels = 1; } -AmAudioFileFormat::AmAudioFileFormat(const string& name, int subtype) - : name(name), subtype(subtype), p_subtype(0) -{ - getSubtype(); - codec = getCodec(); - - if(p_subtype && codec){ - rate = p_subtype->sample_rate; - channels = p_subtype->channels; - subtype = p_subtype->type; - } -} AmAudioFormat::~AmAudioFormat() { destroyCodec(); } -unsigned int AmAudioFormat::samples2bytes(unsigned int nb_samples) const +unsigned int AmAudioFormat::calcBytesToRead(unsigned int needed_samples) const { if (codec && codec->samples2bytes) - return codec->samples2bytes(h_codec, nb_samples) * channels; + return codec->samples2bytes(h_codec, needed_samples) * channels; // FIXME: channels + WARN("Cannot convert samples to bytes\n"); - return nb_samples * channels; + return needed_samples * channels; } unsigned int AmAudioFormat::bytes2samples(unsigned int bytes) const { - if (codec && codec->samples2bytes) + if (codec && codec->bytes2samples) return codec->bytes2samples(h_codec, bytes) / channels; WARN("Cannot convert bytes to samples\n"); return bytes / channels; @@ -232,27 +221,6 @@ void AmAudioFormat::destroyCodec() codec = NULL; } -amci_subtype_t* AmAudioFileFormat::getSubtype() -{ - if(!p_subtype && !name.empty()){ - - amci_inoutfmt_t* iofmt = AmPlugIn::instance()->fileFormat(name.c_str()); - if(!iofmt){ - ERROR("AmAudioFileFormat::getSubtype: file format '%s' does not exist\n", - name.c_str()); - throw string("AmAudioFileFormat::getSubtype: file format '%s' does not exist\n"); - } - else { - p_subtype = AmPlugIn::instance()->subtype(iofmt,subtype); - if(!p_subtype) - ERROR("AmAudioFileFormat::getSubtype: subtype %i in format '%s' does not exist\n", - subtype,iofmt->name); - subtype = p_subtype->type; - } - } - return p_subtype; -} - amci_codec_t* AmAudioFormat::getCodec() { @@ -299,7 +267,7 @@ void AmAudio::close() // returns bytes read, else -1 if error (0 is OK) int AmAudio::get(unsigned int user_ts, unsigned char* buffer, unsigned int nb_samples) { - int size = samples2bytes(nb_samples); + int size = calcBytesToRead(nb_samples); size = read(user_ts,size); //DBG("size = %d\n",size); @@ -428,9 +396,9 @@ unsigned int AmAudio::getFrameSize() return fmt->frame_size; } -unsigned int AmAudio::samples2bytes(unsigned int nb_samples) const +unsigned int AmAudio::calcBytesToRead(unsigned int nb_samples) const { - return fmt->samples2bytes(nb_samples); + return fmt->calcBytesToRead(nb_samples); } unsigned int AmAudio::bytes2samples(unsigned int bytes) const @@ -469,326 +437,6 @@ void DblBuffer::swap() active_buf = !active_buf; } -// returns 0 if everything's OK -// return -1 if error -int AmAudioFile::open(const string& filename, OpenMode mode, bool is_tmp) -{ - close(); - - AmAudioFileFormat* f_fmt = fileName2Fmt(filename); - if(!f_fmt){ - ERROR("while trying to the format of '%s'\n",filename.c_str()); - return -1; - } - fmt.reset(f_fmt); - - open_mode = mode; - this->close_on_exit = true; - - if(!is_tmp){ - fp = fopen(filename.c_str(),mode == AmAudioFile::Read ? "r" : "w+"); - if(!fp){ - if(mode == AmAudioFile::Read) - ERROR("file not found: %s\n",filename.c_str()); - else - ERROR("could not create/overwrite file: %s\n",filename.c_str()); - return -1; - } - } else { - - fp = tmpfile(); - if(!fp){ - ERROR("could not create temporary file: %s\n",strerror(errno)); - } - } - - amci_file_desc_t fd; - int ret = -1; - - if(open_mode == AmAudioFile::Write){ - - if (f_fmt->channels<0 || f_fmt->rate<0) { - if (f_fmt->channels<0) - ERROR("channel count must be set for output file.\n"); - if (f_fmt->rate<0) - ERROR("sampling rate must be set for output file.\n"); - close(); - return -1; - } - } - - fd.subtype = f_fmt->getSubtypeId(); - fd.channels = f_fmt->channels; - fd.rate = f_fmt->rate; - - if( iofmt->open && !(ret = (*iofmt->open)(fp,&fd,mode, f_fmt->getHCodecNoInit())) ) { - if (mode == AmAudioFile::Read) { - f_fmt->setSubtypeId(fd.subtype); - f_fmt->channels = fd.channels; - f_fmt->rate = fd.rate; - data_size = fd.data_size; - } - begin = ftell(fp); - } - else { - if(!iofmt->open) - ERROR("no open function\n"); - else - ERROR("open returned %d\n",ret); - close(); - return ret; - } - - // if(open_mode == AmAudioFile::Write){ - - // DBG("After open:\n"); - // DBG("fmt::subtype = %i\n",f_fmt->getSubtypeId()); - // DBG("fmt::sample = %i\n",f_fmt->sample); - // DBG("fmt::channels = %i\n",f_fmt->channels); - // DBG("fmt::rate = %i\n",f_fmt->rate); - // } - - return ret; -} - -int AmAudioFile::fpopen(const string& filename, OpenMode mode, FILE* n_fp) -{ - close(); - - AmAudioFileFormat* f_fmt = fileName2Fmt(filename); - if(!f_fmt){ - ERROR("while trying to the format of '%s'\n",filename.c_str()); - return -1; - } - fmt.reset(f_fmt); - - open_mode = mode; - fp = n_fp; - fseek(fp,0L,SEEK_SET); - - amci_file_desc_t fd; - int ret = -1; - - if(open_mode == AmAudioFile::Write){ - - if (f_fmt->channels<0 || f_fmt->rate<0) { - if (f_fmt->channels<0) - ERROR("channel count must be set for output file.\n"); - if (f_fmt->rate<0) - ERROR("sampling rate must be set for output file.\n"); - close(); - return -1; - } - } - - fd.subtype = f_fmt->getSubtypeId(); - fd.channels = f_fmt->channels; - fd.rate = f_fmt->rate; - - if( iofmt->open && !(ret = (*iofmt->open)(fp,&fd,mode, f_fmt->getHCodecNoInit())) ) { - if (mode == AmAudioFile::Read) { - f_fmt->setSubtypeId(fd.subtype); - f_fmt->channels = fd.channels; - f_fmt->rate = fd.rate; - data_size = fd.data_size; - } - begin = ftell(fp); - } - else { - if(!iofmt->open) - ERROR("no open function\n"); - else - ERROR("open returned %d\n",ret); - close(); - return ret; - } - - // if(open_mode == AmAudioFile::Write){ - - // DBG("After open:\n"); - // DBG("fmt::subtype = %i\n",f_fmt->getSubtypeId()); - // DBG("fmt::channels = %i\n",f_fmt->channels); - // DBG("fmt::rate = %i\n",f_fmt->rate); - // } - - return ret; -} - - -AmAudioFile::AmAudioFile() - : AmAudio(), data_size(0), - fp(0), begin(0), loop(false), - on_close_done(false), - close_on_exit(true) -{ -} - -AmAudioFile::~AmAudioFile() -{ - close(); -} - -void AmAudioFile::rewind() -{ - fseek(fp,begin,SEEK_SET); -} - -void AmAudioFile::on_close() -{ - if(fp && !on_close_done){ - - AmAudioFileFormat* f_fmt = - dynamic_cast(fmt.get()); - - if(f_fmt){ - amci_file_desc_t fmt_desc = { f_fmt->getSubtypeId(), - f_fmt->rate, - f_fmt->channels, - data_size }; - - if(!iofmt){ - ERROR("file format pointer not initialized: on_close will not be called\n"); - } - else if(iofmt->on_close) - (*iofmt->on_close)(fp,&fmt_desc,open_mode, fmt->getHCodecNoInit(), fmt->getCodec()); - } - - if(open_mode == AmAudioFile::Write){ - - DBG("After close:\n"); - DBG("fmt::subtype = %i\n",f_fmt->getSubtypeId()); - DBG("fmt::channels = %i\n",f_fmt->channels); - DBG("fmt::rate = %i\n",f_fmt->rate); - } - - on_close_done = true; - } -} - - -void AmAudioFile::close() -{ - if(fp){ - on_close(); - - if(close_on_exit) - fclose(fp); - fp = 0; - } -} - -string AmAudioFile::getMimeType() -{ - if(!iofmt) - return ""; - - return iofmt->email_content_type; -} - - -int AmAudioFile::read(unsigned int user_ts, unsigned int size) -{ - if(!fp){ - ERROR("AmAudioFile::read: file is not opened\n"); - return -1; - } - - int ret; - int s = size; - - read_block: - long fpos = ftell(fp); - if(fpos - begin < data_size){ - - if(fpos - begin + (int)size > data_size){ - s = data_size - fpos + begin; - } - - s = fread((void*)((unsigned char*)samples),1,s,fp); - ret = (!ferror(fp) ? s : -1); - -#if __BYTE_ORDER == __BIG_ENDIAN -#define bswap_16(A) ((((u_int16_t)(A) & 0xff00) >> 8) | \ - (((u_int16_t)(A) & 0x00ff) << 8)) - - unsigned int i; - for(i=0;i<=size/2;i++) { - u_int16_t *tmp; - ((u_int16_t *)((unsigned char*)samples))[i]=bswap_16(((u_int16_t *)((unsigned char*)samples))[i]); - } - -#endif - } - else { - if(loop.get() && data_size>0){ - DBG("rewinding audio file...\n"); - rewind(); - goto read_block; - } - - ret = -2; // eof - } - - if(ret > 0 && s > 0 && (unsigned int)s < size){ - DBG("0-stuffing packet: adding %i bytes (packet size=%i)\n",size-s,size); - memset((unsigned char*)samples + s,0,size-s); - return size; - } - - return ret; -} - -int AmAudioFile::write(unsigned int user_ts, unsigned int size) -{ - if(!fp){ - ERROR("AmAudioFile::write: file is not opened\n"); - return -1; - } - - int s = fwrite((void*)((unsigned char*)samples),1,size,fp); - if(s>0) - data_size += s; - return (!ferror(fp) ? s : -1); -} - -int AmAudioFile::getLength() -{ - if (!data_size || !fmt.get()) - return 0; - - return - fmt->bytes2samples(data_size) / - (fmt->rate/1000); -} - -AmAudioFileFormat* AmAudioFile::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); -} - - -int AmAudioFileFormat::getCodecId() -{ - if(!name.empty()){ - getSubtype(); - if(p_subtype) - return p_subtype->codec_id; - } - - return -1; -} int AmAudioRtpFormat::getCodecId() diff --git a/core/AmAudio.h b/core/AmAudio.h index 93765e8e..e2c7af2e 100644 --- a/core/AmAudio.h +++ b/core/AmAudio.h @@ -123,7 +123,7 @@ public: long getHCodec(); long getHCodecNoInit() { return h_codec; } // do not initialize - unsigned int samples2bytes(unsigned int) const; + unsigned int calcBytesToRead(unsigned int needed_samples) const; unsigned int bytes2samples(unsigned int) const; /** @return true if same format. */ @@ -160,52 +160,6 @@ public: AmAudioSimpleFormat(int codec_id); }; -/** \brief \ref AmAudioFormat for file */ -class AmAudioFileFormat: public AmAudioFormat -{ - /** == "" if not yet initialized. */ - string name; - - /** == -1 if not yet initialized. */ - int subtype; - - /** == 0 if not yet initialized. */ - amci_subtype_t* p_subtype; - -protected: - int getCodecId(); - -public: - /** - * Constructor for file based formats. - * All information are taken from the plug-in description. - * @param name The file format name (ex. "Wav"). - * @param subtype Subtype for the file format (see amci.h). - */ - AmAudioFileFormat(const string& name, int subtype = -1); - /** - * Constructor for file based formats. - * All information are taken from the file descriptor. - * @param name The file format name (ex. "Wav"). - * @param fd A file descriptor filled by the a plug-in's open function. - */ - AmAudioFileFormat(const string& name, const amci_file_desc_t* fd); - - /** @return Format name. */ - string getName() { return name; } - /** @return Format subtype. */ - int getSubtypeId() { return subtype; } - /** @return Subtype pointer. */ - amci_subtype_t* getSubtype(); - - void setSubtypeId(int subtype_id) - { - destroyCodec(); - subtype = subtype_id; - p_subtype = 0; - codec = getCodec(); - } -}; /** \brief RTP audio format */ class AmAudioRtpFormat: public AmAudioFormat @@ -294,9 +248,9 @@ protected: unsigned int downMix(unsigned int size); /** - * Convert the size from samples to bytes, depending on the format. + * Get the number of bytes to read from encoded, depending on the format. */ - unsigned int samples2bytes(unsigned int nb_samples) const; + unsigned int calcBytesToRead(unsigned int needed_samples) const; /** * Convert the size from bytes to samples, depending on the format. @@ -335,97 +289,6 @@ public: int incRecordTime(unsigned int samples); }; -/** - * \brief AmAudio implementation for file access - */ -class AmAudioFile: public AmAudio -{ -public: - /** Open mode. */ - enum OpenMode { Read=1, Write=2 }; - -protected: - /** Pointer to the file opened as last. */ - FILE* fp; - long begin; - - /** Format of that file. @see fp, open(). */ - amci_inoutfmt_t* iofmt; - /** Open mode. */ - int open_mode; - - /** Size of datas having been read/written until now. */ - int data_size; - - bool on_close_done; - bool close_on_exit; - - /** @see AmAudio::read */ - int read(unsigned int user_ts, unsigned int size); - - /** @see AmAudio::write */ - int write(unsigned int user_ts, unsigned int size); - - /** @return a file format from file name. (ex: '1234.wav') */ - AmAudioFileFormat* fileName2Fmt(const string& name); - -public: - AmSharedVar loop; - - AmAudioFile(); - ~AmAudioFile(); - - /** - * Opens a file. - * - * @param filename Name of the file. - * @param mode Open mode. - * @return 0 if everything's OK - * @see OpenMode - */ - int open(const string& filename, OpenMode mode, - bool is_tmp=false); - - int fpopen(const string& filename, OpenMode mode, FILE* n_fp); - - /** - * Rewind the file. - */ - void rewind(); - - /** Closes the file. */ - void close(); - - /** Executes the handler's on_close. */ - void on_close(); - - /** be carefull with this one ;-) */ - FILE* getfp() { return fp; } - - OpenMode getMode() { return (OpenMode)open_mode; } - - /** Gets data size in the current file */ - int getDataSize() { return data_size; } - - /** Gets length of the current file in ms */ - int getLength(); - - /** - * @return MIME type corresponding to the audio file. - */ - string getMimeType(); - - void setCloseOnDestroy(bool cod){ - close_on_exit = cod; - } -}; #endif diff --git a/core/AmAudioFile.cpp b/core/AmAudioFile.cpp new file mode 100644 index 00000000..e68b4104 --- /dev/null +++ b/core/AmAudioFile.cpp @@ -0,0 +1,402 @@ +/* + * $Id: AmAudio.cpp 633 2008-01-28 18:17:36Z sayer $ + * + * 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 "AmAudioFile.h" +#include "AmPlugIn.h" +#include "AmUtils.h" + +#include + +AmAudioFileFormat::AmAudioFileFormat(const string& name, int subtype) + : name(name), subtype(subtype), p_subtype(0) +{ + getSubtype(); + codec = getCodec(); + + if(p_subtype && codec){ + rate = p_subtype->sample_rate; + channels = p_subtype->channels; + subtype = p_subtype->type; + } +} + +void AmAudioFileFormat::setSubtypeId(int subtype_id) { + if (subtype != subtype_id) { + DBG("changing file subtype to ID %d\n", subtype_id); + destroyCodec(); + subtype = subtype_id; + p_subtype = 0; + codec = getCodec(); + } +} + +amci_subtype_t* AmAudioFileFormat::getSubtype() +{ + if(!p_subtype && !name.empty()){ + + amci_inoutfmt_t* iofmt = AmPlugIn::instance()->fileFormat(name.c_str()); + if(!iofmt){ + ERROR("AmAudioFileFormat::getSubtype: file format '%s' does not exist\n", + name.c_str()); + throw string("AmAudioFileFormat::getSubtype: file format '%s' does not exist\n"); + } + else { + p_subtype = AmPlugIn::instance()->subtype(iofmt,subtype); + if(!p_subtype) + ERROR("AmAudioFileFormat::getSubtype: subtype %i in format '%s' does not exist\n", + subtype,iofmt->name); + subtype = p_subtype->type; + } + } + return p_subtype; +} + + +AmAudioFileFormat* AmAudioFile::fileName2Fmt(const string& name) +{ + string ext = file_extension(name); + if(ext == ""){ + ERROR("fileName2Fmt: file name has no extension (%s)\n",name.c_str()); + return NULL; + } + + iofmt = AmPlugIn::instance()->fileFormat("",ext); + if(!iofmt){ + ERROR("fileName2Fmt: could not find a format with that extension: '%s'\n",ext.c_str()); + return NULL; + } + + return new AmAudioFileFormat(iofmt->name); +} + + +int AmAudioFileFormat::getCodecId() +{ + if(!name.empty()){ + getSubtype(); + if(p_subtype) + return p_subtype->codec_id; + } + + return -1; +} + + + +// returns 0 if everything's OK +// return -1 if error +int AmAudioFile::open(const string& filename, OpenMode mode, bool is_tmp) +{ + close(); + + AmAudioFileFormat* f_fmt = fileName2Fmt(filename); + if(!f_fmt){ + ERROR("while trying to the format of '%s'\n",filename.c_str()); + return -1; + } + fmt.reset(f_fmt); + + open_mode = mode; + this->close_on_exit = true; + + if(!is_tmp){ + fp = fopen(filename.c_str(),mode == AmAudioFile::Read ? "r" : "w+"); + if(!fp){ + if(mode == AmAudioFile::Read) + ERROR("file not found: %s\n",filename.c_str()); + else + ERROR("could not create/overwrite file: %s\n",filename.c_str()); + return -1; + } + } else { + + fp = tmpfile(); + if(!fp){ + ERROR("could not create temporary file: %s\n",strerror(errno)); + } + } + + amci_file_desc_t fd; + int ret = -1; + + if(open_mode == AmAudioFile::Write){ + + if (f_fmt->channels<0 || f_fmt->rate<0) { + if (f_fmt->channels<0) + ERROR("channel count must be set for output file.\n"); + if (f_fmt->rate<0) + ERROR("sampling rate must be set for output file.\n"); + close(); + return -1; + } + } + + fd.subtype = f_fmt->getSubtypeId(); + fd.channels = f_fmt->channels; + fd.rate = f_fmt->rate; + + if( iofmt->open && !(ret = (*iofmt->open)(fp, &fd, mode, f_fmt->getHCodecNoInit())) ) { + + if (mode == AmAudioFile::Read) { + f_fmt->setSubtypeId(fd.subtype); + f_fmt->channels = fd.channels; + f_fmt->rate = fd.rate; + data_size = fd.data_size; + } + begin = ftell(fp); + } + else { + if(!iofmt->open) + ERROR("no open function\n"); + else + ERROR("open returned %d\n",ret); + close(); + return ret; + } + + // if(open_mode == AmAudioFile::Write){ + + // DBG("After open:\n"); + // DBG("fmt::subtype = %i\n",f_fmt->getSubtypeId()); + // DBG("fmt::sample = %i\n",f_fmt->sample); + // DBG("fmt::channels = %i\n",f_fmt->channels); + // DBG("fmt::rate = %i\n",f_fmt->rate); + // } + + return ret; +} + +int AmAudioFile::fpopen(const string& filename, OpenMode mode, FILE* n_fp) +{ + close(); + + AmAudioFileFormat* f_fmt = fileName2Fmt(filename); + if(!f_fmt){ + ERROR("while trying to the format of '%s'\n",filename.c_str()); + return -1; + } + fmt.reset(f_fmt); + + open_mode = mode; + fp = n_fp; + fseek(fp,0L,SEEK_SET); + + amci_file_desc_t fd; + int ret = -1; + + if(open_mode == AmAudioFile::Write){ + + if (f_fmt->channels<0 || f_fmt->rate<0) { + if (f_fmt->channels<0) + ERROR("channel count must be set for output file.\n"); + if (f_fmt->rate<0) + ERROR("sampling rate must be set for output file.\n"); + close(); + return -1; + } + } + + fd.subtype = f_fmt->getSubtypeId(); + fd.channels = f_fmt->channels; + fd.rate = f_fmt->rate; + + if( iofmt->open && !(ret = (*iofmt->open)(fp,&fd,mode, f_fmt->getHCodecNoInit())) ) { + if (mode == AmAudioFile::Read) { + f_fmt->setSubtypeId(fd.subtype); + f_fmt->channels = fd.channels; + f_fmt->rate = fd.rate; + data_size = fd.data_size; + } + begin = ftell(fp); + } + else { + if(!iofmt->open) + ERROR("no open function\n"); + else + ERROR("open returned %d\n",ret); + close(); + return ret; + } + + // if(open_mode == AmAudioFile::Write){ + + // DBG("After open:\n"); + // DBG("fmt::subtype = %i\n",f_fmt->getSubtypeId()); + // DBG("fmt::channels = %i\n",f_fmt->channels); + // DBG("fmt::rate = %i\n",f_fmt->rate); + // } + + return ret; +} + + +AmAudioFile::AmAudioFile() + : AmAudio(), data_size(0), + fp(0), begin(0), loop(false), + on_close_done(false), + close_on_exit(true) +{ +} + +AmAudioFile::~AmAudioFile() +{ + close(); +} + +void AmAudioFile::rewind() +{ + fseek(fp,begin,SEEK_SET); +} + +void AmAudioFile::on_close() +{ + if(fp && !on_close_done){ + + AmAudioFileFormat* f_fmt = + dynamic_cast(fmt.get()); + + if(f_fmt){ + amci_file_desc_t fmt_desc = { f_fmt->getSubtypeId(), + f_fmt->rate, + f_fmt->channels, + data_size }; + + if(!iofmt){ + ERROR("file format pointer not initialized: on_close will not be called\n"); + } + else if(iofmt->on_close) + (*iofmt->on_close)(fp,&fmt_desc,open_mode, fmt->getHCodecNoInit(), fmt->getCodec()); + } + + if(open_mode == AmAudioFile::Write){ + + DBG("After close:\n"); + DBG("fmt::subtype = %i\n",f_fmt->getSubtypeId()); + DBG("fmt::channels = %i\n",f_fmt->channels); + DBG("fmt::rate = %i\n",f_fmt->rate); + } + + on_close_done = true; + } +} + + +void AmAudioFile::close() +{ + if(fp){ + on_close(); + + if(close_on_exit) + fclose(fp); + fp = 0; + } +} + +string AmAudioFile::getMimeType() +{ + if(!iofmt) + return ""; + + return iofmt->email_content_type; +} + + +int AmAudioFile::read(unsigned int user_ts, unsigned int size) +{ + if(!fp){ + ERROR("AmAudioFile::read: file is not opened\n"); + return -1; + } + + int ret; + int s = size; + + read_block: + long fpos = ftell(fp); + if(data_size < 0 || fpos - begin < data_size){ + + if((data_size > 0) && (fpos - begin + (int)size > data_size)){ + s = data_size - fpos + begin; + } + + s = fread((void*)((unsigned char*)samples),1,s,fp); + + ret = (!ferror(fp) ? s : -1); + +#if __BYTE_ORDER == __BIG_ENDIAN +#define bswap_16(A) ((((u_int16_t)(A) & 0xff00) >> 8) | \ + (((u_int16_t)(A) & 0x00ff) << 8)) + + unsigned int i; + for(i=0;i<=size/2;i++) { + u_int16_t *tmp; + ((u_int16_t *)((unsigned char*)samples))[i]=bswap_16(((u_int16_t *)((unsigned char*)samples))[i]); + } + +#endif + } + else { + if(loop.get() && data_size>0){ + DBG("rewinding audio file...\n"); + rewind(); + goto read_block; + } + + ret = -2; // eof + } + + if(ret > 0 && s > 0 && (unsigned int)s < size){ + DBG("0-stuffing packet: adding %i bytes (packet size=%i)\n",size-s,size); + memset((unsigned char*)samples + s,0,size-s); + return size; + } + + return ret; +} + +int AmAudioFile::write(unsigned int user_ts, unsigned int size) +{ + if(!fp){ + ERROR("AmAudioFile::write: file is not opened\n"); + return -1; + } + + int s = fwrite((void*)((unsigned char*)samples),1,size,fp); + if(s>0) + data_size += s; + return (!ferror(fp) ? s : -1); +} + +int AmAudioFile::getLength() +{ + if (!data_size || !fmt.get()) + return 0; + + return + fmt->bytes2samples(data_size) / + (fmt->rate/1000); +} diff --git a/core/AmAudioFile.h b/core/AmAudioFile.h new file mode 100644 index 00000000..8246a7c3 --- /dev/null +++ b/core/AmAudioFile.h @@ -0,0 +1,170 @@ +/* + * $Id: AmAudio.h 633 2008-01-28 18:17:36Z sayer $ + * + * 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 + */ +/** @file AmAudioFile.h */ +#ifndef _AmAudioFile_h_ +#define _AmAudioFile_h_ + +#include "AmAudio.h" + +/** \brief \ref AmAudioFormat for file */ +class AmAudioFileFormat: public AmAudioFormat +{ + /** == "" if not yet initialized. */ + string name; + + /** == -1 if not yet initialized. */ + int subtype; + + /** == 0 if not yet initialized. */ + amci_subtype_t* p_subtype; + +protected: + int getCodecId(); + +public: + /** + * Constructor for file based formats. + * All information are taken from the plug-in description. + * @param name The file format name (ex. "Wav"). + * @param subtype Subtype for the file format (see amci.h). + */ + AmAudioFileFormat(const string& name, int subtype = -1); + /** + * Constructor for file based formats. + * All information are taken from the file descriptor. + * @param name The file format name (ex. "Wav"). + * @param fd A file descriptor filled by the a plug-in's open function. + */ + AmAudioFileFormat(const string& name, const amci_file_desc_t* fd); + + /** @return Format name. */ + string getName() { return name; } + /** @return Format subtype. */ + int getSubtypeId() { return subtype; } + /** @return Subtype pointer. */ + amci_subtype_t* getSubtype(); + + void setSubtypeId(int subtype_id); +}; + +/** + * \brief AmAudio implementation for file access + */ +class AmAudioFile: public AmAudio +{ +public: + /** Open mode. */ + enum OpenMode { Read=1, Write=2 }; + +protected: + /** Pointer to the file opened as last. */ + FILE* fp; + long begin; + + /** Format of that file. @see fp, open(). */ + amci_inoutfmt_t* iofmt; + /** Open mode. */ + int open_mode; + + /** Size of datas having been read/written until now. */ + int data_size; + + bool on_close_done; + bool close_on_exit; + + /** @see AmAudio::read */ + int read(unsigned int user_ts, unsigned int size); + + /** @see AmAudio::write */ + int write(unsigned int user_ts, unsigned int size); + + /** @return a file format from file name. (ex: '1234.wav') */ + AmAudioFileFormat* fileName2Fmt(const string& name); + +public: + AmSharedVar loop; + + AmAudioFile(); + ~AmAudioFile(); + + /** + * Opens a file. + *
    + *
  • In read mode: sets input format. + *
  • In write mode:
      + *
    1. needs output format set. + *
    2. If file name already exists, + * the file will be overwritten. + *
    + *
+ * @param filename Name of the file. + * @param mode Open mode. + * @return 0 if everything's OK + * @see OpenMode + */ + int open(const string& filename, OpenMode mode, + bool is_tmp=false); + + int fpopen(const string& filename, OpenMode mode, FILE* n_fp); + + /** + * Rewind the file. + */ + void rewind(); + + /** Closes the file. */ + void close(); + + /** Executes the handler's on_close. */ + void on_close(); + + /** be carefull with this one ;-) */ + FILE* getfp() { return fp; } + + OpenMode getMode() { return (OpenMode)open_mode; } + + /** Gets data size in the current file */ + int getDataSize() { return data_size; } + + /** Gets length of the current file in ms */ + int getLength(); + + /** + * @return MIME type corresponding to the audio file. + */ + string getMimeType(); + + void setCloseOnDestroy(bool cod){ + close_on_exit = cod; + } +}; + +#endif + +// Local Variables: +// mode:C++ +// End: diff --git a/core/AmAudioMixIn.h b/core/AmAudioMixIn.h index 0f9aac2d..24034fb4 100644 --- a/core/AmAudioMixIn.h +++ b/core/AmAudioMixIn.h @@ -29,6 +29,7 @@ #define _AM_AUDIO_MIX_IN_H #include "AmAudio.h" +#include "AmAudioFile.h" #define MAX_PACKETLENGTH_MS 30 diff --git a/core/AmCachedAudioFile.h b/core/AmCachedAudioFile.h index 371c5fd9..9de47638 100644 --- a/core/AmCachedAudioFile.h +++ b/core/AmCachedAudioFile.h @@ -28,7 +28,7 @@ #ifndef _AMFILECACHE_H #define _AMFILECACHE_H -#include "AmAudio.h" +#include "AmAudioFile.h" #include diff --git a/core/AmPlaylist.cpp b/core/AmPlaylist.cpp index 9d959a25..89c5e7f0 100644 --- a/core/AmPlaylist.cpp +++ b/core/AmPlaylist.cpp @@ -80,7 +80,7 @@ int AmPlaylist::get(unsigned int user_ts, unsigned char* buffer, unsigned int nb } if(!cur_item || !cur_item->play) { - ret = samples2bytes(nb_samples); + ret = calcBytesToRead(nb_samples); memset(buffer,0,ret); } diff --git a/core/AmPlugIn.cpp b/core/AmPlugIn.cpp index 44442046..236dde8d 100644 --- a/core/AmPlugIn.cpp +++ b/core/AmPlugIn.cpp @@ -450,6 +450,13 @@ int AmPlugIn::loadAudioPlugIn(amci_exports_t* exports) return -1; } + if (exports->module_load) { + if (exports->module_load() < 0) { + ERROR("initializing audio plug-in!\n"); + return -1; + } + } + for( amci_codec_t* c=exports->codecs; c->id>=0; c++ ){ diff --git a/core/amci/amci.h b/core/amci/amci.h index a791ef15..9cd2d604 100644 --- a/core/amci/amci.h +++ b/core/amci/amci.h @@ -90,7 +90,7 @@ struct amci_file_desc_t { /** # channels */ int channels; - /** size of the data */ + /** size of the data. -1 for unknown/unlimited */ int data_size; }; @@ -98,7 +98,7 @@ struct amci_file_desc_t { * \brief Sound converter function pointer. * @param out [out] output buffer * @param in [in] input buffer - * @param size [in] size of input buffer + * @param size [in] size of input buffer * @param channels [in] number of channels * @param rate [in] sampling rate * @param h_codec [in] codec handle @@ -208,6 +208,19 @@ typedef int (*amci_file_mem_close_t)( unsigned char* mptr, struct amci_codec_t *codec ); +/** + * \brief Codec module 's init function pointer. + * this function initializes the codec module. + * @return 0 on success, <0 on error + */ +typedef int (*amci_codec_module_load_t)(void); + +/** + * \brief Codec's module's destroy function pointer. + */ +typedef void (*amci_codec_module_destroy_t)(void); + + /** * \brief Codec's init function pointer. * @@ -217,7 +230,7 @@ typedef int (*amci_file_mem_close_t)( unsigned char* mptr, * AMCI_FMT_FRAME_LENGTH (1) frame length in ms (for framed codecs; must be multiple of 10) * AMCI_FMT_FRAME_SIZE (2) frame size in samples * AMCI_FMT_ENCODED_FRAME_SIZE (3) encoded frame size - * @return -1 if failed, else some handler which will be + * @return -1 if failed, else some handle which will be * passed by each further call (0 is legal). * */ @@ -262,7 +275,7 @@ struct amci_codec_t { /** Does the opposite of encode. */ amci_converter_t decode; - /** Codec specific packet loss concealment. */ + /** Codec specific packet loss concealment. can be NULL */ amci_plc_t plc; /** Init function. can be NULL. */ @@ -382,6 +395,11 @@ struct amci_exports_t { /** Module name */ char* name; + /** Codec module load function. can be NULL */ + amci_codec_module_load_t module_load; + /** Codec module destroy function. can be NULL */ + amci_codec_module_destroy_t module_destroy; + /** NULL terminated array of amci_codec_t */ struct amci_codec_t* codecs; /** NULL terminated array of amci_payload_t */ @@ -397,9 +415,12 @@ struct amci_exports_t { * see example media plug-in 'wav' (plug-in/wav/wav.c). * @hideinitializer */ -#define BEGIN_EXPORTS(name) \ +#define BEGIN_EXPORTS(name, module_load, module_destroy) \ struct amci_exports_t amci_exports = { \ - name, + name,\ + module_load,\ + module_destroy, + /** * Portable export definition macro @@ -431,7 +452,7 @@ struct amci_exports_t { * see example media plug-in 'wav' (plug-in/wav/wav.c). * @hideinitializer */ -#define CODEC(id,intern2type,type2intern,plc,init,destroy,bytes2samples,samples2bytes) \ +#define CODEC(id, intern2type,type2intern,plc,init,destroy,bytes2samples,samples2bytes) \ { id, intern2type, type2intern, plc, init, destroy, bytes2samples, samples2bytes }, /** @@ -520,6 +541,13 @@ struct amci_exports_t { { type, name, rate, channels, codec_id }, + /* defines to make definitions more expressive */ +#define AMCI_NO_MODULEINIT NULL +#define AMCI_NO_MODULEDESTROY NULL +#define AMCI_NO_CODEC_PLC NULL +#define AMCI_NO_CODECCREATE NULL +#define AMCI_NO_CODECDESTROY NULL + #ifdef __cplusplus } #endif diff --git a/core/plug-in/adpcm/adpcm.c b/core/plug-in/adpcm/adpcm.c index 0fa64c04..920ebac6 100644 --- a/core/plug-in/adpcm/adpcm.c +++ b/core/plug-in/adpcm/adpcm.c @@ -77,16 +77,16 @@ static unsigned int g726_40_bytes2samples(long, unsigned int); static unsigned int g726_40_samples2bytes(long, unsigned int); -BEGIN_EXPORTS( "adpcm" ) +BEGIN_EXPORTS( "adpcm", AMCI_NO_MODULEINIT, AMCI_NO_MODULEDESTROY) BEGIN_CODECS -CODEC( CODEC_G726_16, Pcm16_2_G726_16, G726_16_2_Pcm16, NULL, +CODEC( CODEC_G726_16, Pcm16_2_G726_16, G726_16_2_Pcm16, AMCI_NO_CODEC_PLC, G726_create, G726_destroy, g726_16_bytes2samples, g726_16_samples2bytes ) -CODEC( CODEC_G726_24, Pcm16_2_G726_24, G726_24_2_Pcm16, NULL, +CODEC( CODEC_G726_24, Pcm16_2_G726_24, G726_24_2_Pcm16, AMCI_NO_CODEC_PLC, G726_create, G726_destroy, g726_24_bytes2samples, g726_24_samples2bytes ) -CODEC( CODEC_G726_32, Pcm16_2_G726_32, G726_32_2_Pcm16, NULL, +CODEC( CODEC_G726_32, Pcm16_2_G726_32, G726_32_2_Pcm16, AMCI_NO_CODEC_PLC, G726_create, G726_destroy, g726_32_bytes2samples, g726_32_samples2bytes ) -CODEC( CODEC_G726_40, Pcm16_2_G726_40, G726_40_2_Pcm16, NULL, +CODEC( CODEC_G726_40, Pcm16_2_G726_40, G726_40_2_Pcm16, AMCI_NO_CODEC_PLC, G726_create, G726_destroy, g726_40_bytes2samples, g726_40_samples2bytes ) END_CODECS diff --git a/core/plug-in/gsm/gsm.c b/core/plug-in/gsm/gsm.c index 7e854a49..805ef788 100644 --- a/core/plug-in/gsm/gsm.c +++ b/core/plug-in/gsm/gsm.c @@ -46,10 +46,10 @@ static void gsm_destroy_if(long h_codec); static unsigned int gsm_bytes2samples(long, unsigned int); static unsigned int gsm_samples2bytes(long, unsigned int); -BEGIN_EXPORTS( "gsm" ) +BEGIN_EXPORTS( "gsm", AMCI_NO_MODULEINIT, AMCI_NO_MODULEDESTROY ) BEGIN_CODECS -CODEC( CODEC_GSM0610, pcm16_2_gsm, gsm_2_pcm16, NULL, +CODEC( CODEC_GSM0610, pcm16_2_gsm, gsm_2_pcm16, AMCI_NO_CODEC_PLC, gsm_create_if, (amci_codec_destroy_t)gsm_destroy_if, gsm_bytes2samples, gsm_samples2bytes ) END_CODECS diff --git a/core/plug-in/ilbc/ilbc.c b/core/plug-in/ilbc/ilbc.c index 966cb3ad..27a14999 100644 --- a/core/plug-in/ilbc/ilbc.c +++ b/core/plug-in/ilbc/ilbc.c @@ -77,7 +77,7 @@ static int iLBC_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, static unsigned int ilbc_bytes2samples(long, unsigned int); static unsigned int ilbc_samples2bytes(long, unsigned int); -BEGIN_EXPORTS( "ilbc" ) +BEGIN_EXPORTS( "ilbc" , AMCI_NO_MODULEINIT, AMCI_NO_MODULEDESTROY ) BEGIN_CODECS CODEC( CODEC_ILBC, Pcm16_2_iLBC, iLBC_2_Pcm16, iLBC_PLC, diff --git a/core/plug-in/l16/l16.c b/core/plug-in/l16/l16.c index cb5c72ba..65bdb4d2 100644 --- a/core/plug-in/l16/l16.c +++ b/core/plug-in/l16/l16.c @@ -52,10 +52,12 @@ static int L16_2_Pcm16(unsigned char* out_buf, unsigned char* in_buf, unsigned i static unsigned int L16_bytes2samples(long, unsigned int); static unsigned int L16_samples2bytes(long, unsigned int); -BEGIN_EXPORTS( "l16" ) +BEGIN_EXPORTS( "l16" , AMCI_NO_MODULEINIT, AMCI_NO_MODULEDESTROY ) BEGIN_CODECS -CODEC( CODEC_L16, Pcm16_2_L16, L16_2_Pcm16, NULL, NULL, NULL, L16_bytes2samples, L16_samples2bytes ) +CODEC( CODEC_L16, Pcm16_2_L16, L16_2_Pcm16, + AMCI_NO_CODEC_PLC, AMCI_NO_CODECCREATE, AMCI_NO_CODECDESTROY, + L16_bytes2samples, L16_samples2bytes ) END_CODECS BEGIN_PAYLOADS diff --git a/core/plug-in/speex/speex.c b/core/plug-in/speex/speex.c index f43a127c..79096161 100644 --- a/core/plug-in/speex/speex.c +++ b/core/plug-in/speex/speex.c @@ -80,10 +80,11 @@ void speexNB_destroy(long handle); static unsigned int speexNB_bytes2samples(long, unsigned int); static unsigned int speexNB_samples2bytes(long, unsigned int); -BEGIN_EXPORTS("speex") +BEGIN_EXPORTS("speex", AMCI_NO_MODULEINIT, AMCI_NO_MODULEDESTROY) BEGIN_CODECS -CODEC(CODEC_SPEEX_NB, Pcm16_2_SpeexNB, SpeexNB_2_Pcm16, NULL, speexNB_create, speexNB_destroy, +CODEC(CODEC_SPEEX_NB, Pcm16_2_SpeexNB, SpeexNB_2_Pcm16, AMCI_NO_CODEC_PLC, + speexNB_create, speexNB_destroy, speexNB_bytes2samples, speexNB_samples2bytes) END_CODECS diff --git a/core/plug-in/wav/wav.c b/core/plug-in/wav/wav.c index 71aa1091..bec28bfc 100644 --- a/core/plug-in/wav/wav.c +++ b/core/plug-in/wav/wav.c @@ -44,11 +44,11 @@ * * Example declaration: @code - BEGIN_EXPORTS( "wav" ) + BEGIN_EXPORTS( "wav", AMCI_NO_MODULEINIT, AMCI_NO_MODULEDESTROY) BEGIN_CODECS - CODEC( CODEC_ULAW, Pcm16_2_ULaw, ULaw_2_Pcm16, 0, 0 ) - CODEC( CODEC_ALAW, Pcm16_2_ALaw, ALaw_2_Pcm16, 0, 0 ) + CODEC( CODEC_ULAW, Pcm16_2_ULaw, ULaw_2_Pcm16, 0, 0, 0, 0, 0 ) + CODEC( CODEC_ALAW, Pcm16_2_ALaw, ALaw_2_Pcm16, 0, 0, 0, 0, 0 ) END_CODECS BEGIN_PAYLOADS @@ -92,11 +92,15 @@ static int Pcm16_2_ALaw( unsigned char* out_buf, unsigned char* in_buf, unsigned static unsigned int g711_bytes2samples(long, unsigned int); static unsigned int g711_samples2bytes(long, unsigned int); -BEGIN_EXPORTS( "wav" ) +BEGIN_EXPORTS( "wav" , AMCI_NO_MODULEINIT, AMCI_NO_MODULEDESTROY ) BEGIN_CODECS -CODEC( CODEC_ULAW, Pcm16_2_ULaw, ULaw_2_Pcm16, NULL, NULL, NULL, g711_bytes2samples, g711_samples2bytes ) - CODEC( CODEC_ALAW, Pcm16_2_ALaw, ALaw_2_Pcm16, NULL, NULL, NULL, g711_bytes2samples, g711_samples2bytes ) +CODEC( CODEC_ULAW, Pcm16_2_ULaw, ULaw_2_Pcm16, + AMCI_NO_CODEC_PLC, AMCI_NO_CODECCREATE, AMCI_NO_CODECDESTROY, + g711_bytes2samples, g711_samples2bytes ) + CODEC( CODEC_ALAW, Pcm16_2_ALaw, ALaw_2_Pcm16, + AMCI_NO_CODEC_PLC, AMCI_NO_CODECCREATE, AMCI_NO_CODECDESTROY, + g711_bytes2samples, g711_samples2bytes ) END_CODECS BEGIN_PAYLOADS