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