ARI: Add ability to raise arbitrary User Events
[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 /*** MODULEINFO
25         <support_level>core</support_level>
26  ***/
27
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
31
32 #include "asterisk/_private.h"
33 #include "asterisk/frame.h"
34 #include "asterisk/channel.h"
35 #include "asterisk/utils.h"
36
37 void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, int right)
38 {
39         size_t f_len;
40         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
41         int x, differential = (int) 'A', mem;
42         char *from, *to;
43
44         /* TODO re-evaluate this function.  It is using the order of the formats specified
45          * in the global format list in a way that may not be safe. */
46         if (right) {
47                 from = pref->order;
48                 to = buf;
49                 mem = size;
50         } else {
51                 to = pref->order;
52                 from = buf;
53                 mem = AST_CODEC_PREF_SIZE;
54         }
55
56         memset(to, 0, mem);
57         for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
58                 if (!from[x]) {
59                         break;
60                 }
61                 to[x] = right ? (from[x] + differential) : (from[x] - differential);
62                 if (!right && to[x] && (to[x] < f_len)) {
63                         ast_format_copy(&pref->formats[x], &f_list[to[x]-1].format);
64                 }
65         }
66         ast_format_list_destroy(f_list);
67 }
68
69 int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size)
70 {
71         int x;
72         struct ast_format format;
73         size_t total_len, slen;
74         const char *formatname;
75
76         memset(buf, 0, size);
77         total_len = size;
78         buf[0] = '(';
79         total_len--;
80         for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
81                 if (total_len <= 0)
82                         break;
83                 if (!(ast_codec_pref_index(pref, x, &format)))
84                         break;
85                 if ((formatname = ast_getformatname(&format))) {
86                         slen = strlen(formatname);
87                         if (slen > total_len)
88                                 break;
89                         strncat(buf, formatname, total_len - 1); /* safe */
90                         total_len -= slen;
91                 }
92                 if (total_len && x < AST_CODEC_PREF_SIZE - 1 && ast_codec_pref_index(pref, x + 1, &format)) {
93                         strncat(buf, "|", total_len - 1); /* safe */
94                         total_len--;
95                 }
96         }
97         if (total_len) {
98                 strncat(buf, ")", total_len - 1); /* safe */
99                 total_len--;
100         }
101
102         return size - total_len;
103 }
104
105 struct ast_format *ast_codec_pref_index(struct ast_codec_pref *pref, int idx, struct ast_format *result)
106 {
107         if ((idx >= 0) && (idx < sizeof(pref->order)) && pref->formats[idx].id) {
108                 ast_format_copy(result, &pref->formats[idx]);
109         } else {
110                 ast_format_clear(result);
111                 return NULL;
112         }
113
114         return result;
115 }
116
117 /*! \brief Remove codec from pref list */
118 void ast_codec_pref_remove(struct ast_codec_pref *pref, struct ast_format *format)
119 {
120         struct ast_codec_pref oldorder;
121         int x, y = 0;
122         size_t f_len = 0;
123         const struct ast_format_list *f_list;
124
125         if (!pref->order[0]) {
126                 return;
127         }
128
129         f_list = ast_format_list_get(&f_len);
130         memcpy(&oldorder, pref, sizeof(oldorder));
131         memset(pref, 0, sizeof(*pref));
132
133         for (x = 0; x < f_len; x++) {
134                 if (!oldorder.order[x]) {
135                         break;
136                 }
137                 if (ast_format_cmp(&f_list[oldorder.order[x]-1].format, format) == AST_FORMAT_CMP_NOT_EQUAL) {
138                         pref->order[y] = oldorder.order[x];
139                         ast_format_copy(&pref->formats[y], &oldorder.formats[x]);
140                         pref->framing[y++] = oldorder.framing[x];
141                 }
142         }
143         ast_format_list_destroy(f_list);
144 }
145
146 /*! \brief Append all codecs to a preference list, without distrubing existing order */
147 void ast_codec_pref_append_all(struct ast_codec_pref *pref)
148 {
149         int x, y, found;
150         size_t f_len = 0;
151         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
152
153         /* leave any existing entries, and don't create duplicates (e.g. allow=ulaw,all) */
154         for (x = 0; x < f_len; x++) {
155                 /* x = codec to add */
156                 found = 0;
157                 for (y = 0; y < f_len; y++) {
158                         /* y = scan of existing preferences */
159                         if (!pref->order[y]) {
160                                 break;
161                         }
162                         if (x + 1 == pref->order[y]) {
163                                 found = 1;
164                                 break;
165                         }
166                 }
167                 if (found) {
168                         continue;
169                 }
170                 for (; y < f_len; y++) {
171                         /* add x to the end of y */
172                         if (!pref->order[y])
173                         {
174                                 pref->order[y] = x + 1;
175                                 ast_format_copy(&pref->formats[y], &f_list[x].format);
176                                 break;
177                         }
178                 }
179         }
180 }
181
182 /*! \brief Append codec to list */
183 int ast_codec_pref_append(struct ast_codec_pref *pref, struct ast_format *format)
184 {
185         int x, newindex = 0;
186         size_t f_len = 0;
187         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
188
189         ast_codec_pref_remove(pref, format);
190
191         for (x = 0; x < f_len; x++) {
192                 if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) {
193                         newindex = x + 1;
194                         break;
195                 }
196         }
197
198         if (newindex) {
199                 for (x = 0; x < f_len; x++) {
200                         if (!pref->order[x]) {
201                                 pref->order[x] = newindex;
202                                 ast_format_copy(&pref->formats[x], format);
203                                 break;
204                         }
205                 }
206         }
207
208         ast_format_list_destroy(f_list);
209         return x;
210 }
211
212 /*! \brief Prepend codec to list */
213 void ast_codec_pref_prepend(struct ast_codec_pref *pref, struct ast_format *format, int only_if_existing)
214 {
215         int x, newindex = 0;
216         size_t f_len = 0;
217         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
218
219         /* First step is to get the codecs "index number" */
220         for (x = 0; x < f_len; x++) {
221                 if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) {
222                         newindex = x + 1;
223                         break;
224                 }
225         }
226         /* Done if its unknown */
227         if (!newindex) {
228                 ast_format_list_destroy(f_list);
229                 return;
230         }
231
232         /* Now find any existing occurrence, or the end */
233         for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
234                 if (!pref->order[x] || pref->order[x] == newindex)
235                         break;
236         }
237
238         /* If we failed to find any occurrence, set to the end */
239         if (x == AST_CODEC_PREF_SIZE) {
240                 --x;
241         }
242
243         if (only_if_existing && !pref->order[x]) {
244                 ast_format_list_destroy(f_list);
245                 return;
246         }
247
248         /* Move down to make space to insert - either all the way to the end,
249            or as far as the existing location (which will be overwritten) */
250         for (; x > 0; x--) {
251                 pref->order[x] = pref->order[x - 1];
252                 pref->framing[x] = pref->framing[x - 1];
253                 ast_format_copy(&pref->formats[x], &pref->formats[x - 1]);
254         }
255
256         /* And insert the new entry */
257         pref->order[0] = newindex;
258         pref->framing[0] = 0; /* ? */
259         ast_format_copy(&pref->formats[0], format);
260         ast_format_list_destroy(f_list);
261 }
262
263 /*! \brief Set packet size for codec */
264 int ast_codec_pref_setsize(struct ast_codec_pref *pref, struct ast_format *format, int framems)
265 {
266         int x, idx = -1;
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                         idx = x;
273                         break;
274                 }
275         }
276
277         if (idx < 0) {
278                 ast_format_list_destroy(f_list);
279                 return -1;
280         }
281
282         /* size validation */
283         if (!framems)
284                 framems = f_list[idx].def_ms;
285
286         if (f_list[idx].inc_ms && framems % f_list[idx].inc_ms) /* avoid division by zero */
287                 framems -= framems % f_list[idx].inc_ms;
288
289         if (framems < f_list[idx].min_ms)
290                 framems = f_list[idx].min_ms;
291
292         if (framems > f_list[idx].max_ms)
293                 framems = f_list[idx].max_ms;
294
295         for (x = 0; x < f_len; x++) {
296                 if (pref->order[x] == (idx + 1)) {
297                         pref->framing[x] = framems;
298                         break;
299                 }
300         }
301
302         ast_format_list_destroy(f_list);
303         return x;
304 }
305
306 /*! \brief Get packet size for codec */
307 struct ast_format_list ast_codec_pref_getsize(struct ast_codec_pref *pref, struct ast_format *format)
308 {
309         int x, idx = -1, framems = 0;
310         struct ast_format_list fmt = { { 0, }, };
311         size_t f_len = 0;
312         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
313
314         for (x = 0; x < f_len; x++) {
315                 if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) {
316                         fmt = f_list[x];
317                         idx = x;
318                         break;
319                 }
320         }
321
322         if (idx < 0) {
323                 ast_log(AST_LOG_WARNING, "Format %s unknown; unable to get preferred codec packet size\n", ast_getformatname(format));
324                 ast_format_list_destroy(f_list);
325                 return fmt;
326         }
327
328         for (x = 0; x < f_len; x++) {
329                 if (pref->order[x] == (idx + 1)) {
330                         framems = pref->framing[x];
331                         break;
332                 }
333         }
334
335         /* size validation */
336         if (!framems)
337                 framems = f_list[idx].def_ms;
338
339         if (f_list[idx].inc_ms && framems % f_list[idx].inc_ms) /* avoid division by zero */
340                 framems -= framems % f_list[idx].inc_ms;
341
342         if (framems < f_list[idx].min_ms)
343                 framems = f_list[idx].min_ms;
344
345         if (framems > f_list[idx].max_ms)
346                 framems = f_list[idx].max_ms;
347
348         fmt.cur_ms = framems;
349         ast_format_list_destroy(f_list);
350         return fmt;
351 }
352
353 /*! \brief Pick a codec */
354 struct ast_format *ast_codec_choose(struct ast_codec_pref *pref, struct ast_format_cap *cap, int find_best, struct ast_format *result)
355 {
356         int x, slot, found = 0;
357         size_t f_len = 0;
358         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
359
360         for (x = 0; x < f_len; x++) {
361                 slot = pref->order[x];
362
363                 if (!slot)
364                         break;
365                 if (ast_format_cap_get_compatible_format(cap, &f_list[slot-1].format, result)) {
366                         found = 1; /*format is found and stored in result */
367                         break;
368                 }
369         }
370         ast_format_list_destroy(f_list);
371         if (found && (AST_FORMAT_GET_TYPE(result->id) == AST_FORMAT_TYPE_AUDIO)) {
372                 return result;
373         }
374         ast_format_clear(result);
375         ast_debug(4, "Could not find preferred codec - %s\n", find_best ? "Going for the best codec" : "Returning zero codec");
376
377         return find_best ? ast_best_codec(cap, result) : NULL;
378 }
379
380