Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 <[email protected]>
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include <ctype.h>
33 
34 #include "asterisk/module.h"
35 #include "asterisk/format.h"
36 #include "asterisk/astobj2.h"
37 #include "asterisk/logger.h"
38 #include "asterisk/strings.h"
39 #include "asterisk/utils.h"
40 #include "asterisk/opus.h"
41 
42 /*!
43  * \brief Opus attribute structure.
44  *
45  * \note http://tools.ietf.org/html/rfc7587#section-6
46  */
47 struct opus_attr {
50  int ptime;
51  int stereo;
52  int cbr;
53  int fec;
54  int dtx;
57  int maxptime;
58  /* Note data is expected to be an ao2_object type */
59  void *data;
60 };
61 
62 static struct opus_attr default_opus_attr = {
64  .maxplayrate = CODEC_OPUS_DEFAULT_SAMPLE_RATE,
65  .ptime = CODEC_OPUS_DEFAULT_PTIME,
66  .stereo = CODEC_OPUS_DEFAULT_STEREO,
70  .spropmaxcapturerate = CODEC_OPUS_DEFAULT_SAMPLE_RATE,
71  .spropstereo = CODEC_OPUS_DEFAULT_STEREO,
73 };
74 
75 static void opus_destroy(struct ast_format *format)
76 {
77  struct opus_attr *attr = ast_format_get_attribute_data(format);
78 
79  if (!attr) {
80  return;
81  }
82 
83  ao2_cleanup(attr->data);
84  ast_free(attr);
85 }
86 
87 static int opus_clone(const struct ast_format *src, struct ast_format *dst)
88 {
89  struct opus_attr *original = ast_format_get_attribute_data(src);
90  struct opus_attr *attr = ast_malloc(sizeof(*attr));
91 
92  if (!attr) {
93  return -1;
94  }
95 
96  *attr = original ? *original : default_opus_attr;
97  ao2_bump(attr->data);
98 
101 
102  return 0;
103 }
104 
105 static void sdp_fmtp_get(const char *attributes, const char *name, int *attr)
106 {
107  const char *kvp = attributes;
108  int val;
109 
110  if (ast_strlen_zero(attributes)) {
111  return;
112  }
113 
114  /* This logic goes through each attribute in the fmtp line looking for the
115  * requested named attribute.
116  */
117  while (*kvp) {
118  /* Skip any preceeding blanks as some implementations separate attributes using spaces too */
119  kvp = ast_skip_blanks(kvp);
120 
121  /* If we are at the requested attribute get its value and return */
122  if (!strncmp(kvp, name, strlen(name)) && kvp[strlen(name)] == '=') {
123  if (sscanf(kvp, "%*[^=]=%30d", &val) == 1) {
124  *attr = val;
125  break;
126  }
127  }
128 
129  /* Move on to the next attribute if possible */
130  kvp = strchr(kvp, ';');
131  if (!kvp) {
132  break;
133  }
134 
135  kvp++;
136  }
137 }
138 
139 static struct ast_format *opus_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
140 {
141  char *attribs = ast_strdupa(attributes), *attrib;
142  struct ast_format *cloned;
143  struct opus_attr *attr;
144 
145  cloned = ast_format_clone(format);
146  if (!cloned) {
147  return NULL;
148  }
149 
150  attr = ast_format_get_attribute_data(cloned);
151 
152  /* lower-case everything, so we are case-insensitive */
153  for (attrib = attribs; *attrib; ++attrib) {
154  *attrib = tolower(*attrib);
155  } /* based on channels/chan_sip.c:process_a_sdp_image() */
156 
159  &attr->maxplayrate);
161  &attr->spropmaxcapturerate);
163  sdp_fmtp_get(attribs, CODEC_OPUS_ATTR_PTIME, &attr->ptime);
165  sdp_fmtp_get(attribs, CODEC_OPUS_ATTR_STEREO, &attr->stereo);
166  if (attr->stereo) {
167  ast_format_set_channel_count(cloned, 2);
168  }
170  sdp_fmtp_get(attribs, CODEC_OPUS_ATTR_CBR, &attr->cbr);
171  sdp_fmtp_get(attribs, CODEC_OPUS_ATTR_FEC, &attr->fec);
172  sdp_fmtp_get(attribs, CODEC_OPUS_ATTR_DTX, &attr->dtx);
173 
174  return cloned;
175 }
176 
177 static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
178 {
179  struct opus_attr *attr = ast_format_get_attribute_data(format);
180  int base_fmtp_size;
181  int original_size;
182 
183  if (!attr) {
184  /*
185  * (Only) cached formats do not have attribute data assigned because
186  * they were created before this attribute module was registered.
187  * Therefore, we assume the default attribute values here.
188  */
189  attr = &default_opus_attr;
190  }
191 
192  original_size = ast_str_strlen(*str);
193  base_fmtp_size = ast_str_append(str, 0, "a=fmtp:%u ", payload);
194 
196  ast_str_append(str, 0, "%s=%d;",
198  }
199 
201  ast_str_append(str, 0, "%s=%d;",
203  }
204 
205  if (CODEC_OPUS_DEFAULT_BITRATE != attr->maxbitrate || attr->maxbitrate > 0) {
206  ast_str_append(str, 0, "%s=%d;",
208  }
209 
210  if (CODEC_OPUS_DEFAULT_STEREO != attr->stereo) {
211  ast_str_append(str, 0, "%s=%d;",
213  }
214 
215  if (CODEC_OPUS_DEFAULT_STEREO != attr->spropstereo) {
216  ast_str_append(str, 0, "%s=%d;",
218  }
219 
220  if (CODEC_OPUS_DEFAULT_CBR != attr->cbr) {
221  ast_str_append(str, 0, "%s=%d;",
222  CODEC_OPUS_ATTR_CBR, attr->cbr);
223  }
224 
225  if (CODEC_OPUS_DEFAULT_FEC!= attr->fec) {
226  ast_str_append(str, 0, "%s=%d;",
227  CODEC_OPUS_ATTR_FEC, attr->fec);
228  }
229 
230  if (CODEC_OPUS_DEFAULT_DTX != attr->dtx) {
231  ast_str_append(str, 0, "%s=%d;",
232  CODEC_OPUS_ATTR_DTX, attr->dtx);
233  }
234 
235  if (base_fmtp_size == ast_str_strlen(*str) - original_size) {
236  ast_str_truncate(*str, original_size);
237  } else {
238  ast_str_truncate(*str, -1);
239  ast_str_append(str, 0, "\r\n");
240  }
241 }
242 
243 static struct ast_format *opus_getjoint(const struct ast_format *format1, const struct ast_format *format2)
244 {
245  struct opus_attr *attr1 = ast_format_get_attribute_data(format1);
246  struct opus_attr *attr2 = ast_format_get_attribute_data(format2);
247  struct ast_format *jointformat;
248  struct opus_attr *attr_res;
249 
250  if (!attr1) {
251  attr1 = &default_opus_attr;
252  }
253 
254  if (!attr2) {
255  attr2 = &default_opus_attr;
256  }
257 
258  jointformat = ast_format_clone(format1);
259  if (!jointformat) {
260  return NULL;
261  }
262 
263  if (ast_format_get_channel_count(format1) == 2 || ast_format_get_channel_count(format2) == 2) {
264  ast_format_set_channel_count(jointformat, 2);
265  }
266  attr_res = ast_format_get_attribute_data(jointformat);
267 
268  attr_res->dtx = attr1->dtx || attr2->dtx ? 1 : 0;
269 
270  /* Only do FEC if both sides want it. If a peer specifically requests not
271  * to receive with FEC, it may be a waste of bandwidth. */
272  attr_res->fec = attr1->fec && attr2->fec ? 1 : 0;
273 
274  attr_res->cbr = attr1->cbr || attr2->cbr ? 1 : 0;
275  attr_res->spropstereo = attr1->spropstereo || attr2->spropstereo ? 1 : 0;
276 
277  /* Only do stereo if both sides want it. If a peer specifically requests not
278  * to receive stereo signals, it may be a waste of bandwidth. */
279  attr_res->stereo = attr1->stereo && attr2->stereo ? 1 : 0;
280 
281  if (attr1->maxbitrate < 0) {
282  attr_res->maxbitrate = attr2->maxbitrate;
283  } else if (attr2->maxbitrate < 0) {
284  attr_res->maxbitrate = attr1->maxbitrate;
285  } else {
286  attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
287  }
288 
290  attr_res->maxplayrate = MIN(attr1->maxplayrate, attr2->maxplayrate);
291 
292  return jointformat;
293 }
294 
295 static struct ast_format *opus_set(const struct ast_format *format,
296  const char *name, const char *value)
297 {
298  struct ast_format *cloned;
299  struct opus_attr *attr;
300  int val;
301 
302  if (!(cloned = ast_format_clone(format))) {
303  return NULL;
304  }
305 
306  attr = ast_format_get_attribute_data(cloned);
307 
308  if (!strcmp(name, CODEC_OPUS_ATTR_DATA)) {
309  ao2_cleanup(attr->data);
310  attr->data = ao2_bump((void*)value);
311  return cloned;
312  }
313 
314  if (sscanf(value, "%30d", &val) != 1) {
315  ast_log(LOG_WARNING, "Unknown value '%s' for attribute type '%s'\n",
316  value, name);
317  ao2_ref(cloned, -1);
318  return NULL;
319  }
320 
321  if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_PLAYBACK_RATE)) {
322  attr->maxplayrate = val;
323  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_CODED_AUDIO_BANDWIDTH)) {
324  attr->maxplayrate = val;
325  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_SPROP_MAX_CAPTURE_RATE)) {
326  attr->spropmaxcapturerate = val;
327  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_PTIME)) {
328  attr->maxptime = val;
329  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_PTIME)) {
330  attr->ptime = val;
331  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE)) {
332  attr->maxbitrate = val;
333  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_STEREO)) {
334  attr->stereo = val;
335  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_SPROP_STEREO)) {
336  attr->spropstereo = val;
337  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_CBR)) {
338  attr->cbr = val;
339  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_FEC)) {
340  attr->fec = val;
341  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_DTX)) {
342  attr->dtx = val;
343  } else {
344  ast_log(LOG_WARNING, "unknown attribute type %s\n", name);
345  }
346 
347  return cloned;
348 }
349 
350 static const void *opus_get(const struct ast_format *format, const char *name)
351 {
352  struct opus_attr *attr = ast_format_get_attribute_data(format);
353  int *val = NULL;
354 
355  if (!attr) {
356  return NULL;
357  }
358 
359  if (!strcasecmp(name, CODEC_OPUS_ATTR_DATA)) {
360  return ao2_bump(attr->data);
361  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_PLAYBACK_RATE)) {
362  val = &attr->maxplayrate;
363  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_SPROP_MAX_CAPTURE_RATE)) {
364  val = &attr->spropmaxcapturerate;
365  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_PTIME)) {
366  val = &attr->maxptime;
367  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_PTIME)) {
368  val = &attr->ptime;
369  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE)) {
370  val = &attr->maxbitrate;
371  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_STEREO)) {
372  val = &attr->stereo;
373  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_SPROP_STEREO)) {
374  val = &attr->spropstereo;
375  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_CBR)) {
376  val = &attr->cbr;
377  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_FEC)) {
378  val = &attr->fec;
379  } else if (!strcasecmp(name, CODEC_OPUS_ATTR_DTX)) {
380  val = &attr->dtx;
381  } else {
382  ast_log(LOG_WARNING, "unknown attribute type %s\n", name);
383  }
384 
385  return val;
386 }
387 
390  .format_clone = opus_clone,
391  .format_get_joint = opus_getjoint,
392  .format_attribute_set = opus_set,
393  .format_parse_sdp_fmtp = opus_parse_sdp_fmtp,
394  .format_generate_sdp_fmtp = opus_generate_sdp_fmtp,
395  .format_attribute_get = opus_get
396 };
397 
398 static int load_module(void)
399 {
400  if (__ast_format_interface_register("opus", &opus_interface, ast_module_info->self)) {
402  }
403 
405 }
406 
407 static int unload_module(void)
408 {
409  return 0;
410 }
411 
413  .support_level = AST_MODULE_SUPPORT_CORE,
414  .load = load_module,
415  .unload = unload_module,
416  .load_pri = AST_MODPRI_REALTIME_DRIVER /* Needs to load before codec_opus */
417 );
Asterisk main include file. File version handling, generic pbx functions.
unsigned int ast_format_get_channel_count(const struct ast_format *format)
Get the channel count on a format.
Definition: format.c:135
static int opus_clone(const struct ast_format *src, struct ast_format *dst)
#define CODEC_OPUS_DEFAULT_BITRATE
Definition: opus.h:58
Optional format interface to extend format operations.
Definition: format.h:44
#define CODEC_OPUS_ATTR_STEREO
Decode stereo (1) vs mono (0)
Definition: opus.h:41
static int load_module(void)
#define CODEC_OPUS_ATTR_CBR
Decoder prefers a constant (1) vs variable (0) bitrate.
Definition: opus.h:45
String manipulation functions.
Definition: ast_expr2.c:325
#define LOG_WARNING
Definition: logger.h:274
static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
void ast_format_set_channel_count(struct ast_format *format, unsigned int channel_count)
Set the channel count on a format.
Definition: format.c:140
static struct opus_attr default_opus_attr
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:1091
void * ast_format_get_attribute_data(const struct ast_format *format)
Get the attribute data on a format.
Definition: format.c:125
const char * str
Definition: app_jack.c:147
#define NULL
Definition: resample.c:96
int value
Definition: syslog.c:37
Opus attribute structure.
static struct ast_format_interface opus_interface
#define CODEC_OPUS_ATTR_SPROP_STEREO
Likeliness of sender producing stereo (1) vs mono (0)
Definition: opus.h:43
Utility functions.
Media Format API.
#define CODEC_OPUS_DEFAULT_MAX_PTIME
Definition: opus.h:56
char * ast_str_truncate(struct ast_str *buf, ssize_t len)
Truncates the enclosed string to the given length.
Definition: strings.h:738
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ao2_bump(obj)
Definition: astobj2.h:491
#define MIN(a, b)
Definition: utils.h:226
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
#define CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE
Maximum average received bit rate (in bits per second)
Definition: opus.h:39
#define CODEC_OPUS_ATTR_DTX
Use discontinuous transmission (1) or not (0)
Definition: opus.h:49
#define CODEC_OPUS_ATTR_PTIME
Duration of packet (in milliseconds)
Definition: opus.h:37
#define ast_log
Definition: astobj2.c:42
#define CODEC_OPUS_ATTR_MAX_PTIME
Maximum duration of packet (in milliseconds)
Definition: opus.h:35
struct ast_module * self
Definition: module.h:342
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 ao2_ref(o, delta)
Definition: astobj2.h:464
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
#define CODEC_OPUS_ATTR_MAX_CODED_AUDIO_BANDWIDTH
An alias for maxplaybackrate (used in older versions)
Definition: opus.h:31
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define CODEC_OPUS_DEFAULT_SAMPLE_RATE
Default attribute values.
Definition: opus.h:54
#define CODEC_OPUS_DEFAULT_CBR
Definition: opus.h:59
static int unload_module(void)
#define CODEC_OPUS_DEFAULT_DTX
Definition: opus.h:61
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:157
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
#define CODEC_OPUS_ATTR_MAX_PLAYBACK_RATE
Maximum sampling rate an endpoint is capable of receiving.
Definition: opus.h:29
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define CODEC_OPUS_ATTR_SPROP_MAX_CAPTURE_RATE
Maximum sampling rate an endpoint is capable of sending.
Definition: opus.h:33
static const void * opus_get(const struct ast_format *format, const char *name)
#define CODEC_OPUS_DEFAULT_PTIME
Definition: opus.h:57
#define CODEC_OPUS_ATTR_FEC
Use forward error correction (1) or not (0)
Definition: opus.h:47
#define CODEC_OPUS_ATTR_DATA
Custom data object.
Definition: opus.h:51
Support for logging to various files, console and syslog Configuration in file logger.conf.
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:688
Codec opus externals and format attributes.
#define CODEC_OPUS_DEFAULT_STEREO
Definition: opus.h:62
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 ao2_cleanup(obj)
Definition: astobj2.h:1958
static struct ast_format * opus_set(const struct ast_format *format, const char *name, const char *value)
static void opus_destroy(struct ast_format *format)
static struct ast_format * opus_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
int __ast_format_interface_register(const char *codec, const struct ast_format_interface *interface, struct ast_module *mod)
Register a format interface for use with the provided codec.
Definition: format.c:90
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
static snd_pcm_format_t format
Definition: chan_alsa.c:102
static struct ast_format * opus_getjoint(const struct ast_format *format1, const struct ast_format *format2)
static void sdp_fmtp_get(const char *attributes, const char *name, int *attr)
#define CODEC_OPUS_DEFAULT_FEC
Definition: opus.h:60