Media Project Phase2: SILK 8khz-24khz, SLINEAR 8khz-192khz, SPEEX 32khz, hd audio...
[asterisk/asterisk.git] / main / format_pref.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief Format Preference API
22  */
23
24 #include "asterisk.h"
25
26 ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
27
28 #include "asterisk/_private.h"
29 #include "asterisk/version.h"
30 #include "asterisk/frame.h"
31 #include "asterisk/channel.h"
32 #include "asterisk/utils.h"
33
34 void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, int right)
35 {
36         size_t f_len;
37         const const struct ast_format_list *f_list = ast_format_list_get(&f_len);
38         int x, differential = (int) 'A', mem;
39         char *from, *to;
40
41         /* TODO re-evaluate this function.  It is using the order of the formats specified
42          * in the global format list in a way that may not be safe. */
43         if (right) {
44                 from = pref->order;
45                 to = buf;
46                 mem = size;
47         } else {
48                 to = pref->order;
49                 from = buf;
50                 mem = AST_CODEC_PREF_SIZE;
51         }
52
53         memset(to, 0, mem);
54         for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
55                 if (!from[x]) {
56                         break;
57                 }
58                 to[x] = right ? (from[x] + differential) : (from[x] - differential);
59                 if (!right && to[x] && (to[x] < f_len)) {
60                         ast_format_copy(&pref->formats[x], &f_list[to[x]-1].format);
61                 }
62         }
63         ast_format_list_destroy(f_list);
64 }
65
66 int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size)
67 {
68         int x;
69         struct ast_format format;
70         size_t total_len, slen;
71         const char *formatname;
72
73         memset(buf, 0, size);
74         total_len = size;
75         buf[0] = '(';
76         total_len--;
77         for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
78                 if (total_len <= 0)
79                         break;
80                 if (!(ast_codec_pref_index(pref, x, &format)))
81                         break;
82                 if ((formatname = ast_getformatname(&format))) {
83                         slen = strlen(formatname);
84                         if (slen > total_len)
85                                 break;
86                         strncat(buf, formatname, total_len - 1); /* safe */
87                         total_len -= slen;
88                 }
89                 if (total_len && x < AST_CODEC_PREF_SIZE - 1 && ast_codec_pref_index(pref, x + 1, &format)) {
90                         strncat(buf, "|", total_len - 1); /* safe */
91                         total_len--;
92                 }
93         }
94         if (total_len) {
95                 strncat(buf, ")", total_len - 1); /* safe */
96                 total_len--;
97         }
98
99         return size - total_len;
100 }
101
102 struct ast_format *ast_codec_pref_index(struct ast_codec_pref *pref, int idx, struct ast_format *result)
103 {
104         if ((idx >= 0) && (idx < sizeof(pref->order)) && pref->formats[idx].id) {
105                 ast_format_copy(result, &pref->formats[idx]);
106         } else {
107                 ast_format_clear(result);
108                 return NULL;
109         }
110
111         return result;
112 }
113
114 /*! \brief Remove codec from pref list */
115 void ast_codec_pref_remove(struct ast_codec_pref *pref, struct ast_format *format)
116 {
117         struct ast_codec_pref oldorder;
118         int x, y = 0;
119         size_t f_len = 0;
120         const const struct ast_format_list *f_list;
121
122         if (!pref->order[0]) {
123                 return;
124         }
125
126         f_list = ast_format_list_get(&f_len);
127         memcpy(&oldorder, pref, sizeof(oldorder));
128         memset(pref, 0, sizeof(*pref));
129
130         for (x = 0; x < f_len; x++) {
131                 if (!oldorder.order[x]) {
132                         break;
133                 }
134                 if (ast_format_cmp(&f_list[oldorder.order[x]-1].format, format) == AST_FORMAT_CMP_NOT_EQUAL) {
135                         pref->order[y] = oldorder.order[x];
136                         ast_format_copy(&pref->formats[y], &oldorder.formats[x]);
137                         pref->framing[y++] = oldorder.framing[x];
138                 }
139         }
140         ast_format_list_destroy(f_list);
141 }
142
143 /*! \brief Append codec to list */
144 int ast_codec_pref_append(struct ast_codec_pref *pref, struct ast_format *format)
145 {
146         int x, newindex = 0;
147         size_t f_len = 0;
148         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
149
150         ast_codec_pref_remove(pref, format);
151
152         for (x = 0; x < f_len; x++) {
153                 if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) {
154                         newindex = x + 1;
155                         break;
156                 }
157         }
158
159         if (newindex) {
160                 for (x = 0; x < f_len; x++) {
161                         if (!pref->order[x]) {
162                                 pref->order[x] = newindex;
163                                 ast_format_copy(&pref->formats[x], format);
164                                 break;
165                         }
166                 }
167         }
168
169         ast_format_list_destroy(f_list);
170         return x;
171 }
172
173 /*! \brief Prepend codec to list */
174 void ast_codec_pref_prepend(struct ast_codec_pref *pref, struct ast_format *format, int only_if_existing)
175 {
176         int x, newindex = 0;
177         size_t f_len = 0;
178         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
179
180         /* First step is to get the codecs "index number" */
181         for (x = 0; x < f_len; x++) {
182                 if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) {
183                         newindex = x + 1;
184                         break;
185                 }
186         }
187         /* Done if its unknown */
188         if (!newindex) {
189                 ast_format_list_destroy(f_list);
190                 return;
191         }
192
193         /* Now find any existing occurrence, or the end */
194         for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
195                 if (!pref->order[x] || pref->order[x] == newindex)
196                         break;
197         }
198
199         if (only_if_existing && !pref->order[x]) {
200                 ast_format_list_destroy(f_list);
201                 return;
202         }
203
204         /* Move down to make space to insert - either all the way to the end,
205            or as far as the existing location (which will be overwritten) */
206         for (; x > 0; x--) {
207                 pref->order[x] = pref->order[x - 1];
208                 pref->framing[x] = pref->framing[x - 1];
209                 ast_format_copy(&pref->formats[x], &pref->formats[x - 1]);
210         }
211
212         /* And insert the new entry */
213         pref->order[0] = newindex;
214         pref->framing[0] = 0; /* ? */
215         ast_format_copy(&pref->formats[0], format);
216         ast_format_list_destroy(f_list);
217 }
218
219 /*! \brief Set packet size for codec */
220 int ast_codec_pref_setsize(struct ast_codec_pref *pref, struct ast_format *format, int framems)
221 {
222         int x, idx = -1;
223         size_t f_len = 0;
224         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
225
226         for (x = 0; x < f_len; x++) {
227                 if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) {
228                         idx = x;
229                         break;
230                 }
231         }
232
233         if (idx < 0) {
234                 ast_format_list_destroy(f_list);
235                 return -1;
236         }
237
238         /* size validation */
239         if (!framems)
240                 framems = f_list[idx].def_ms;
241
242         if (f_list[idx].inc_ms && framems % f_list[idx].inc_ms) /* avoid division by zero */
243                 framems -= framems % f_list[idx].inc_ms;
244
245         if (framems < f_list[idx].min_ms)
246                 framems = f_list[idx].min_ms;
247
248         if (framems > f_list[idx].max_ms)
249                 framems = f_list[idx].max_ms;
250
251         for (x = 0; x < f_len; x++) {
252                 if (pref->order[x] == (idx + 1)) {
253                         pref->framing[x] = framems;
254                         break;
255                 }
256         }
257
258         ast_format_list_destroy(f_list);
259         return x;
260 }
261
262 /*! \brief Get packet size for codec */
263 struct ast_format_list ast_codec_pref_getsize(struct ast_codec_pref *pref, struct ast_format *format)
264 {
265         int x, idx = -1, framems = 0;
266         struct ast_format_list fmt = { { 0, }, };
267         size_t f_len = 0;
268         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
269
270         for (x = 0; x < f_len; x++) {
271                 if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) {
272                         fmt = f_list[x];
273                         idx = x;
274                         break;
275                 }
276         }
277
278         for (x = 0; x < f_len; x++) {
279                 if (pref->order[x] == (idx + 1)) {
280                         framems = pref->framing[x];
281                         break;
282                 }
283         }
284
285         /* size validation */
286         if (!framems)
287                 framems = f_list[idx].def_ms;
288
289         if (f_list[idx].inc_ms && framems % f_list[idx].inc_ms) /* avoid division by zero */
290                 framems -= framems % f_list[idx].inc_ms;
291
292         if (framems < f_list[idx].min_ms)
293                 framems = f_list[idx].min_ms;
294
295         if (framems > f_list[idx].max_ms)
296                 framems = f_list[idx].max_ms;
297
298         fmt.cur_ms = framems;
299         ast_format_list_destroy(f_list);
300         return fmt;
301 }
302
303 /*! \brief Pick a codec */
304 struct ast_format *ast_codec_choose(struct ast_codec_pref *pref, struct ast_format_cap *cap, int find_best, struct ast_format *result)
305 {
306         int x, slot, found;
307         size_t f_len = 0;
308         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
309
310         for (x = 0; x < f_len; x++) {
311                 slot = pref->order[x];
312
313                 if (!slot)
314                         break;
315                 if (ast_format_cap_get_compatible_format(cap, &f_list[slot-1].format, result)) {
316                         found = 1; /*format is found and stored in result */
317                         break;
318                 }
319         }
320         ast_format_list_destroy(f_list);
321         if (found && (AST_FORMAT_GET_TYPE(result->id) == AST_FORMAT_TYPE_AUDIO)) {
322                 return result;
323         }
324         ast_format_clear(result);
325         ast_debug(4, "Could not find preferred codec - %s\n", find_best ? "Going for the best codec" : "Returning zero codec");
326
327         return find_best ? ast_best_codec(cap, result) : NULL;
328 }
329
330