26801b648d0268068f03c34cbc3c2f5bc3d78ec4
[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 struct ast_format_list *f_list = ast_get_format_list(&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_set(&pref->formats[x], f_list[to[x]-1].id , 0);
61                 }
62         }
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         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 struct ast_format_list *f_list = ast_get_format_list(&f_len);
120
121         if (!pref->order[0])
122                 return;
123
124         memcpy(&oldorder, pref, sizeof(oldorder));
125         memset(pref, 0, sizeof(*pref));
126
127         for (x = 0; x < f_len; x++) {
128                 if (!oldorder.order[x])
129                         break;
130                 if (f_list[oldorder.order[x]-1].id != format->id) {
131                         pref->order[y] = oldorder.order[x];
132                         ast_format_copy(&pref->formats[y], &oldorder.formats[x]);
133                         pref->framing[y++] = oldorder.framing[x];
134                 }
135         }
136 }
137
138 /*! \brief Append codec to list */
139 int ast_codec_pref_append(struct ast_codec_pref *pref, struct ast_format *format)
140 {
141         int x, newindex = 0;
142         size_t f_len = 0;
143         const struct ast_format_list *f_list = ast_get_format_list(&f_len);
144
145         ast_codec_pref_remove(pref, format);
146
147         for (x = 0; x < f_len; x++) {
148                 if (f_list[x].id == format->id) {
149                         newindex = x + 1;
150                         break;
151                 }
152         }
153
154         if (newindex) {
155                 for (x = 0; x < f_len; x++) {
156                         if (!pref->order[x]) {
157                                 pref->order[x] = newindex;
158                                 ast_format_copy(&pref->formats[x], format);
159                                 break;
160                         }
161                 }
162         }
163
164         return x;
165 }
166
167 /*! \brief Prepend codec to list */
168 void ast_codec_pref_prepend(struct ast_codec_pref *pref, struct ast_format *format, int only_if_existing)
169 {
170         int x, newindex = 0;
171         size_t f_len = 0;
172         const struct ast_format_list *f_list = ast_get_format_list(&f_len);
173
174         /* First step is to get the codecs "index number" */
175         for (x = 0; x < f_len; x++) {
176                 if (f_list[x].id == format->id) {
177                         newindex = x + 1;
178                         break;
179                 }
180         }
181         /* Done if its unknown */
182         if (!newindex)
183                 return;
184
185         /* Now find any existing occurrence, or the end */
186         for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
187                 if (!pref->order[x] || pref->order[x] == newindex)
188                         break;
189         }
190
191         if (only_if_existing && !pref->order[x])
192                 return;
193
194         /* Move down to make space to insert - either all the way to the end,
195            or as far as the existing location (which will be overwritten) */
196         for (; x > 0; x--) {
197                 pref->order[x] = pref->order[x - 1];
198                 pref->framing[x] = pref->framing[x - 1];
199                 ast_format_copy(&pref->formats[x], &pref->formats[x - 1]);
200         }
201
202         /* And insert the new entry */
203         pref->order[0] = newindex;
204         pref->framing[0] = 0; /* ? */
205         ast_format_copy(&pref->formats[0], format);
206 }
207
208 /*! \brief Set packet size for codec */
209 int ast_codec_pref_setsize(struct ast_codec_pref *pref, struct ast_format *format, int framems)
210 {
211         int x, idx = -1;
212         size_t f_len = 0;
213         const struct ast_format_list *f_list = ast_get_format_list(&f_len);
214
215         for (x = 0; x < f_len; x++) {
216                 if (f_list[x].id == format->id) {
217                         idx = x;
218                         break;
219                 }
220         }
221
222         if (idx < 0)
223                 return -1;
224
225         /* size validation */
226         if (!framems)
227                 framems = f_list[idx].def_ms;
228
229         if (f_list[idx].inc_ms && framems % f_list[idx].inc_ms) /* avoid division by zero */
230                 framems -= framems % f_list[idx].inc_ms;
231
232         if (framems < f_list[idx].min_ms)
233                 framems = f_list[idx].min_ms;
234
235         if (framems > f_list[idx].max_ms)
236                 framems = f_list[idx].max_ms;
237
238         for (x = 0; x < f_len; x++) {
239                 if (pref->order[x] == (idx + 1)) {
240                         pref->framing[x] = framems;
241                         break;
242                 }
243         }
244
245         return x;
246 }
247
248 /*! \brief Get packet size for codec */
249 struct ast_format_list ast_codec_pref_getsize(struct ast_codec_pref *pref, struct ast_format *format)
250 {
251         int x, idx = -1, framems = 0;
252         struct ast_format_list fmt = { 0, };
253         size_t f_len = 0;
254         const struct ast_format_list *f_list = ast_get_format_list(&f_len);
255
256         for (x = 0; x < f_len; x++) {
257                 if (f_list[x].id == format->id) {
258                         fmt = f_list[x];
259                         idx = x;
260                         break;
261                 }
262         }
263
264         for (x = 0; x < f_len; x++) {
265                 if (pref->order[x] == (idx + 1)) {
266                         framems = pref->framing[x];
267                         break;
268                 }
269         }
270
271         /* size validation */
272         if (!framems)
273                 framems = f_list[idx].def_ms;
274
275         if (f_list[idx].inc_ms && framems % f_list[idx].inc_ms) /* avoid division by zero */
276                 framems -= framems % f_list[idx].inc_ms;
277
278         if (framems < f_list[idx].min_ms)
279                 framems = f_list[idx].min_ms;
280
281         if (framems > f_list[idx].max_ms)
282                 framems = f_list[idx].max_ms;
283
284         fmt.cur_ms = framems;
285
286         return fmt;
287 }
288
289 /*! \brief Pick a codec */
290 struct ast_format *ast_codec_choose(struct ast_codec_pref *pref, struct ast_format_cap *cap, int find_best, struct ast_format *result)
291 {
292         int x, slot, found;
293         size_t f_len = 0;
294         struct ast_format tmp_fmt;
295
296         const struct ast_format_list *f_list = ast_get_format_list(&f_len);
297
298         ast_format_clear(result);
299
300         for (x = 0; x < f_len; x++) {
301                 slot = pref->order[x];
302
303                 if (!slot)
304                         break;
305                 if (ast_format_cap_iscompatible(cap, ast_format_set(&tmp_fmt, f_list[slot-1].id, 0))) {
306                         found = 1; /*format is found and stored in tmp_fmt */
307                         break;
308                 }
309         }
310         if (found && (AST_FORMAT_GET_TYPE(tmp_fmt.id) == AST_FORMAT_TYPE_AUDIO)) {
311                 ast_format_copy(result, &tmp_fmt);
312                 return result;
313         }
314
315         ast_debug(4, "Could not find preferred codec - %s\n", find_best ? "Going for the best codec" : "Returning zero codec");
316
317         return find_best ? ast_best_codec(cap, result) : NULL;
318 }
319
320