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