codec.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2014, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Codecs API
00022  *
00023  * \author Joshua Colp <jcolp@digium.com>
00024  */
00025 
00026 /*** MODULEINFO
00027    <support_level>core</support_level>
00028  ***/
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 433498 $")
00033 
00034 #include "asterisk/logger.h"
00035 #include "asterisk/codec.h"
00036 #include "asterisk/format.h"
00037 #include "asterisk/frame.h"
00038 #include "asterisk/astobj2.h"
00039 #include "asterisk/strings.h"
00040 #include "asterisk/module.h"
00041 #include "asterisk/cli.h"
00042 
00043 /*! \brief Number of buckets to use for codecs (should be prime for performance reasons) */
00044 #define CODEC_BUCKETS 53
00045 
00046 /*! \brief Current identifier value for newly registered codec */
00047 static int codec_id = 1;
00048 
00049 /*! \brief Registered codecs */
00050 static struct ao2_container *codecs;
00051 
00052 static int codec_hash(const void *obj, int flags)
00053 {
00054    const struct ast_codec *codec;
00055    const char *key;
00056 
00057    switch (flags & OBJ_SEARCH_MASK) {
00058    case OBJ_SEARCH_KEY:
00059       key = obj;
00060       return ast_str_hash(key);
00061    case OBJ_SEARCH_OBJECT:
00062       codec = obj;
00063       return ast_str_hash(codec->name);
00064    default:
00065       /* Hash can only work on something with a full key. */
00066       ast_assert(0);
00067       return 0;
00068    }
00069 }
00070 
00071 static int codec_cmp(void *obj, void *arg, int flags)
00072 {
00073    const struct ast_codec *left = obj;
00074    const struct ast_codec *right = arg;
00075    const char *right_key = arg;
00076    int cmp;
00077 
00078    switch (flags & OBJ_SEARCH_MASK) {
00079    case OBJ_SEARCH_OBJECT:
00080       right_key = right->name;
00081       cmp = strcmp(left->name, right_key);
00082 
00083       if (right->type != AST_MEDIA_TYPE_UNKNOWN) {
00084          cmp |= (right->type != left->type);
00085       }
00086 
00087       /* BUGBUG: this will allow a match on a codec by name only.
00088        * This is particularly useful when executed by the CLI; if
00089        * that is not needed in translate.c, this can be removed.
00090        */
00091       if (right->sample_rate) {
00092          cmp |= (right->sample_rate != left->sample_rate);
00093       }
00094       break;
00095    case OBJ_SEARCH_KEY:
00096       cmp = strcmp(left->name, right_key);
00097       break;
00098    case OBJ_SEARCH_PARTIAL_KEY:
00099       cmp = strncmp(left->name, right_key, strlen(right_key));
00100       break;
00101    default:
00102       ast_assert(0);
00103       cmp = 0;
00104       break;
00105    }
00106    if (cmp) {
00107       return 0;
00108    }
00109 
00110    return CMP_MATCH;
00111 }
00112 
00113 static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00114 {
00115    struct ao2_iterator i;
00116    struct ast_codec *codec;
00117 
00118    switch (cmd) {
00119    case CLI_INIT:
00120       e->command = "core show codecs [audio|video|image|text]";
00121       e->usage =
00122          "Usage: core show codecs [audio|video|image|text]\n"
00123          "       Displays codec mapping\n";
00124       return NULL;
00125    case CLI_GENERATE:
00126       return NULL;
00127    }
00128 
00129    if ((a->argc < 3) || (a->argc > 4)) {
00130       return CLI_SHOWUSAGE;
00131    }
00132 
00133    if (!ast_opt_dont_warn) {
00134       ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n"
00135             "\tIt does not indicate anything about your configuration.\n");
00136    }
00137 
00138    ast_cli(a->fd, "%8s %5s %8s %s\n","ID","TYPE","NAME","DESCRIPTION");
00139    ast_cli(a->fd, "-----------------------------------------------------------------------------------\n");
00140 
00141    ao2_rdlock(codecs);
00142    i = ao2_iterator_init(codecs, AO2_ITERATOR_DONTLOCK);
00143 
00144    for (; (codec = ao2_iterator_next(&i)); ao2_ref(codec, -1)) {
00145       if (a->argc == 4) {
00146          if (!strcasecmp(a->argv[3], "audio")) {
00147             if (codec->type != AST_MEDIA_TYPE_AUDIO) {
00148                continue;
00149             }
00150          } else if (!strcasecmp(a->argv[3], "video")) {
00151             if (codec->type != AST_MEDIA_TYPE_VIDEO) {
00152                continue;
00153             }
00154          } else if (!strcasecmp(a->argv[3], "image")) {
00155             if (codec->type != AST_MEDIA_TYPE_IMAGE) {
00156                continue;
00157             }
00158          } else if (!strcasecmp(a->argv[3], "text")) {
00159             if (codec->type != AST_MEDIA_TYPE_TEXT) {
00160                continue;
00161             }
00162          } else {
00163             continue;
00164          }
00165       }
00166 
00167       ast_cli(a->fd, "%8u %5s %8s (%s)\n",
00168          codec->id,
00169          ast_codec_media_type2str(codec->type),
00170          codec->name,
00171          codec->description);
00172    }
00173 
00174    ao2_iterator_destroy(&i);
00175    ao2_unlock(codecs);
00176 
00177    return CLI_SUCCESS;
00178 }
00179 
00180 /*! \brief Callback function for getting a codec based on unique identifier */
00181 static int codec_id_cmp(void *obj, void *arg, int flags)
00182 {
00183    struct ast_codec *codec = obj;
00184    int *id = arg;
00185 
00186    return (codec->id == *id) ? CMP_MATCH | CMP_STOP : 0;
00187 }
00188 
00189 static char *show_codec(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00190 {
00191    int type_punned_codec;
00192    struct ast_codec *codec;
00193 
00194    switch (cmd) {
00195    case CLI_INIT:
00196       e->command = "core show codec";
00197       e->usage =
00198          "Usage: core show codec <number>\n"
00199          "       Displays codec mapping\n";
00200       return NULL;
00201    case CLI_GENERATE:
00202       return NULL;
00203    }
00204 
00205    if (a->argc != 4) {
00206       return CLI_SHOWUSAGE;
00207    }
00208 
00209    if (sscanf(a->argv[3], "%30d", &type_punned_codec) != 1) {
00210       return CLI_SHOWUSAGE;
00211    }
00212 
00213    codec = ao2_callback(codecs, 0, codec_id_cmp, &type_punned_codec);
00214    if (!codec) {
00215       ast_cli(a->fd, "Codec %d not found\n", type_punned_codec);
00216       return CLI_SUCCESS;
00217    }
00218 
00219    ast_cli(a->fd, "%11u %s\n", (unsigned int) codec->id, codec->description);
00220 
00221    ao2_ref(codec, -1);
00222 
00223    return CLI_SUCCESS;
00224 }
00225 
00226 /* Builtin Asterisk CLI-commands for debugging */
00227 static struct ast_cli_entry codec_cli[] = {
00228    AST_CLI_DEFINE(show_codecs, "Displays a list of registered codecs"),
00229    AST_CLI_DEFINE(show_codec, "Shows a specific codec"),
00230 };
00231 
00232 /*! \brief Function called when the process is shutting down */
00233 static void codec_shutdown(void)
00234 {
00235    ast_cli_unregister_multiple(codec_cli, ARRAY_LEN(codec_cli));
00236    ao2_cleanup(codecs);
00237    codecs = NULL;
00238 }
00239 
00240 int ast_codec_init(void)
00241 {
00242    codecs = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, CODEC_BUCKETS, codec_hash, codec_cmp);
00243    if (!codecs) {
00244       return -1;
00245    }
00246 
00247    ast_cli_register_multiple(codec_cli, ARRAY_LEN(codec_cli));
00248    ast_register_cleanup(codec_shutdown);
00249 
00250    return 0;
00251 }
00252 
00253 static void codec_dtor(void *obj)
00254 {
00255    struct ast_codec *codec;
00256 
00257    codec = obj;
00258 
00259    ast_module_unref(codec->mod);
00260 }
00261 
00262 int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
00263 {
00264    SCOPED_AO2WRLOCK(lock, codecs);
00265    struct ast_codec *codec_new;
00266 
00267    /* Some types have specific requirements */
00268    if (codec->type == AST_MEDIA_TYPE_UNKNOWN) {
00269       ast_log(LOG_ERROR, "A media type must be specified for codec '%s'\n", codec->name);
00270       return -1;
00271    } else if (codec->type == AST_MEDIA_TYPE_AUDIO) {
00272       if (!codec->sample_rate) {
00273          ast_log(LOG_ERROR, "A sample rate must be specified for codec '%s' of type '%s'\n",
00274             codec->name, ast_codec_media_type2str(codec->type));
00275          return -1;
00276       }
00277    }
00278 
00279    codec_new = ao2_find(codecs, codec, OBJ_SEARCH_OBJECT | OBJ_NOLOCK);
00280    if (codec_new) {
00281       ast_log(LOG_ERROR, "A codec with name '%s' of type '%s' and sample rate '%u' is already registered\n",
00282          codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
00283       ao2_ref(codec_new, -1);
00284       return -1;
00285    }
00286 
00287    codec_new = ao2_t_alloc_options(sizeof(*codec_new), codec_dtor,
00288       AO2_ALLOC_OPT_LOCK_NOLOCK, S_OR(codec->description, ""));
00289    if (!codec_new) {
00290       ast_log(LOG_ERROR, "Could not allocate a codec with name '%s' of type '%s' and sample rate '%u'\n",
00291          codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
00292       return -1;
00293    }
00294    *codec_new = *codec;
00295    codec_new->id = codec_id++;
00296 
00297    ao2_link_flags(codecs, codec_new, OBJ_NOLOCK);
00298 
00299    /* Once registered a codec can not be unregistered, and the module must persist until shutdown */
00300    ast_module_shutdown_ref(mod);
00301 
00302    ast_verb(2, "Registered '%s' codec '%s' at sample rate '%u' with id '%u'\n",
00303       ast_codec_media_type2str(codec->type), codec->name, codec->sample_rate, codec_new->id);
00304 
00305    ao2_ref(codec_new, -1);
00306 
00307    return 0;
00308 }
00309 
00310 struct ast_codec *ast_codec_get(const char *name, enum ast_media_type type, unsigned int sample_rate)
00311 {
00312    struct ast_codec codec = {
00313       .name = name,
00314       .type = type,
00315       .sample_rate = sample_rate,
00316    };
00317 
00318    return ao2_find(codecs, &codec, OBJ_SEARCH_OBJECT);
00319 }
00320 
00321 struct ast_codec *ast_codec_get_by_id(int id)
00322 {
00323    return ao2_callback(codecs, 0, codec_id_cmp, &id);
00324 }
00325 
00326 int ast_codec_get_max(void)
00327 {
00328    return codec_id;
00329 }
00330 
00331 const char *ast_codec_media_type2str(enum ast_media_type type)
00332 {
00333    switch (type) {
00334    case AST_MEDIA_TYPE_AUDIO:
00335       return "audio";
00336    case AST_MEDIA_TYPE_VIDEO:
00337       return "video";
00338    case AST_MEDIA_TYPE_IMAGE:
00339       return "image";
00340    case AST_MEDIA_TYPE_TEXT:
00341       return "text";
00342    default:
00343       return "<unknown>";
00344    }
00345 }
00346 
00347 unsigned int ast_codec_samples_count(struct ast_frame *frame)
00348 {
00349    struct ast_codec *codec;
00350    unsigned int samples = 0;
00351 
00352    if ((frame->frametype != AST_FRAME_VOICE) &&
00353       (frame->frametype != AST_FRAME_VIDEO) &&
00354       (frame->frametype != AST_FRAME_IMAGE)) {
00355       return 0;
00356    }
00357 
00358    codec = ast_format_get_codec(frame->subclass.format);
00359 
00360    if (codec->samples_count) {
00361       samples = codec->samples_count(frame);
00362    } else {
00363       ast_log(LOG_WARNING, "Unable to calculate samples for codec %s\n",
00364          ast_format_get_name(frame->subclass.format));
00365    }
00366 
00367    ao2_ref(codec, -1);
00368    return samples;
00369 }
00370 
00371 unsigned int ast_codec_determine_length(const struct ast_codec *codec, unsigned int samples)
00372 {
00373    if (!codec->get_length) {
00374       return 0;
00375    }
00376 
00377    return codec->get_length(samples);
00378 }

Generated on Thu Apr 16 06:27:30 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6