Asterisk - The Open Source Telephony Project  21.4.1
codec.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2014, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@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 /*! \file
20  *
21  * \brief Codecs API
22  *
23  * \author Joshua Colp <jcolp@digium.com>
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include "asterisk/logger.h"
33 #include "asterisk/codec.h"
34 #include "asterisk/format.h"
35 #include "asterisk/frame.h"
36 #include "asterisk/astobj2.h"
37 #include "asterisk/strings.h"
38 #include "asterisk/module.h"
39 #include "asterisk/cli.h"
40 
41 /*! \brief Number of buckets to use for codecs (should be prime for performance reasons) */
42 #define CODEC_BUCKETS 53
43 
44 /*! \brief Current identifier value for newly registered codec */
45 static int codec_id = 1;
46 
47 /*! \brief Registered codecs */
48 static struct ao2_container *codecs;
49 
50 /*!
51  * \internal
52  * \brief Internal codec structure
53  *
54  * External codecs won't know about the format_name field so the public
55  * ast_codec structure has to leave it out. This structure will be used
56  * for the internal codecs.
57  *
58  */
60  /*! \brief Public codec structure. Must remain first. */
62  /*! \brief A format name for a default sane format using this codec */
63  const char *format_name;
64 };
65 
66 /*!
67  * \internal
68  * \brief Internal function for registration with format name
69  *
70  * This function is only used by codec.c and codec_builtin.c and
71  * will be removed in Asterisk 14
72  */
73 int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name,
74  struct ast_module *mod);
75 
77 
78 static int codec_cmp(void *obj, void *arg, int flags)
79 {
80  const struct ast_codec *left = obj;
81  const struct ast_codec *right = arg;
82  const char *right_key = arg;
83  int cmp;
84 
85  switch (flags & OBJ_SEARCH_MASK) {
86  case OBJ_SEARCH_OBJECT:
87  right_key = right->name;
88  cmp = strcmp(left->name, right_key);
89 
90  if (right->type != AST_MEDIA_TYPE_UNKNOWN) {
91  cmp |= (right->type != left->type);
92  }
93 
94  /* BUGBUG: this will allow a match on a codec by name only.
95  * This is particularly useful when executed by the CLI; if
96  * that is not needed in translate.c, this can be removed.
97  */
98  if (right->sample_rate) {
99  cmp |= (right->sample_rate != left->sample_rate);
100  }
101  break;
102  case OBJ_SEARCH_KEY:
103  cmp = strcmp(left->name, right_key);
104  break;
106  cmp = strncmp(left->name, right_key, strlen(right_key));
107  break;
108  default:
109  ast_assert(0);
110  cmp = 0;
111  break;
112  }
113  if (cmp) {
114  return 0;
115  }
116 
117  return CMP_MATCH;
118 }
119 
120 static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
121 {
122  struct ao2_iterator i;
123  struct internal_ast_codec *codec;
124 
125  switch (cmd) {
126  case CLI_INIT:
127  e->command = "core show codecs [audio|video|image|text]";
128  e->usage =
129  "Usage: core show codecs [audio|video|image|text]\n"
130  " Displays codec mapping\n";
131  return NULL;
132  case CLI_GENERATE:
133  return NULL;
134  }
135 
136  if ((a->argc < 3) || (a->argc > 4)) {
137  return CLI_SHOWUSAGE;
138  }
139 
140  if (!ast_opt_dont_warn) {
141  ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n"
142  "\tIt does not indicate anything about your configuration.\n");
143  }
144 
145  ast_cli(a->fd, "%8s %-5s %-12s %-16s %7s %s\n","ID","TYPE","NAME","FORMAT","QUALITY", "DESCRIPTION");
146  ast_cli(a->fd, "------------------------------------------------------------------------------------------------\n");
147 
148  ao2_rdlock(codecs);
150 
151  for (; (codec = ao2_iterator_next(&i)); ao2_ref(codec, -1)) {
152  if (a->argc == 4) {
153  if (!strcasecmp(a->argv[3], "audio")) {
154  if (codec->external.type != AST_MEDIA_TYPE_AUDIO) {
155  continue;
156  }
157  } else if (!strcasecmp(a->argv[3], "video")) {
158  if (codec->external.type != AST_MEDIA_TYPE_VIDEO) {
159  continue;
160  }
161  } else if (!strcasecmp(a->argv[3], "image")) {
162  if (codec->external.type != AST_MEDIA_TYPE_IMAGE) {
163  continue;
164  }
165  } else if (!strcasecmp(a->argv[3], "text")) {
166  if (codec->external.type != AST_MEDIA_TYPE_TEXT) {
167  continue;
168  }
169  } else {
170  continue;
171  }
172  }
173 
174  ast_cli(a->fd, "%8u %-5s %-12s %-16s %7d (%s)\n",
175  codec->external.id,
177  codec->external.name,
178  S_OR(codec->format_name, "no cached format"),
179  codec->external.quality,
180  codec->external.description);
181  }
182 
184  ao2_unlock(codecs);
185 
186  return CLI_SUCCESS;
187 }
188 
189 /*! \brief Callback function for getting a codec based on unique identifier */
190 static int codec_id_cmp(void *obj, void *arg, int flags)
191 {
192  struct ast_codec *codec = obj;
193  int *id = arg;
194 
195  return (codec->id == *id) ? CMP_MATCH | CMP_STOP : 0;
196 }
197 
198 static char *show_codec(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
199 {
200  int type_punned_codec;
201  struct internal_ast_codec *codec;
202 
203  switch (cmd) {
204  case CLI_INIT:
205  e->command = "core show codec";
206  e->usage =
207  "Usage: core show codec <number>\n"
208  " Displays codec mapping\n";
209  return NULL;
210  case CLI_GENERATE:
211  return NULL;
212  }
213 
214  if (a->argc != 4) {
215  return CLI_SHOWUSAGE;
216  }
217 
218  if (sscanf(a->argv[3], "%30d", &type_punned_codec) != 1) {
219  return CLI_SHOWUSAGE;
220  }
221 
222  codec = ao2_callback(codecs, 0, codec_id_cmp, &type_punned_codec);
223  if (!codec) {
224  ast_cli(a->fd, "Codec %d not found\n", type_punned_codec);
225  return CLI_SUCCESS;
226  }
227 
228  ast_cli(a->fd, "%11u %s (%s)\n", (unsigned int) codec->external.id, codec->external.description,
229  S_OR(codec->format_name, "no format"));
230 
231  ao2_ref(codec, -1);
232 
233  return CLI_SUCCESS;
234 }
235 
236 /* Builtin Asterisk CLI-commands for debugging */
237 static struct ast_cli_entry codec_cli[] = {
238  AST_CLI_DEFINE(show_codecs, "Displays a list of registered codecs"),
239  AST_CLI_DEFINE(show_codec, "Shows a specific codec"),
240 };
241 
242 /*! \brief Function called when the process is shutting down */
243 static void codec_shutdown(void)
244 {
245  ast_cli_unregister_multiple(codec_cli, ARRAY_LEN(codec_cli));
246  ao2_cleanup(codecs);
247  codecs = NULL;
248 }
249 
250 int ast_codec_init(void)
251 {
253  ast_codec_hash_fn, NULL, codec_cmp);
254  if (!codecs) {
255  return -1;
256  }
257 
258  ast_cli_register_multiple(codec_cli, ARRAY_LEN(codec_cli));
260 
261  return 0;
262 }
263 
264 static void codec_dtor(void *obj)
265 {
266  struct ast_codec *codec;
267 
268  codec = obj;
269 
270  ast_module_unref(codec->mod);
271 }
272 
273 int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
274 {
275  return __ast_codec_register_with_format(codec, NULL, mod);
276 }
277 
278 int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name, struct ast_module *mod)
279 {
280  SCOPED_AO2WRLOCK(lock, codecs);
281  struct internal_ast_codec *codec_new;
282 
283  /* Some types have specific requirements */
284  if (codec->type == AST_MEDIA_TYPE_UNKNOWN) {
285  ast_log(LOG_ERROR, "A media type must be specified for codec '%s'\n", codec->name);
286  return -1;
287  } else if (codec->type == AST_MEDIA_TYPE_AUDIO) {
288  if (!codec->sample_rate) {
289  ast_log(LOG_ERROR, "A sample rate must be specified for codec '%s' of type '%s'\n",
290  codec->name, ast_codec_media_type2str(codec->type));
291  return -1;
292  }
293  }
294 
295  codec_new = ao2_find(codecs, codec, OBJ_SEARCH_OBJECT | OBJ_NOLOCK);
296  if (codec_new) {
297  ast_log(LOG_ERROR, "A codec with name '%s' of type '%s' and sample rate '%u' is already registered\n",
298  codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
299  ao2_ref(codec_new, -1);
300  return -1;
301  }
302 
303  codec_new = ao2_t_alloc_options(sizeof(*codec_new), codec_dtor,
305  if (!codec_new) {
306  ast_log(LOG_ERROR, "Could not allocate a codec with name '%s' of type '%s' and sample rate '%u'\n",
307  codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
308  return -1;
309  }
310  codec_new->external = *codec;
311  codec_new->format_name = format_name;
312  codec_new->external.id = codec_id++;
313 
314  ao2_link_flags(codecs, codec_new, OBJ_NOLOCK);
315 
316  /* Once registered a codec can not be unregistered, and the module must persist until shutdown */
318 
319  ast_verb(5, "Registered '%s' codec '%s' at sample rate '%u' with id '%u'\n",
320  ast_codec_media_type2str(codec->type), codec->name, codec->sample_rate, codec_new->external.id);
321 
322  ao2_ref(codec_new, -1);
323 
324  return 0;
325 }
326 
327 struct ast_codec *ast_codec_get(const char *name, enum ast_media_type type, unsigned int sample_rate)
328 {
329  struct ast_codec codec = {
330  .name = name,
331  .type = type,
332  .sample_rate = sample_rate,
333  };
334 
335  return ao2_find(codecs, &codec, OBJ_SEARCH_OBJECT);
336 }
337 
339 {
340  return ao2_callback(codecs, 0, codec_id_cmp, &id);
341 }
342 
344 {
345  return codec_id;
346 }
347 
349 {
350  switch (type) {
351  case AST_MEDIA_TYPE_AUDIO:
352  return "audio";
353  case AST_MEDIA_TYPE_VIDEO:
354  return "video";
355  case AST_MEDIA_TYPE_IMAGE:
356  return "image";
357  case AST_MEDIA_TYPE_TEXT:
358  return "text";
359  default:
360  return "<unknown>";
361  }
362 }
363 
364 enum ast_media_type ast_media_type_from_str(const char *media_type_str)
365 {
366  if (!strcasecmp(media_type_str, "audio")) {
367  return AST_MEDIA_TYPE_AUDIO;
368  } else if (!strcasecmp(media_type_str, "video")) {
369  return AST_MEDIA_TYPE_VIDEO;
370  } else if (!strcasecmp(media_type_str, "image")) {
371  return AST_MEDIA_TYPE_IMAGE;
372  } else if (!strcasecmp(media_type_str, "text")) {
373  return AST_MEDIA_TYPE_TEXT;
374  } else {
375  return AST_MEDIA_TYPE_UNKNOWN;
376  }
377 }
378 
379 unsigned int ast_codec_samples_count(struct ast_frame *frame)
380 {
381  struct ast_codec *codec;
382  unsigned int samples = 0;
383 
384  if ((frame->frametype != AST_FRAME_VOICE) &&
385  (frame->frametype != AST_FRAME_VIDEO) &&
386  (frame->frametype != AST_FRAME_IMAGE)) {
387  return 0;
388  }
389 
390  codec = ast_format_get_codec(frame->subclass.format);
391 
392  if (codec->samples_count) {
393  samples = codec->samples_count(frame);
394  if ((int) samples < 0) {
395  ast_log(LOG_WARNING, "Codec %s returned invalid number of samples.\n",
397  samples = 0;
398  }
399  } else {
400  ast_log(LOG_WARNING, "Unable to calculate samples for codec %s\n",
402  }
403 
404  ao2_ref(codec, -1);
405  return samples;
406 }
407 
408 unsigned int ast_codec_determine_length(const struct ast_codec *codec, unsigned int samples)
409 {
410  if (!codec->get_length) {
411  return 0;
412  }
413 
414  return codec->get_length(samples);
415 }
const char * name
Name for this codec.
Definition: codec.h:46
Asterisk main include file. File version handling, generic pbx functions.
AO2_STRING_FIELD_HASH_FN(transport_monitor, key)
Hashing function for struct transport_monitor.
const char * ast_codec_media_type2str(enum ast_media_type type)
Conversion function to take a media type and turn it into a string.
Definition: codec.c:348
struct ast_codec * ast_format_get_codec(const struct ast_format *format)
Get the codec associated with a format.
Definition: format.c:324
String manipulation functions.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
unsigned int id
Internal unique identifier for this codec, set at registration time (starts at 1) ...
Definition: codec.h:44
descriptor for a cli entry.
Definition: cli.h:171
enum ast_media_type ast_media_type_from_str(const char *media_type_str)
Conversion function to take a media string and convert it to a media type.
Definition: codec.c:364
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1693
struct ast_codec external
Public codec structure. Must remain first.
Definition: codec.c:61
Assume that the ao2_container is already locked.
Definition: astobj2.h:1063
Codec API.
int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
This function is used to register a codec with the Asterisk core. Registering allows it to be passed ...
Definition: codec.c:273
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
Definition: astobj2.h:1554
struct ast_codec * ast_codec_get_by_id(int id)
Retrieve a codec given the unique identifier.
Definition: codec.c:338
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
#define ao2_t_alloc_options(data_size, destructor_fn, options, debug_msg)
Allocate and initialize an object.
Definition: astobj2.h:402
struct ast_module * mod
The module that registered this codec.
Definition: codec.h:84
#define SCOPED_AO2WRLOCK(varname, obj)
scoped lock specialization for ao2 write locks.
Definition: lock.h:614
struct ast_frame_subclass subclass
Media Format API.
unsigned int ast_codec_samples_count(struct ast_frame *frame)
Get the number of samples contained within a frame.
Definition: codec.c:379
static int codec_id_cmp(void *obj, void *arg, int flags)
Callback function for getting a codec based on unique identifier.
Definition: codec.c:190
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
Definition: astobj2.h:1116
ast_mutex_t lock
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
static struct ao2_container * codecs
Registered codecs.
Definition: codec.c:48
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
Asterisk internal frame definitions.
unsigned int quality
Format quality, on scale from 0 to 150 (100 is ulaw, the reference). This allows better format to be ...
Definition: codec.h:82
int ast_codec_get_max(void)
Retrieve the current maximum identifier for codec iteration.
Definition: codec.c:343
unsigned int ast_codec_determine_length(const struct ast_codec *codec, unsigned int samples)
Get the length of media (in milliseconds) given a number of samples.
Definition: codec.c:408
int ast_codec_init(void)
Initialize codec support within the core.
Definition: codec.c:250
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
const char * description
Brief description.
Definition: codec.h:48
static void codec_shutdown(void)
Function called when the process is shutting down.
Definition: codec.c:243
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
Definition: module.h:478
int(* get_length)(unsigned int samples)
Retrieve the length of media from number of samples.
Definition: codec.h:76
char * command
Definition: cli.h:186
static int codec_id
Current identifier value for newly registered codec.
Definition: codec.c:45
struct ast_codec * ast_codec_get(const char *name, enum ast_media_type type, unsigned int sample_rate)
Retrieve a codec given a name, type, and sample rate.
Definition: codec.c:327
Support for logging to various files, console and syslog Configuration in file logger.conf.
const char * format_name
A format name for a default sane format using this codec.
Definition: codec.c:63
unsigned int sample_rate
Sample rate (number of samples carried in a second)
Definition: codec.h:52
const char * usage
Definition: cli.h:177
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:483
#define CODEC_BUCKETS
Number of buckets to use for codecs (should be prime for performance reasons)
Definition: codec.c:42
Assume that the ao2_container is already locked.
Definition: astobj2.h:1852
The arg parameter is an object of the same type.
Definition: astobj2.h:1087
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Standard Command Line Interface.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
int(* samples_count)(struct ast_frame *frame)
Retrieve the number of samples in a frame.
Definition: codec.h:68
Data structure associated with a single frame of data.
enum ast_media_type type
Type of media this codec contains.
Definition: codec.h:50
ast_media_type
Types of media.
Definition: codec.h:30
enum ast_frame_type frametype
Generic container type.
Search option field mask.
Definition: astobj2.h:1072
struct ast_format * format
Asterisk module definitions.
Represents a media codec within Asterisk.
Definition: codec.h:42
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.