Asterisk - The Open Source Telephony Project  21.4.1
asterisk-opus-a959f072d3f364be983dd27e6e250b038aaef747/res/res_format_attr_opus.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Lorenzo Miniero <lorenzo@meetecho.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 Opus format attribute interface
22  *
23  * \author Lorenzo Miniero <lorenzo@meetecho.com>
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #if defined(ASTERISK_REGISTER_FILE)
33 ASTERISK_REGISTER_FILE()
34 #elif defined(ASTERISK_FILE_VERSION)
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision: $")
36 #endif
37 
38 #include "asterisk/module.h"
39 #include "asterisk/format.h"
40 #include "asterisk/logger.h" /* for ast_log, LOG_WARNING */
41 #include "asterisk/opus.h" /* for CODEC_OPUS_DEFAULT_* */
42 #include "asterisk/strings.h" /* for ast_str_append */
43 #include "asterisk/utils.h" /* for MIN, ast_malloc, ast_free */
44 
45 /*!
46  * \brief Opus attribute structure.
47  *
48  * \note http://tools.ietf.org/html/rfc7587#section-6
49  */
50 struct opus_attr {
51  unsigned int maxbitrate;
52  unsigned int maxplayrate;
53  unsigned int unused; /* was minptime, kept for binary compatibility */
54  unsigned int stereo;
55  unsigned int cbr;
56  unsigned int fec;
57  unsigned int dtx;
58  unsigned int spropmaxcapturerate;
59  unsigned int spropstereo;
60 };
61 
62 static struct opus_attr default_opus_attr = {
63  .maxplayrate = CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE,
64  .spropmaxcapturerate = CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE,
65  .maxbitrate = CODEC_OPUS_DEFAULT_BITRATE,
66  .stereo = CODEC_OPUS_DEFAULT_STEREO,
67  .spropstereo = CODEC_OPUS_DEFAULT_STEREO,
68  .cbr = CODEC_OPUS_DEFAULT_CBR,
69  .fec = CODEC_OPUS_DEFAULT_FEC,
70  .dtx = CODEC_OPUS_DEFAULT_DTX,
71 };
72 
73 static void opus_destroy(struct ast_format *format)
74 {
75  struct opus_attr *attr = ast_format_get_attribute_data(format);
76 
77  ast_free(attr);
78 }
79 
80 static int opus_clone(const struct ast_format *src, struct ast_format *dst)
81 {
82  struct opus_attr *original = ast_format_get_attribute_data(src);
83  struct opus_attr *attr = ast_malloc(sizeof(*attr));
84 
85  if (!attr) {
86  return -1;
87  }
88 
89  if (original) {
90  *attr = *original;
91  } else {
92  *attr = default_opus_attr;
93  }
94 
96 
97  return 0;
98 }
99 
100 static struct ast_format *opus_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
101 {
102  struct ast_format *cloned;
103  struct opus_attr *attr;
104  const char *kvp;
105  unsigned int val;
106 
107  cloned = ast_format_clone(format);
108  if (!cloned) {
109  return NULL;
110  }
111  attr = ast_format_get_attribute_data(cloned);
112 
113  if ((kvp = strstr(attributes, "maxplaybackrate")) && sscanf(kvp, "maxplaybackrate=%30u", &val) == 1) {
114  attr->maxplayrate = val;
115  } else {
116  attr->maxplayrate = 48000;
117  }
118 
119  if ((kvp = strstr(attributes, "sprop-maxcapturerate")) && sscanf(kvp, "sprop-maxcapturerate=%30u", &val) == 1) {
120  attr->spropmaxcapturerate = val;
121  } else {
122  attr->spropmaxcapturerate = 48000;
123  }
124 
125  if ((kvp = strstr(attributes, "maxaveragebitrate")) && sscanf(kvp, "maxaveragebitrate=%30u", &val) == 1) {
126  attr->maxbitrate = val;
127  } else {
128  attr->maxbitrate = 510000;
129  }
130 
131  if (!strncmp(attributes, "stereo=1", 8)) {
132  attr->stereo = 1;
133  } else if (strstr(attributes, " stereo=1")) {
134  attr->stereo = 1;
135  } else if (strstr(attributes, ";stereo=1")) {
136  attr->stereo = 1;
137  } else {
138  attr->stereo = 0;
139  }
140 
141  if (strstr(attributes, "sprop-stereo=1")) {
142  attr->spropstereo = 1;
143  } else {
144  attr->spropstereo = 0;
145  }
146 
147  if (strstr(attributes, "cbr=1")) {
148  attr->cbr = 1;
149  } else {
150  attr->cbr = 0;
151  }
152 
153  if (strstr(attributes, "useinbandfec=1")) {
154  attr->fec = 1;
155  } else {
156  attr->fec = 0;
157  }
158 
159  if (strstr(attributes, "usedtx=1")) {
160  attr->dtx = 1;
161  } else {
162  attr->dtx = 0;
163  }
164 
165  return cloned;
166 }
167 
168 static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
169 {
170  struct opus_attr *attr = ast_format_get_attribute_data(format);
171  int added = 0;
172 
173  if (!attr) {
174  /*
175  * (Only) cached formats do not have attribute data assigned because
176  * they were created before this attribute module was registered.
177  * Therefore, we assume the default attribute values here.
178  */
179  attr = &default_opus_attr;
180  }
181 
182  if (48000 != attr->maxplayrate) {
183  if (added) {
184  ast_str_append(str, 0, ";");
185  } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
186  added = 1;
187  }
188  ast_str_append(str, 0, "maxplaybackrate=%u", attr->maxplayrate);
189  }
190 
191  if (48000 != attr->spropmaxcapturerate) {
192  if (added) {
193  ast_str_append(str, 0, ";");
194  } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
195  added = 1;
196  }
197  ast_str_append(str, 0, "sprop-maxcapturerate=%u", attr->spropmaxcapturerate);
198  }
199 
200  if (510000 != attr->maxbitrate) {
201  if (added) {
202  ast_str_append(str, 0, ";");
203  } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
204  added = 1;
205  }
206  ast_str_append(str, 0, "maxaveragebitrate=%u", attr->maxbitrate);
207  }
208 
209  if (0 != attr->stereo) {
210  if (added) {
211  ast_str_append(str, 0, ";");
212  } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
213  added = 1;
214  }
215  ast_str_append(str, 0, "stereo=%u", attr->stereo);
216  }
217 
218  if (0 != attr->spropstereo) {
219  if (added) {
220  ast_str_append(str, 0, ";");
221  } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
222  added = 1;
223  }
224  ast_str_append(str, 0, "sprop-stereo=%u", attr->spropstereo);
225  }
226 
227  if (0 != attr->cbr) {
228  if (added) {
229  ast_str_append(str, 0, ";");
230  } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
231  added = 1;
232  }
233  ast_str_append(str, 0, "cbr=%u", attr->cbr);
234  }
235 
236  if (0 != attr->fec) {
237  if (added) {
238  ast_str_append(str, 0, ";");
239  } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
240  added = 1;
241  }
242  ast_str_append(str, 0, "useinbandfec=%u", attr->fec);
243  }
244 
245  if (0 != attr->dtx) {
246  if (added) {
247  ast_str_append(str, 0, ";");
248  } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
249  added = 1;
250  }
251  ast_str_append(str, 0, "usedtx=%u", attr->dtx);
252  }
253 
254  if (added) {
255  ast_str_append(str, 0, "\r\n");
256  }
257 }
258 
259 static struct ast_format *opus_getjoint(const struct ast_format *format1, const struct ast_format *format2)
260 {
261  struct opus_attr *attr1 = ast_format_get_attribute_data(format1);
262  struct opus_attr *attr2 = ast_format_get_attribute_data(format2);
263  struct ast_format *jointformat;
264  struct opus_attr *attr_res;
265 
266  if (!attr1) {
267  attr1 = &default_opus_attr;
268  }
269 
270  if (!attr2) {
271  attr2 = &default_opus_attr;
272  }
273 
274  jointformat = ast_format_clone(format1);
275  if (!jointformat) {
276  return NULL;
277  }
278  attr_res = ast_format_get_attribute_data(jointformat);
279 
280  attr_res->dtx = attr1->dtx || attr2->dtx ? 1 : 0;
281 
282  /* Only do FEC if both sides want it. If a peer specifically requests not
283  * to receive with FEC, it may be a waste of bandwidth. */
284  attr_res->fec = attr1->fec && attr2->fec ? 1 : 0;
285 
286  attr_res->cbr = attr1->cbr || attr2->cbr ? 1 : 0;
287  attr_res->spropstereo = attr1->spropstereo || attr2->spropstereo ? 1 : 0;
288 
289  /* Only do stereo if both sides want it. If a peer specifically requests not
290  * to receive stereo signals, it may be a waste of bandwidth. */
291  attr_res->stereo = attr1->stereo && attr2->stereo ? 1 : 0;
292 
293  attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
294  attr_res->spropmaxcapturerate = MIN(attr1->spropmaxcapturerate, attr2->spropmaxcapturerate);
295  attr_res->maxplayrate = MIN(attr1->maxplayrate, attr2->maxplayrate);
296 
297  return jointformat;
298 }
299 
300 static struct ast_format *opus_set(const struct ast_format *format, const char *name, const char *value)
301 {
302  struct ast_format *cloned;
303  struct opus_attr *attr;
304  unsigned int val;
305 
306  if (sscanf(value, "%30u", &val) != 1) {
307  ast_log(LOG_WARNING, "Unknown value '%s' for attribute type '%s'\n",
308  value, name);
309  return NULL;
310  }
311 
312  cloned = ast_format_clone(format);
313  if (!cloned) {
314  return NULL;
315  }
316  attr = ast_format_get_attribute_data(cloned);
317 
318  if (!strcasecmp(name, "max_bitrate")) {
319  attr->maxbitrate = val;
320  } else if (!strcasecmp(name, "max_playrate")) {
321  attr->maxplayrate = val;
322  } else if (!strcasecmp(name, "minptime")) {
323  attr->unused = val;
324  } else if (!strcasecmp(name, "stereo")) {
325  attr->stereo = val;
326  } else if (!strcasecmp(name, "cbr")) {
327  attr->cbr = val;
328  } else if (!strcasecmp(name, "fec")) {
329  attr->fec = val;
330  } else if (!strcasecmp(name, "dtx")) {
331  attr->dtx = val;
332  } else if (!strcasecmp(name, "sprop_capture_rate")) {
333  attr->spropmaxcapturerate = val;
334  } else if (!strcasecmp(name, "sprop_stereo")) {
335  attr->spropstereo = val;
336  } else {
337  ast_log(LOG_WARNING, "unknown attribute type %s\n", name);
338  }
339 
340  return cloned;
341 }
342 
343 static struct ast_format_interface opus_interface = {
344  .format_destroy = opus_destroy,
345  .format_clone = opus_clone,
346  .format_get_joint = opus_getjoint,
347  .format_attribute_set = opus_set,
348  .format_parse_sdp_fmtp = opus_parse_sdp_fmtp,
349  .format_generate_sdp_fmtp = opus_generate_sdp_fmtp,
350 };
351 
352 static int load_module(void)
353 {
354  if (ast_format_interface_register("opus", &opus_interface)) {
356  }
357 
359 }
360 
361 static int unload_module(void)
362 {
363  return 0;
364 }
365 
366 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Opus Format Attribute Module",
367  .support_level = AST_MODULE_SUPPORT_CORE,
368  .load = load_module,
369  .unload = unload_module,
370  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
371 );
Asterisk main include file. File version handling, generic pbx functions.
const char * name
Definition: format.c:45
Optional format interface to extend format operations.
Definition: format.h:44
String manipulation functions.
Definition of a media format.
Definition: format.c:43
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
void * ast_format_get_attribute_data(const struct ast_format *format)
Get the attribute data on a format.
Definition: format.c:125
Utility functions.
Media Format API.
struct ast_format * ast_format_clone(const struct ast_format *format)
Clone an existing media format so it can be modified.
Definition: format.c:180
void(*const format_destroy)(struct ast_format *format)
Callback for when the format is destroyed, used to release attribute resources.
Definition: format.h:50
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
Support for dynamic strings.
Definition: strings.h:623
#define ast_format_interface_register(codec, interface)
Register a format interface for use with the provided codec.
Definition: format.h:273
Support for logging to various files, console and syslog Configuration in file logger.conf.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
void ast_format_set_attribute_data(struct ast_format *format, void *attribute_data)
Set the attribute data on a format.
Definition: format.c:130
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.