1 /*
2  * Copyright (C) 2008 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. This program is released under
10  * the GPL with the additional exemption that compiling, linking,
11  * and/or using OpenSSL is allowed.
12  *
13  * For a license to use the SEMS software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * SEMS is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 
28 #include "AmPrecodedFile.h"
29 #include "AmUtils.h"
30 #include "log.h"
31 #include <fstream>
32 
33 #include <libgen.h>
34 
35 unsigned int precoded_bytes2samples(long h_codec, unsigned int num_bytes) {
36 
37   return ((precoded_payload_t*)h_codec)->frame_ms
38     * ((precoded_payload_t*)h_codec)->sample_rate
39     / 1000;
40 }
41 
42 unsigned int precoded_samples2bytes(long h_codec, unsigned int num_samples) {
43   return ((precoded_payload_t*)h_codec)->frame_bytes;
44 }
45 
46 amci_codec_t _codec_precoded = {
47   PRECODED_CODEC_ID,
48   NULL,
49   NULL,
50   NULL,
51   NULL,
52   NULL,
53   precoded_bytes2samples,
54   precoded_samples2bytes,
55   NULL
56 };
57 
58 void AmPrecodedFile::initPlugin() {
59   AmPlugIn::instance()->addCodec(&_codec_precoded);
AmPayloadProvider()60   for(std::map<int,precoded_payload_t>::iterator it =
61 	payloads.begin(); it != payloads.end(); ++it)
62     AmPlugIn::instance()->addPayload(&it->second);
63 }
64 
65 AmPrecodedRtpFormat::AmPrecodedRtpFormat(precoded_payload_t& precoded_payload)
66   : AmAudioRtpFormat(), precoded_payload(precoded_payload)
67 {
68   channels = precoded_payload.channels;
69   rate = precoded_payload.sample_rate;
70   // frame_size is in samples, precoded_payload.frame_size in millisec
71   //frame_size = precoded_payload.frame_ms * precoded_payload.sample_rate / 1000;
72   //frame_length = precoded_payload.frame_ms;
73   //frame_encoded_size = precoded_payload.frame_bytes;
74   h_codec = (long)&(this->precoded_payload);
75 }
76 
77 AmPrecodedRtpFormat::~AmPrecodedRtpFormat() {
78 }
79 
80 AmPrecodedFileFormat::AmPrecodedFileFormat(precoded_payload_t& precoded_payload)
81   : AmAudioFileFormat(""), precoded_payload(precoded_payload)
82 {
83   subtype.type = 0;
84   subtype.name = precoded_payload.name;
85   subtype.sample_rate = precoded_payload.sample_rate;
86   subtype.channels = precoded_payload.channels;
87   subtype.codec_id = PRECODED_CODEC_ID;
88 
89   channels = precoded_payload.channels;
90   rate = precoded_payload.sample_rate;
91   codec = getCodec();
92 
93   // used in precoded_bytes2samples()/precoded_samples2bytes()
94   //frame_size = precoded_payload.frame_ms * precoded_payload.sample_rate / 1000;
95   //frame_encoded_size = precoded_payload.frame_bytes;
96   h_codec = (long)&(this->precoded_payload);
97 }
98 
99 AmPrecodedFileFormat::~AmPrecodedFileFormat() {
100 }
101 
102 int precoded_file_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec)
103 {
104   fseek (fp, 0, SEEK_END);
105   fmt_desc->data_size=ftell (fp);
106   rewind(fp);
107   DBG("file opened, size = %d\n", fmt_desc->data_size);
108   return 0;
109 }
110 
111 int precoded_file_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options,
112 			long h_codec, struct amci_codec_t *codec)
113 {
114   DBG("file closed\n");
115   return 0;
116 }
117 
118 AmPrecodedFileInstance::AmPrecodedFileInstance(precoded_payload_t& precoded_payload)
119   : AmAudioFile(), precoded_payload(precoded_payload)
120 {
121   memset(&m_iofmt, 0, sizeof(amci_inoutfmt_t));
122   m_iofmt.open = &precoded_file_open;
123   m_iofmt.on_close = &precoded_file_close;
124   iofmt = &m_iofmt;
125 }
126 
127 AmPrecodedFileInstance::~AmPrecodedFileInstance(){
128 }
129 
130 AmAudioFileFormat* AmPrecodedFileInstance::fileName2Fmt(const string& name, const string& subtype) {
131   return new AmPrecodedFileFormat(precoded_payload);
132 }
133 
134 int AmPrecodedFileInstance::open() {
135   return AmAudioFile::open(precoded_payload.filename, AmAudioFile::Read);
136 }
137 
138 AmPrecodedRtpFormat* AmPrecodedFileInstance::getRtpFormat() {
139   return new AmPrecodedRtpFormat(precoded_payload);
140 }
141 
142 AmPrecodedFile::AmPrecodedFile()
143 {
144 }
145 
146 AmPrecodedFile::~AmPrecodedFile() {
147 }
148 
149 int AmPrecodedFile::open(const std::string& filename) {
150   std::ifstream ifs(filename.c_str());
151   if (!ifs.good()) {
152     return -1;
153   }
154 
155   char *dir = strdup(filename.c_str());
156   string str_dir(dirname(dir));
157   str_dir += "/";
158 
159   while (ifs.good() && !ifs.eof()) {
160     string codec_line;
161     getline(ifs, codec_line);
162     if (!codec_line.length() || codec_line[0]=='#')
163       continue;
164 
165     vector<string> codec_def = explode(codec_line, ";");
166     if (codec_def.size() != 8) {
167       ERROR("unable to decipher codec line '%s'\n",
168 	    codec_line.c_str());
169       continue;
170     }
171 
172     precoded_payload_t pl;
173 
174 #define get_uint_item(name, index, description)			\
175     unsigned int name;						\
176     if (str2i(codec_def[index], name)) {			\
177       ERROR("decoding " description " in codec line '%s'\n",	\
178 	    codec_line.c_str());				\
179       continue;							\
180     }								\
181     pl.name = name;
182 
183     get_uint_item(payload_id, 0, "payload_id");
184     pl.c_name = codec_def[1];
185     pl.name = pl.c_name.c_str();
186     get_uint_item(sample_rate, 2, "sample_rate");
187     get_uint_item(channels, 3, "channels");
188     pl.format_parameters = codec_def[4];
189     get_uint_item(frame_ms, 5, "frame ms");
190     get_uint_item(frame_bytes, 6, "frame bytes");
191     pl.filename=str_dir + codec_def[7];
192 #undef get_uint_item
193 
194     DBG("inserting codec '%s' file '%s' and id %d\n",
195 	pl.name, pl.filename.c_str(), pl.payload_id);
196     payloads[pl.payload_id]=pl;
197   }
198   free(dir);
199   ifs.close();
200   return 0; // OK
201 }
202 
203 amci_payload_t* AmPrecodedFile::payload(int payload_id) const {
204   std::map<int,precoded_payload_t>::const_iterator it =
205     payloads.find(payload_id);
206 
207   if(it != payloads.end())
208     return (amci_payload_t*)&it->second;
209 
210   return NULL;
211 }
212 
213 int AmPrecodedFile::getDynPayload(const string& name, int rate, int encoding_param) const {
214   // find a dynamic payload by name/rate and encoding_param (channels, if > 0)
215   for(std::map<int, precoded_payload_t>::const_iterator pl_it = payloads.begin();
216       pl_it != payloads.end(); ++pl_it)
217     if( (name == pl_it->second.name)
218 	&& (rate == pl_it->second.sample_rate) ) {
219       if ((encoding_param > 0) && (pl_it->second.channels > 0) &&
220 	  (encoding_param != pl_it->second.channels))
221 	continue;
222 
223       return pl_it->first;
224     }
225 
226   // not found
227   return -1;
228 }
229 
230 
231 void AmPrecodedFile::getPayloads(vector<SdpPayload>& pl_vec) const
232 {
233   for(std::map<int,precoded_payload_t>::const_iterator pl_it = payloads.begin();
234       pl_it != payloads.end(); ++pl_it) {
235     pl_vec.push_back(SdpPayload(pl_it->first, pl_it->second.name, pl_it->second.sample_rate, 0));
236   }
237 }
238 
239 AmPrecodedFileInstance* AmPrecodedFile::getFileInstance(int payload_id) {
240   std::map<int,precoded_payload_t>::iterator it=payloads.find(payload_id);
241   if (it != payloads.end())
242     return new AmPrecodedFileInstance(it->second);
243   return NULL;
244 }
245