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