1 /* 2 * Copyright (C) 2007 iptego GmbH 3 * 4 * This file is part of SEMS, a free SIP media server. 5 * 6 * SEMS is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * For a license to use the sems software under conditions 12 * other than those described here, or to purchase support for this 13 * software, please contact iptel.org by e-mail at the following addresses: 14 * info@iptel.org 15 * 16 * SEMS is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24 */ 25 26 #include "AmCachedAudioFile.h" 27 #include "AmUtils.h" 28 #include "log.h" 29 #include "AmPlugIn.h" 30 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <fcntl.h> 34 #include <string.h> 35 #include <sys/mman.h> 36 37 38 39 using std::string; 40 41 AmFileCache::AmFileCache() 42 : data(NULL), 43 data_size(0) 44 { } 45 46 AmFileCache::~AmFileCache() { 47 if ((data != NULL) && 48 munmap(data, data_size)) { 49 ERROR("while unmapping file.\n"); 50 } 51 } 52 53 int AmFileCache::load(const std::string& filename) { 54 int fd; 55 struct stat sbuf; 56 57 name = filename; 58 59 if ((fd = open(name.c_str(), O_RDONLY)) == -1) { 60 ERROR("while opening file '%s' for caching.\n", 61 filename.c_str()); 62 return -1; 63 } 64 65 if (fstat(fd, &sbuf) == -1) { 66 ERROR("cannot stat file '%s'.\n", 67 name.c_str()); 68 close(fd); 69 return -2; 70 } 71 72 if ((data = mmap((caddr_t)0, sbuf.st_size, PROT_READ, MAP_PRIVATE, 73 fd, 0)) == (caddr_t)(-1)) { 74 ERROR("cannot mmap file '%s'.\n", 75 name.c_str()); 76 close(fd); 77 return -3; 78 } 79 80 data_size = sbuf.st_size; 81 close(fd); 82 83 return 0; 84 } 85 86 int AmFileCache::read(void* buf, 87 size_t* pos, 88 size_t size) { 89 90 if (*pos >= data_size) 91 return -1; // eof 92 93 size_t r_size = size; 94 if (*pos+size > data_size) 95 r_size = data_size-*pos; 96 97 if (r_size>0) { 98 memcpy(buf, (unsigned char*)data + *pos, r_size); 99 *pos+=r_size; 100 } 101 return r_size; 102 } 103 104 inline size_t AmFileCache::getSize() { 105 return data_size; 106 } 107 108 inline const string& AmFileCache::getFilename() { 109 return name; 110 } 111 112 113 AmCachedAudioFile::AmCachedAudioFile(AmFileCache* cache) 114 : cache(cache), fpos(0), begin(0), good(false), loop(false) 115 { 116 if (!cache) { 117 ERROR("Need open file cache.\n"); 118 return; 119 } 120 121 AmAudioFileFormat* f_fmt = fileName2Fmt(cache->getFilename()); 122 if(!f_fmt){ 123 ERROR("while trying to determine the format of '%s'\n", 124 cache->getFilename().c_str()); 125 return; 126 } 127 fmt.reset(f_fmt); 128 129 amci_file_desc_t fd; 130 int ret = -1; 131 132 fd.subtype = f_fmt->getSubtypeId(); 133 fd.channels = f_fmt->channels; 134 fd.rate = f_fmt->getRate(); 135 136 long unsigned int ofpos = fpos; 137 138 if( iofmt->mem_open && 139 !(ret = (*iofmt->mem_open)((unsigned char*)cache->getData(),cache->getSize(),&ofpos, 140 &fd,AmAudioFile::Read,f_fmt->getHCodecNoInit())) ) { 141 f_fmt->setSubtypeId(fd.subtype); 142 f_fmt->channels = fd.channels; 143 f_fmt->setRate(fd.rate); 144 145 begin = fpos = ofpos; 146 } 147 else { 148 if(!iofmt->mem_open) 149 ERROR("no mem_open function\n"); 150 else 151 ERROR("mem_open returned %d\n",ret); 152 close(); 153 return; 154 } 155 156 good = true; 157 158 return; 159 } 160 161 AmCachedAudioFile::~AmCachedAudioFile() { 162 } 163 164 AmAudioFileFormat* AmCachedAudioFile::fileName2Fmt(const string& name) 165 { 166 string ext = file_extension(name); 167 if(ext == ""){ 168 ERROR("fileName2Fmt: file name has no extension (%s)",name.c_str()); 169 return NULL; 170 } 171 172 iofmt = AmPlugIn::instance()->fileFormat("",ext); 173 if(!iofmt){ 174 ERROR("fileName2Fmt: could not find a format with that extension: '%s'",ext.c_str()); 175 return NULL; 176 } 177 178 return new AmAudioFileFormat(iofmt->name); 179 } 180 181 void AmCachedAudioFile::rewind() { 182 fpos = begin; 183 } 184 185 /** Closes the file. */ 186 void AmCachedAudioFile::close() { 187 fpos = 0; 188 } 189 190 /** Executes the handler's on_close. */ 191 void on_close() { 192 } 193 194 int AmCachedAudioFile::read(unsigned int user_ts, unsigned int size) { 195 196 if(!good){ 197 ERROR("AmAudioFile::read: file is not opened\n"); 198 return -1; 199 } 200 201 int ret = cache->read((void*)((unsigned char*)samples),&fpos,size); 202 203 //DBG("s = %i; ret = %i\n",s,ret); 204 if(loop.get() && (ret <= 0) && fpos==cache->getSize()){ 205 DBG("rewinding audio file...\n"); 206 rewind(); 207 ret = cache->read((void*)((unsigned char*)samples),&fpos, size); 208 } 209 210 if(ret > 0 && (unsigned int)ret < size){ 211 DBG("0-stuffing packet: adding %i bytes (packet size=%i)\n",size-ret,size); 212 memset((unsigned char*)samples + ret,0,size-ret); 213 return size; 214 } 215 216 return (fpos==cache->getSize() && !loop.get() ? -2 : ret); 217 } 218 219 int AmCachedAudioFile::write(unsigned int user_ts, unsigned int size) { 220 ERROR("AmCachedAudioFile writing not supported!\n"); 221 return -1; 222 } 223 224