res/res_format_attr_silk: Expose format attributes to other modules
[asterisk/asterisk.git] / res / res_format_attr_silk.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2011, Digium, Inc.
5  *
6  * David Vossel <dvossel@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 SILK format attribute interface
22  *
23  * \author David Vossel <dvossel@digium.com>
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_REGISTER_FILE()
33
34 #include "asterisk/module.h"
35 #include "asterisk/format.h"
36
37 /*!
38  * \brief SILK attribute structure.
39  *
40  * \note The only attribute that affects compatibility here is the sample rate.
41  */
42 struct silk_attr {
43         unsigned int samplerate;
44         unsigned int maxbitrate;
45         unsigned int dtx;
46         unsigned int fec;
47         unsigned int packetloss_percentage;
48 };
49
50 static void silk_destroy(struct ast_format *format)
51 {
52         struct silk_attr *attr = ast_format_get_attribute_data(format);
53
54         ast_free(attr);
55 }
56
57 static int silk_clone(const struct ast_format *src, struct ast_format *dst)
58 {
59         struct silk_attr *original = ast_format_get_attribute_data(src);
60         struct silk_attr *attr = ast_calloc(1, sizeof(*attr));
61
62         if (!attr) {
63                 return -1;
64         }
65
66         if (original) {
67                 *attr = *original;
68         }
69
70         ast_format_set_attribute_data(dst, attr);
71
72         return 0;
73 }
74
75 static struct ast_format *silk_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
76 {
77         struct ast_format *cloned;
78         struct silk_attr *attr;
79         unsigned int val;
80
81         cloned = ast_format_clone(format);
82         if (!cloned) {
83                 return NULL;
84         }
85         attr = ast_format_get_attribute_data(cloned);
86
87         if (sscanf(attributes, "maxaveragebitrate=%30u", &val) == 1) {
88                 attr->maxbitrate = val;
89         }
90         if (sscanf(attributes, "usedtx=%30u", &val) == 1) {
91                 attr->dtx = val;
92         }
93         if (sscanf(attributes, "useinbandfec=%30u", &val) == 1) {
94                 attr->fec = val;
95         }
96
97         return cloned;
98 }
99
100 static void silk_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
101 {
102         struct silk_attr *attr = ast_format_get_attribute_data(format);
103
104         if (!attr) {
105                 return;
106         }
107
108         if ((attr->maxbitrate > 5000) && (attr->maxbitrate < 40000)) { 
109                 ast_str_append(str, 0, "a=fmtp:%u maxaveragebitrate=%u\r\n", payload, attr->maxbitrate);
110         }
111
112         ast_str_append(str, 0, "a=fmtp:%u usedtx=%u\r\n", payload, attr->dtx);
113         ast_str_append(str, 0, "a=fmtp:%u useinbandfec=%u\r\n", payload, attr->fec);
114 }
115
116 static enum ast_format_cmp_res silk_cmp(const struct ast_format *format1, const struct ast_format *format2)
117 {
118         struct silk_attr *attr1 = ast_format_get_attribute_data(format1);
119         struct silk_attr *attr2 = ast_format_get_attribute_data(format2);
120
121         if (((!attr1 || !attr1->samplerate) && (!attr2 || !attr2->samplerate)) ||
122                 (attr1->samplerate == attr2->samplerate)) {
123                 return AST_FORMAT_CMP_EQUAL;
124         }
125
126         return AST_FORMAT_CMP_NOT_EQUAL;
127 }
128
129 static struct ast_format *silk_getjoint(const struct ast_format *format1, const struct ast_format *format2)
130 {
131         struct silk_attr *attr1 = ast_format_get_attribute_data(format1);
132         struct silk_attr *attr2 = ast_format_get_attribute_data(format2);
133         unsigned int samplerate;
134         struct ast_format *jointformat;
135         struct silk_attr *attr_res;
136
137         samplerate = attr1->samplerate & attr2->samplerate;
138         /* sample rate is the only attribute that has any bearing on if joint capabilities exist or not */
139         if (samplerate) {
140                 return NULL;
141         }
142
143         jointformat = ast_format_clone(format1);
144         if (!jointformat) {
145                 return NULL;
146         }
147         attr_res = ast_format_get_attribute_data(jointformat);
148         attr_res->samplerate = samplerate;
149
150         /* Take the lowest max bitrate */
151         attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
152
153         /* Only do dtx if both sides want it. DTX is a trade off between
154          * computational complexity and bandwidth. */
155         attr_res->dtx = attr1->dtx && attr2->dtx ? 1 : 0;
156
157         /* Only do FEC if both sides want it.  If a peer specifically requests not
158          * to receive with FEC, it may be a waste of bandwidth. */
159         attr_res->fec = attr1->fec && attr2->fec ? 1 : 0;
160
161         /* Use the maximum packetloss percentage between the two attributes. This affects how
162          * much redundancy is used in the FEC. */
163         attr_res->packetloss_percentage = MAX(attr1->packetloss_percentage, attr2->packetloss_percentage);
164
165         return jointformat;
166 }
167
168 static struct ast_format *silk_set(const struct ast_format *format, const char *name, const char *value)
169 {
170         struct ast_format *cloned;
171         struct silk_attr *attr;
172         unsigned int val;
173
174         if (sscanf(value, "%30u", &val) != 1) {
175                 ast_log(LOG_WARNING, "Unknown value '%s' for attribute type '%s'\n",
176                         value, name);
177                 return NULL;
178         }
179
180         cloned = ast_format_clone(format);
181         if (!cloned) {
182                 return NULL;
183         }
184         attr = ast_format_get_attribute_data(cloned);
185
186         if (!strcasecmp(name, "sample_rate")) {
187                 attr->samplerate = val;
188         } else if (!strcasecmp(name, "max_bitrate")) {
189                 attr->maxbitrate = val;
190         } else if (!strcasecmp(name, "dtx")) {
191                 attr->dtx = val;
192         } else if (!strcasecmp(name, "fec")) {
193                 attr->fec = val;
194         } else if (!strcasecmp(name, "packetloss_percentage")) {
195                 attr->packetloss_percentage = val;
196         } else {
197                 ast_log(LOG_WARNING, "unknown attribute type %s\n", name);
198         }
199
200         return cloned;
201 }
202
203 static const void *silk_get(const struct ast_format *format, const char *name)
204 {
205         struct silk_attr *attr = ast_format_get_attribute_data(format);
206         unsigned int *val;
207
208         if (!strcasecmp(name, "sample_rate")) {
209                 val = &attr->samplerate;
210         } else if (!strcasecmp(name, "max_bitrate")) {
211                 val = &attr->maxbitrate;
212         } else if (!strcasecmp(name, "dtx")) {
213                 val = &attr->dtx;
214         } else if (!strcasecmp(name, "fec")) {
215                 val = &attr->fec;
216         } else if (!strcasecmp(name, "packetloss_percentage")) {
217                 val = &attr->packetloss_percentage;
218         } else {
219                 ast_log(LOG_WARNING, "unknown attribute type %s\n", name);
220                 return NULL;
221         }
222
223         return val;
224 }
225
226 static struct ast_format_interface silk_interface = {
227         .format_destroy = silk_destroy,
228         .format_clone = silk_clone,
229         .format_cmp = silk_cmp,
230         .format_get_joint = silk_getjoint,
231         .format_attribute_set = silk_set,
232         .format_attribute_get = silk_get,
233         .format_parse_sdp_fmtp = silk_parse_sdp_fmtp,
234         .format_generate_sdp_fmtp = silk_generate_sdp_fmtp,
235 };
236
237 static int load_module(void)
238 {
239         if (ast_format_interface_register("silk", &silk_interface)) {
240                 return AST_MODULE_LOAD_DECLINE;
241         }
242
243         return AST_MODULE_LOAD_SUCCESS;
244 }
245
246 static int unload_module(void)
247 {
248         return 0;
249 }
250
251 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SILK Format Attribute Module",
252         .support_level = AST_MODULE_SUPPORT_CORE,
253         .load = load_module,
254         .unload = unload_module,
255         .load_pri = AST_MODPRI_CHANNEL_DEPEND,
256 );