vector: Additional string vector definitions.
[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 AO2_STRING_FIELD_HASH_FN(ast_codec, name)
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;
105         case OBJ_SEARCH_PARTIAL_KEY:
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 %s\n","ID","TYPE","NAME","FORMAT","DESCRIPTION");
146         ast_cli(a->fd, "------------------------------------------------------------------------------------------------\n");
147
148         ao2_rdlock(codecs);
149         i = ao2_iterator_init(codecs, AO2_ITERATOR_DONTLOCK);
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 (%s)\n",
175                         codec->external.id,
176                         ast_codec_media_type2str(codec->external.type),
177                         codec->external.name,
178                         S_OR(codec->format_name, "no cached format"),
179                         codec->external.description);
180         }
181
182         ao2_iterator_destroy(&i);
183         ao2_unlock(codecs);
184
185         return CLI_SUCCESS;
186 }
187
188 /*! \brief Callback function for getting a codec based on unique identifier */
189 static int codec_id_cmp(void *obj, void *arg, int flags)
190 {
191         struct ast_codec *codec = obj;
192         int *id = arg;
193
194         return (codec->id == *id) ? CMP_MATCH | CMP_STOP : 0;
195 }
196
197 static char *show_codec(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
198 {
199         int type_punned_codec;
200         struct internal_ast_codec *codec;
201
202         switch (cmd) {
203         case CLI_INIT:
204                 e->command = "core show codec";
205                 e->usage =
206                         "Usage: core show codec <number>\n"
207                         "       Displays codec mapping\n";
208                 return NULL;
209         case CLI_GENERATE:
210                 return NULL;
211         }
212
213         if (a->argc != 4) {
214                 return CLI_SHOWUSAGE;
215         }
216
217         if (sscanf(a->argv[3], "%30d", &type_punned_codec) != 1) {
218                 return CLI_SHOWUSAGE;
219         }
220
221         codec = ao2_callback(codecs, 0, codec_id_cmp, &type_punned_codec);
222         if (!codec) {
223                 ast_cli(a->fd, "Codec %d not found\n", type_punned_codec);
224                 return CLI_SUCCESS;
225         }
226
227         ast_cli(a->fd, "%11u %s (%s)\n", (unsigned int) codec->external.id, codec->external.description,
228                 S_OR(codec->format_name, "no format"));
229
230         ao2_ref(codec, -1);
231
232         return CLI_SUCCESS;
233 }
234
235 /* Builtin Asterisk CLI-commands for debugging */
236 static struct ast_cli_entry codec_cli[] = {
237         AST_CLI_DEFINE(show_codecs, "Displays a list of registered codecs"),
238         AST_CLI_DEFINE(show_codec, "Shows a specific codec"),
239 };
240
241 /*! \brief Function called when the process is shutting down */
242 static void codec_shutdown(void)
243 {
244         ast_cli_unregister_multiple(codec_cli, ARRAY_LEN(codec_cli));
245         ao2_cleanup(codecs);
246         codecs = NULL;
247 }
248
249 int ast_codec_init(void)
250 {
251         codecs = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, CODEC_BUCKETS,
252                 ast_codec_hash_fn, codec_cmp);
253         if (!codecs) {
254                 return -1;
255         }
256
257         ast_cli_register_multiple(codec_cli, ARRAY_LEN(codec_cli));
258         ast_register_cleanup(codec_shutdown);
259
260         return 0;
261 }
262
263 static void codec_dtor(void *obj)
264 {
265         struct ast_codec *codec;
266
267         codec = obj;
268
269         ast_module_unref(codec->mod);
270 }
271
272 int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
273 {
274         return __ast_codec_register_with_format(codec, NULL, mod);
275 }
276
277 int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name, struct ast_module *mod)
278 {
279         SCOPED_AO2WRLOCK(lock, codecs);
280         struct internal_ast_codec *codec_new;
281
282         /* Some types have specific requirements */
283         if (codec->type == AST_MEDIA_TYPE_UNKNOWN) {
284                 ast_log(LOG_ERROR, "A media type must be specified for codec '%s'\n", codec->name);
285                 return -1;
286         } else if (codec->type == AST_MEDIA_TYPE_AUDIO) {
287                 if (!codec->sample_rate) {
288                         ast_log(LOG_ERROR, "A sample rate must be specified for codec '%s' of type '%s'\n",
289                                 codec->name, ast_codec_media_type2str(codec->type));
290                         return -1;
291                 }
292         }
293
294         codec_new = ao2_find(codecs, codec, OBJ_SEARCH_OBJECT | OBJ_NOLOCK);
295         if (codec_new) {
296                 ast_log(LOG_ERROR, "A codec with name '%s' of type '%s' and sample rate '%u' is already registered\n",
297                         codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
298                 ao2_ref(codec_new, -1);
299                 return -1;
300         }
301
302         codec_new = ao2_t_alloc_options(sizeof(*codec_new), codec_dtor,
303                 AO2_ALLOC_OPT_LOCK_NOLOCK, S_OR(codec->description, ""));
304         if (!codec_new) {
305                 ast_log(LOG_ERROR, "Could not allocate a codec with name '%s' of type '%s' and sample rate '%u'\n",
306                         codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
307                 return -1;
308         }
309         codec_new->external = *codec;
310         codec_new->format_name = format_name;
311         codec_new->external.id = codec_id++;
312
313         ao2_link_flags(codecs, codec_new, OBJ_NOLOCK);
314
315         /* Once registered a codec can not be unregistered, and the module must persist until shutdown */
316         ast_module_shutdown_ref(mod);
317
318         ast_verb(2, "Registered '%s' codec '%s' at sample rate '%u' with id '%u'\n",
319                 ast_codec_media_type2str(codec->type), codec->name, codec->sample_rate, codec_new->external.id);
320
321         ao2_ref(codec_new, -1);
322
323         return 0;
324 }
325
326 struct ast_codec *ast_codec_get(const char *name, enum ast_media_type type, unsigned int sample_rate)
327 {
328         struct ast_codec codec = {
329                 .name = name,
330                 .type = type,
331                 .sample_rate = sample_rate,
332         };
333
334         return ao2_find(codecs, &codec, OBJ_SEARCH_OBJECT);
335 }
336
337 struct ast_codec *ast_codec_get_by_id(int id)
338 {
339         return ao2_callback(codecs, 0, codec_id_cmp, &id);
340 }
341
342 int ast_codec_get_max(void)
343 {
344         return codec_id;
345 }
346
347 const char *ast_codec_media_type2str(enum ast_media_type type)
348 {
349         switch (type) {
350         case AST_MEDIA_TYPE_AUDIO:
351                 return "audio";
352         case AST_MEDIA_TYPE_VIDEO:
353                 return "video";
354         case AST_MEDIA_TYPE_IMAGE:
355                 return "image";
356         case AST_MEDIA_TYPE_TEXT:
357                 return "text";
358         default:
359                 return "<unknown>";
360         }
361 }
362
363 enum ast_media_type ast_media_type_from_str(const char *media_type_str)
364 {
365         if (!strcasecmp(media_type_str, "audio")) {
366                 return AST_MEDIA_TYPE_AUDIO;
367         } else if (!strcasecmp(media_type_str, "video")) {
368                 return AST_MEDIA_TYPE_VIDEO;
369         } else if (!strcasecmp(media_type_str, "image")) {
370                 return AST_MEDIA_TYPE_IMAGE;
371         } else if (!strcasecmp(media_type_str, "text")) {
372                 return AST_MEDIA_TYPE_TEXT;
373         } else {
374                 return AST_MEDIA_TYPE_UNKNOWN;
375         }
376 }
377
378 unsigned int ast_codec_samples_count(struct ast_frame *frame)
379 {
380         struct ast_codec *codec;
381         unsigned int samples = 0;
382
383         if ((frame->frametype != AST_FRAME_VOICE) &&
384                 (frame->frametype != AST_FRAME_VIDEO) &&
385                 (frame->frametype != AST_FRAME_IMAGE)) {
386                 return 0;
387         }
388
389         codec = ast_format_get_codec(frame->subclass.format);
390
391         if (codec->samples_count) {
392                 samples = codec->samples_count(frame);
393                 if ((int) samples < 0) {
394                         ast_log(LOG_WARNING, "Codec %s returned invalid number of samples.\n",
395                                 ast_format_get_name(frame->subclass.format));
396                         samples = 0;
397                 }
398         } else {
399                 ast_log(LOG_WARNING, "Unable to calculate samples for codec %s\n",
400                         ast_format_get_name(frame->subclass.format));
401         }
402
403         ao2_ref(codec, -1);
404         return samples;
405 }
406
407 unsigned int ast_codec_determine_length(const struct ast_codec *codec, unsigned int samples)
408 {
409         if (!codec->get_length) {
410                 return 0;
411         }
412
413         return codec->get_length(samples);
414 }