core: Fix unused variable error in handle_show_sysinfo.
[asterisk/asterisk.git] / main / codec.c
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  */
59 struct internal_ast_codec {
60         /*! \brief Public codec structure.  Must remain first. */
61         struct ast_codec external;
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
76 static int codec_hash(const void *obj, int flags)
77 {
78         const struct ast_codec *codec;
79         const char *key;
80
81         switch (flags & OBJ_SEARCH_MASK) {
82         case OBJ_SEARCH_KEY:
83                 key = obj;
84                 return ast_str_hash(key);
85         case OBJ_SEARCH_OBJECT:
86                 codec = obj;
87                 return ast_str_hash(codec->name);
88         default:
89                 /* Hash can only work on something with a full key. */
90                 ast_assert(0);
91                 return 0;
92         }
93 }
94
95 static int codec_cmp(void *obj, void *arg, int flags)
96 {
97         const struct ast_codec *left = obj;
98         const struct ast_codec *right = arg;
99         const char *right_key = arg;
100         int cmp;
101
102         switch (flags & OBJ_SEARCH_MASK) {
103         case OBJ_SEARCH_OBJECT:
104                 right_key = right->name;
105                 cmp = strcmp(left->name, right_key);
106
107                 if (right->type != AST_MEDIA_TYPE_UNKNOWN) {
108                         cmp |= (right->type != left->type);
109                 }
110
111                 /* BUGBUG: this will allow a match on a codec by name only.
112                  * This is particularly useful when executed by the CLI; if
113                  * that is not needed in translate.c, this can be removed.
114                  */
115                 if (right->sample_rate) {
116                         cmp |= (right->sample_rate != left->sample_rate);
117                 }
118                 break;
119         case OBJ_SEARCH_KEY:
120                 cmp = strcmp(left->name, right_key);
121                 break;
122         case OBJ_SEARCH_PARTIAL_KEY:
123                 cmp = strncmp(left->name, right_key, strlen(right_key));
124                 break;
125         default:
126                 ast_assert(0);
127                 cmp = 0;
128                 break;
129         }
130         if (cmp) {
131                 return 0;
132         }
133
134         return CMP_MATCH;
135 }
136
137 static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
138 {
139         struct ao2_iterator i;
140         struct internal_ast_codec *codec;
141
142         switch (cmd) {
143         case CLI_INIT:
144                 e->command = "core show codecs [audio|video|image|text]";
145                 e->usage =
146                         "Usage: core show codecs [audio|video|image|text]\n"
147                         "       Displays codec mapping\n";
148                 return NULL;
149         case CLI_GENERATE:
150                 return NULL;
151         }
152
153         if ((a->argc < 3) || (a->argc > 4)) {
154                 return CLI_SHOWUSAGE;
155         }
156
157         if (!ast_opt_dont_warn) {
158                 ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n"
159                                 "\tIt does not indicate anything about your configuration.\n");
160         }
161
162         ast_cli(a->fd, "%8s %-5s %-12s %-16s %s\n","ID","TYPE","NAME","FORMAT","DESCRIPTION");
163         ast_cli(a->fd, "------------------------------------------------------------------------------------------------\n");
164
165         ao2_rdlock(codecs);
166         i = ao2_iterator_init(codecs, AO2_ITERATOR_DONTLOCK);
167
168         for (; (codec = ao2_iterator_next(&i)); ao2_ref(codec, -1)) {
169                 if (a->argc == 4) {
170                         if (!strcasecmp(a->argv[3], "audio")) {
171                                 if (codec->external.type != AST_MEDIA_TYPE_AUDIO) {
172                                         continue;
173                                 }
174                         } else if (!strcasecmp(a->argv[3], "video")) {
175                                 if (codec->external.type != AST_MEDIA_TYPE_VIDEO) {
176                                         continue;
177                                 }
178                         } else if (!strcasecmp(a->argv[3], "image")) {
179                                 if (codec->external.type != AST_MEDIA_TYPE_IMAGE) {
180                                         continue;
181                                 }
182                         } else if (!strcasecmp(a->argv[3], "text")) {
183                                 if (codec->external.type != AST_MEDIA_TYPE_TEXT) {
184                                         continue;
185                                 }
186                         } else {
187                                 continue;
188                         }
189                 }
190
191                 ast_cli(a->fd, "%8u %-5s %-12s %-16s (%s)\n",
192                         codec->external.id,
193                         ast_codec_media_type2str(codec->external.type),
194                         codec->external.name,
195                         S_OR(codec->format_name, "no cached format"),
196                         codec->external.description);
197         }
198
199         ao2_iterator_destroy(&i);
200         ao2_unlock(codecs);
201
202         return CLI_SUCCESS;
203 }
204
205 /*! \brief Callback function for getting a codec based on unique identifier */
206 static int codec_id_cmp(void *obj, void *arg, int flags)
207 {
208         struct ast_codec *codec = obj;
209         int *id = arg;
210
211         return (codec->id == *id) ? CMP_MATCH | CMP_STOP : 0;
212 }
213
214 static char *show_codec(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
215 {
216         int type_punned_codec;
217         struct internal_ast_codec *codec;
218
219         switch (cmd) {
220         case CLI_INIT:
221                 e->command = "core show codec";
222                 e->usage =
223                         "Usage: core show codec <number>\n"
224                         "       Displays codec mapping\n";
225                 return NULL;
226         case CLI_GENERATE:
227                 return NULL;
228         }
229
230         if (a->argc != 4) {
231                 return CLI_SHOWUSAGE;
232         }
233
234         if (sscanf(a->argv[3], "%30d", &type_punned_codec) != 1) {
235                 return CLI_SHOWUSAGE;
236         }
237
238         codec = ao2_callback(codecs, 0, codec_id_cmp, &type_punned_codec);
239         if (!codec) {
240                 ast_cli(a->fd, "Codec %d not found\n", type_punned_codec);
241                 return CLI_SUCCESS;
242         }
243
244         ast_cli(a->fd, "%11u %s (%s)\n", (unsigned int) codec->external.id, codec->external.description,
245                 S_OR(codec->format_name, "no format"));
246
247         ao2_ref(codec, -1);
248
249         return CLI_SUCCESS;
250 }
251
252 /* Builtin Asterisk CLI-commands for debugging */
253 static struct ast_cli_entry codec_cli[] = {
254         AST_CLI_DEFINE(show_codecs, "Displays a list of registered codecs"),
255         AST_CLI_DEFINE(show_codec, "Shows a specific codec"),
256 };
257
258 /*! \brief Function called when the process is shutting down */
259 static void codec_shutdown(void)
260 {
261         ast_cli_unregister_multiple(codec_cli, ARRAY_LEN(codec_cli));
262         ao2_cleanup(codecs);
263         codecs = NULL;
264 }
265
266 int ast_codec_init(void)
267 {
268         codecs = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, CODEC_BUCKETS, codec_hash, codec_cmp);
269         if (!codecs) {
270                 return -1;
271         }
272
273         ast_cli_register_multiple(codec_cli, ARRAY_LEN(codec_cli));
274         ast_register_cleanup(codec_shutdown);
275
276         return 0;
277 }
278
279 static void codec_dtor(void *obj)
280 {
281         struct ast_codec *codec;
282
283         codec = obj;
284
285         ast_module_unref(codec->mod);
286 }
287
288 int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
289 {
290         return __ast_codec_register_with_format(codec, NULL, mod);
291 }
292
293 int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name, struct ast_module *mod)
294 {
295         SCOPED_AO2WRLOCK(lock, codecs);
296         struct internal_ast_codec *codec_new;
297
298         /* Some types have specific requirements */
299         if (codec->type == AST_MEDIA_TYPE_UNKNOWN) {
300                 ast_log(LOG_ERROR, "A media type must be specified for codec '%s'\n", codec->name);
301                 return -1;
302         } else if (codec->type == AST_MEDIA_TYPE_AUDIO) {
303                 if (!codec->sample_rate) {
304                         ast_log(LOG_ERROR, "A sample rate must be specified for codec '%s' of type '%s'\n",
305                                 codec->name, ast_codec_media_type2str(codec->type));
306                         return -1;
307                 }
308         }
309
310         codec_new = ao2_find(codecs, codec, OBJ_SEARCH_OBJECT | OBJ_NOLOCK);
311         if (codec_new) {
312                 ast_log(LOG_ERROR, "A codec with name '%s' of type '%s' and sample rate '%u' is already registered\n",
313                         codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
314                 ao2_ref(codec_new, -1);
315                 return -1;
316         }
317
318         codec_new = ao2_t_alloc_options(sizeof(*codec_new), codec_dtor,
319                 AO2_ALLOC_OPT_LOCK_NOLOCK, S_OR(codec->description, ""));
320         if (!codec_new) {
321                 ast_log(LOG_ERROR, "Could not allocate a codec with name '%s' of type '%s' and sample rate '%u'\n",
322                         codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
323                 return -1;
324         }
325         codec_new->external = *codec;
326         codec_new->format_name = format_name;
327         codec_new->external.id = codec_id++;
328
329         ao2_link_flags(codecs, codec_new, OBJ_NOLOCK);
330
331         /* Once registered a codec can not be unregistered, and the module must persist until shutdown */
332         ast_module_shutdown_ref(mod);
333
334         ast_verb(2, "Registered '%s' codec '%s' at sample rate '%u' with id '%u'\n",
335                 ast_codec_media_type2str(codec->type), codec->name, codec->sample_rate, codec_new->external.id);
336
337         ao2_ref(codec_new, -1);
338
339         return 0;
340 }
341
342 struct ast_codec *ast_codec_get(const char *name, enum ast_media_type type, unsigned int sample_rate)
343 {
344         struct ast_codec codec = {
345                 .name = name,
346                 .type = type,
347                 .sample_rate = sample_rate,
348         };
349
350         return ao2_find(codecs, &codec, OBJ_SEARCH_OBJECT);
351 }
352
353 struct ast_codec *ast_codec_get_by_id(int id)
354 {
355         return ao2_callback(codecs, 0, codec_id_cmp, &id);
356 }
357
358 int ast_codec_get_max(void)
359 {
360         return codec_id;
361 }
362
363 const char *ast_codec_media_type2str(enum ast_media_type type)
364 {
365         switch (type) {
366         case AST_MEDIA_TYPE_AUDIO:
367                 return "audio";
368         case AST_MEDIA_TYPE_VIDEO:
369                 return "video";
370         case AST_MEDIA_TYPE_IMAGE:
371                 return "image";
372         case AST_MEDIA_TYPE_TEXT:
373                 return "text";
374         default:
375                 return "<unknown>";
376         }
377 }
378
379 enum ast_media_type ast_media_type_from_str(const char *media_type_str)
380 {
381         if (!strcasecmp(media_type_str, "audio")) {
382                 return AST_MEDIA_TYPE_AUDIO;
383         } else if (!strcasecmp(media_type_str, "video")) {
384                 return AST_MEDIA_TYPE_VIDEO;
385         } else if (!strcasecmp(media_type_str, "image")) {
386                 return AST_MEDIA_TYPE_IMAGE;
387         } else if (!strcasecmp(media_type_str, "text")) {
388                 return AST_MEDIA_TYPE_TEXT;
389         } else {
390                 return AST_MEDIA_TYPE_UNKNOWN;
391         }
392 }
393
394 unsigned int ast_codec_samples_count(struct ast_frame *frame)
395 {
396         struct ast_codec *codec;
397         unsigned int samples = 0;
398
399         if ((frame->frametype != AST_FRAME_VOICE) &&
400                 (frame->frametype != AST_FRAME_VIDEO) &&
401                 (frame->frametype != AST_FRAME_IMAGE)) {
402                 return 0;
403         }
404
405         codec = ast_format_get_codec(frame->subclass.format);
406
407         if (codec->samples_count) {
408                 samples = codec->samples_count(frame);
409                 if ((int) samples < 0) {
410                         ast_log(LOG_WARNING, "Codec %s returned invalid number of samples.\n",
411                                 ast_format_get_name(frame->subclass.format));
412                         samples = 0;
413                 }
414         } else {
415                 ast_log(LOG_WARNING, "Unable to calculate samples for codec %s\n",
416                         ast_format_get_name(frame->subclass.format));
417         }
418
419         ao2_ref(codec, -1);
420         return samples;
421 }
422
423 unsigned int ast_codec_determine_length(const struct ast_codec *codec, unsigned int samples)
424 {
425         if (!codec->get_length) {
426                 return 0;
427         }
428
429         return codec->get_length(samples);
430 }