mirror of https://github.com/sipwise/sems.git
o added codec module init/destroy functions o fixed re-creating codec instance on file open o added unlimited data_size option on RO files git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@708 8eb893ce-cfd4-0310-b710-fb5ebe64c474sayer/1.4-spce2.6
parent
42ab9d76e7
commit
20e1dd182f
@ -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 <string.h>
|
||||
|
||||
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<AmAudioFileFormat*>(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);
|
||||
}
|
||||
@ -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<bool> loop;
|
||||
|
||||
AmAudioFile();
|
||||
~AmAudioFile();
|
||||
|
||||
/**
|
||||
* Opens a file.
|
||||
* <ul>
|
||||
* <li>In read mode: sets input format.
|
||||
* <li>In write mode: <ol>
|
||||
* <li>needs output format set.
|
||||
* <li>If file name already exists,
|
||||
* the file will be overwritten.
|
||||
* </ol>
|
||||
* </ul>
|
||||
* @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:
|
||||
Loading…
Reference in new issue