diff options
Diffstat (limited to 'main/translate.c')
-rw-r--r-- | main/translate.c | 775 |
1 files changed, 421 insertions, 354 deletions
diff --git a/main/translate.c b/main/translate.c index c24700b45..0696f5d83 100644 --- a/main/translate.c +++ b/main/translate.c @@ -43,6 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/sched.h" #include "asterisk/cli.h" #include "asterisk/term.h" +#include "asterisk/format.h" /*! \todo * TODO: sample frames for each supported input format. @@ -76,11 +77,11 @@ struct translator_path { static struct translator_path **__matrix; /*! - * \brief table for converting index to format id values. + * \brief table for converting index to format values. * * \note this table is protected by the table_lock. */ -static int *__indextable; +static unsigned int *__indextable; /*! protects the __indextable for resizing */ static ast_rwlock_t tablelock; @@ -99,9 +100,9 @@ static void matrix_rebuild(int samples); /*! * \internal - * \brief converts format id to index value. + * \brief converts codec id to index value. */ -static int format2index(enum ast_format_id id) +static int codec_to_index(unsigned int id) { int x; @@ -119,16 +120,34 @@ static int format2index(enum ast_format_id id) /*! * \internal - * \brief add a new format to the matrix and index table structures. + * \brief converts codec to index value. + */ +static int codec2index(struct ast_codec *codec) +{ + return codec_to_index(codec->id); +} + +/*! + * \internal + * \brief converts format to codec index value. + */ +static int format2index(struct ast_format *format) +{ + return codec_to_index(ast_format_get_codec_id(format)); +} + +/*! + * \internal + * \brief add a new codec to the matrix and index table structures. * - * \note it is perfectly safe to call this on formats already indexed. + * \note it is perfectly safe to call this on codecs already indexed. * * \retval 0, success * \retval -1, matrix and index table need to be resized */ -static int add_format2index(enum ast_format_id id) +static int add_codec2index(struct ast_codec *codec) { - if (format2index(id) >= 0) { + if (codec2index(codec) != -1) { /* format is already already indexed */ return 0; } @@ -138,7 +157,7 @@ static int add_format2index(enum ast_format_id id) ast_rwlock_unlock(&tablelock); return -1; /* hit max length */ } - __indextable[cur_max_index] = id; + __indextable[cur_max_index] = codec->id; cur_max_index++; ast_rwlock_unlock(&tablelock); @@ -147,20 +166,20 @@ static int add_format2index(enum ast_format_id id) /*! * \internal - * \brief converts index value back to format id + * \brief converts index value back to codec */ -static enum ast_format_id index2format(int index) +static struct ast_codec *index2codec(int index) { - enum ast_format_id format_id; + struct ast_codec *codec; if (index >= cur_max_index) { return 0; } ast_rwlock_rdlock(&tablelock); - format_id = __indextable[index]; + codec = ast_codec_get_by_id(__indextable[index]); ast_rwlock_unlock(&tablelock); - return format_id; + return codec; } /*! @@ -176,7 +195,7 @@ static enum ast_format_id index2format(int index) static int matrix_resize(int init) { struct translator_path **tmp_matrix = NULL; - int *tmp_table = NULL; + unsigned int *tmp_table = NULL; int old_index; int x; @@ -202,7 +221,7 @@ static int matrix_resize(int init) } /* make new index table */ - if (!(tmp_table = ast_calloc(1, sizeof(int) * index_size))) { + if (!(tmp_table = ast_calloc(1, sizeof(unsigned int) * index_size))) { goto resize_cleanup; } @@ -213,7 +232,7 @@ static int matrix_resize(int init) } ast_free(__matrix); - memcpy(tmp_table, __indextable, sizeof(int) * old_index); + memcpy(tmp_table, __indextable, sizeof(unsigned int) * old_index); ast_free(__indextable); } @@ -270,11 +289,23 @@ static struct translator_path *matrix_get(unsigned int x, unsigned int y) * wrappers around the translator routines. */ +static void destroy(struct ast_trans_pvt *pvt) +{ + struct ast_translator *t = pvt->t; + + if (t->destroy) { + t->destroy(pvt); + } + ao2_cleanup(pvt->f.subclass.format); + ast_free(pvt); + ast_module_unref(t->module); +} + /*! * \brief Allocate the descriptor, required outbuf space, * and possibly desc. */ -static void *newpvt(struct ast_translator *t, const struct ast_format *explicit_dst) +static struct ast_trans_pvt *newpvt(struct ast_translator *t) { struct ast_trans_pvt *pvt; int len; @@ -300,28 +331,49 @@ static void *newpvt(struct ast_translator *t, const struct ast_format *explicit_ if (t->buf_size) {/* finally buffer and header */ pvt->outbuf.c = ofs + AST_FRIENDLY_OFFSET; } - /* if a explicit destination format is provided, set that on the pvt so the - * translator will process it. */ - if (explicit_dst) { - ast_format_copy(&pvt->explicit_dst, explicit_dst); - } + + ast_module_ref(t->module); + /* call local init routine, if present */ if (t->newpvt && t->newpvt(pvt)) { ast_free(pvt); + ast_module_unref(t->module); return NULL; } - ast_module_ref(t->module); - return pvt; -} -static void destroy(struct ast_trans_pvt *pvt) -{ - struct ast_translator *t = pvt->t; + /* Setup normal static translation frame. */ + pvt->f.frametype = AST_FRAME_VOICE; + pvt->f.mallocd = 0; + pvt->f.offset = AST_FRIENDLY_OFFSET; + pvt->f.src = pvt->t->name; + pvt->f.data.ptr = pvt->outbuf.c; - if (t->destroy) - t->destroy(pvt); - ast_free(pvt); - ast_module_unref(t->module); + /* if the translator has not provided a format find one in the cache or create one */ + if (!pvt->f.subclass.format) { + if (!ast_strlen_zero(pvt->t->format)) { + pvt->f.subclass.format = ast_format_cache_get(pvt->t->format); + } + + if (!pvt->f.subclass.format) { + struct ast_codec *codec = ast_codec_get(t->dst_codec.name, + t->dst_codec.type, t->dst_codec.sample_rate); + if (!codec) { + ast_log(LOG_ERROR, "Unable to get destination codec\n"); + destroy(pvt); + return NULL; + } + pvt->f.subclass.format = ast_format_create(codec); + ao2_ref(codec, -1); + } + + if (!pvt->f.subclass.format) { + ast_log(LOG_ERROR, "Unable to create format\n"); + destroy(pvt); + return NULL; + } + } + + return pvt; } /*! \brief framein wrapper, deals with bound checks. */ @@ -374,8 +426,9 @@ struct ast_frame *ast_trans_frameout(struct ast_trans_pvt *pvt, if (samples) { f->samples = samples; } else { - if (pvt->samples == 0) + if (pvt->samples == 0) { return NULL; + } f->samples = pvt->samples; pvt->samples = 0; } @@ -386,13 +439,6 @@ struct ast_frame *ast_trans_frameout(struct ast_trans_pvt *pvt, pvt->datalen = 0; } - f->frametype = AST_FRAME_VOICE; - ast_format_copy(&f->subclass.format, &pvt->t->dst_format); - f->mallocd = 0; - f->offset = AST_FRIENDLY_OFFSET; - f->src = pvt->t->name; - f->data.ptr = pvt->outbuf.c; - return ast_frisolate(f); } @@ -417,11 +463,9 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a { struct ast_trans_pvt *head = NULL, *tail = NULL; int src_index, dst_index; - struct ast_format tmp_fmt1; - struct ast_format tmp_fmt2; - src_index = format2index(src->id); - dst_index = format2index(dst->id); + src_index = format2index(src); + dst_index = format2index(dst); if (src_index < 0 || dst_index < 0) { ast_log(LOG_WARNING, "No translator path: (%s codec is not valid)\n", src_index < 0 ? "starting" : "ending"); @@ -432,26 +476,16 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a while (src_index != dst_index) { struct ast_trans_pvt *cur; - struct ast_format *explicit_dst = NULL; struct ast_translator *t = matrix_get(src_index, dst_index)->step; if (!t) { - int src_id = index2format(src_index); - int dst_id = index2format(dst_index); ast_log(LOG_WARNING, "No translator path from %s to %s\n", - ast_getformatname(ast_format_set(&tmp_fmt1, src_id, 0)), - ast_getformatname(ast_format_set(&tmp_fmt2, dst_id, 0))); + ast_format_get_name(src), ast_format_get_name(dst)); AST_RWLIST_UNLOCK(&translators); return NULL; } - if (dst_index == t->dst_fmt_index) { - explicit_dst = dst; - } - if (!(cur = newpvt(t, explicit_dst))) { - int src_id = index2format(src_index); - int dst_id = index2format(dst_index); + if (!(cur = newpvt(t))) { ast_log(LOG_WARNING, "Failed to build translator step from %s to %s\n", - ast_getformatname(ast_format_set(&tmp_fmt1, src_id, 0)), - ast_getformatname(ast_format_set(&tmp_fmt2, dst_id, 0))); + ast_format_get_name(src), ast_format_get_name(dst)); if (head) { ast_translator_free_path(head); } @@ -508,7 +542,8 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, path->nextout = f->delivery; } /* Predict next incoming sample */ - path->nextin = ast_tvadd(path->nextin, ast_samp2tv(f->samples, ast_format_rate(&f->subclass.format))); + path->nextin = ast_tvadd(path->nextin, ast_samp2tv( + f->samples, ast_format_get_sample_rate(f->subclass.format))); } delivery = f->delivery; for (out = f; out && p ; p = p->next) { @@ -531,7 +566,8 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, /* Predict next outgoing timestamp from samples in this frame. */ - path->nextout = ast_tvadd(path->nextout, ast_samp2tv(out->samples, ast_format_rate(&out->subclass.format))); + path->nextout = ast_tvadd(path->nextout, ast_samp2tv( + out->samples, ast_format_get_sample_rate(out->subclass.format))); if (f->samples != out->samples && ast_test_flag(out, AST_FRFLAG_HAS_TIMING_INFO)) { ast_debug(4, "Sample size different %d vs %d\n", f->samples, out->samples); ast_clear_flag(out, AST_FRFLAG_HAS_TIMING_INFO); @@ -572,7 +608,7 @@ static void generate_computational_cost(struct ast_translator *t, int seconds) struct rusage start; struct rusage end; int cost; - int out_rate = ast_format_rate(&t->dst_format); + int out_rate = t->dst_codec.sample_rate; if (!seconds) { seconds = 1; @@ -585,7 +621,7 @@ static void generate_computational_cost(struct ast_translator *t, int seconds) return; } - pvt = newpvt(t, NULL); + pvt = newpvt(t); if (!pvt) { ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name); t->comp_cost = 999999; @@ -643,21 +679,23 @@ static void generate_computational_cost(struct ast_translator *t, int seconds) * \retval Table Cost value greater than 0. * \retval 0 on error. */ -static int generate_table_cost(struct ast_format *src, struct ast_format *dst) +static int generate_table_cost(struct ast_codec *src, struct ast_codec *dst) { - int src_rate = ast_format_rate(src); + int src_rate = src->sample_rate; int src_ll = 0; - int dst_rate = ast_format_rate(dst); + int dst_rate = dst->sample_rate; int dst_ll = 0; - if ((AST_FORMAT_GET_TYPE(src->id) != AST_FORMAT_TYPE_AUDIO) || (AST_FORMAT_GET_TYPE(dst->id) != AST_FORMAT_TYPE_AUDIO)) { + if ((src->type != AST_MEDIA_TYPE_AUDIO) || + (dst->type != AST_MEDIA_TYPE_AUDIO)) { /* This method of generating table cost is limited to audio. * Translators for media other than audio must manually set their * table cost. */ return 0; } - src_ll = ast_format_is_slinear(src); - dst_ll = ast_format_is_slinear(dst); + + src_ll = !strcmp(src->name, "slin"); + dst_ll = !strcmp(dst->name, "slin"); if (src_ll) { if (dst_ll && (src_rate == dst_rate)) { return AST_TRANS_COST_LL_LL_ORIGSAMP; @@ -763,18 +801,25 @@ static void matrix_rebuild(int samples) /* if no step already exists between x and z OR the new cost of using the intermediate * step is cheaper, use this step. */ if (!matrix_get(x, z)->step || (newtablecost < matrix_get(x, z)->table_cost)) { - struct ast_format tmpx; - struct ast_format tmpy; - struct ast_format tmpz; matrix_get(x, z)->step = matrix_get(x, y)->step; matrix_get(x, z)->table_cost = newtablecost; matrix_get(x, z)->multistep = 1; changed++; - ast_debug(10, "Discovered %u cost path from %s to %s, via %s\n", - matrix_get(x, z)->table_cost, - ast_getformatname(ast_format_set(&tmpx, index2format(x), 0)), - ast_getformatname(ast_format_set(&tmpy, index2format(z), 0)), - ast_getformatname(ast_format_set(&tmpz, index2format(y), 0))); + + if (DEBUG_ATLEAST(10)) { + struct ast_codec *x_codec = index2codec(x); + struct ast_codec *y_codec = index2codec(y); + struct ast_codec *z_codec = index2codec(z); + + ast_log(LOG_DEBUG, + "Discovered %u cost path from %s to %s, via %s\n", + matrix_get(x, z)->table_cost, x_codec->name, + y_codec->name, z_codec->name); + + ao2_ref(x_codec, -1); + ao2_ref(y_codec, -1); + ao2_ref(z_codec, -1); + } } } } @@ -785,20 +830,26 @@ static void matrix_rebuild(int samples) } } -const char *ast_translate_path_to_str(struct ast_trans_pvt *p, struct ast_str **str) +static void codec_append_name(const struct ast_codec *codec, struct ast_str **buf) { - struct ast_trans_pvt *pn = p; - char tmp[256]; + if (codec) { + ast_str_append(buf, 0, "(%s@%u)", codec->name, codec->sample_rate); + } else { + ast_str_append(buf, 0, "(nothing)"); + } +} +const char *ast_translate_path_to_str(struct ast_trans_pvt *p, struct ast_str **str) +{ if (!p || !p->t) { return ""; } - ast_str_set(str, 0, "%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), p->t->src_format.id)); - - while ( (p = pn) ) { - pn = p->next; - ast_str_append(str, 0, "->%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), p->t->dst_format.id)); + codec_append_name(&p->t->src_codec, str); + while (p) { + ast_str_append(str, 0, "->"); + codec_append_name(&p->t->dst_codec, str); + p = p->next; } return ast_str_buffer(*str); @@ -806,24 +857,24 @@ const char *ast_translate_path_to_str(struct ast_trans_pvt *p, struct ast_str ** static char *complete_trans_path_choice(const char *line, const char *word, int pos, int state) { - int which = 0; + int i = 1, which = 0; int wordlen = strlen(word); - int i; - char *ret = NULL; - size_t len = 0; - const struct ast_format_list *format_list = ast_format_list_get(&len); + struct ast_codec *codec; - for (i = 0; i < len; i++) { - if (AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) { + while ((codec = ast_codec_get_by_id(i))) { + ++i; + if (codec->type != AST_MEDIA_TYPE_AUDIO) { + ao2_ref(codec, -1); continue; } - if (!strncasecmp(word, format_list[i].name, wordlen) && ++which > state) { - ret = ast_strdup(format_list[i].name); - break; + if (!strncasecmp(word, codec->name, wordlen) && ++which > state) { + char *res = ast_strdup(codec->name); + ao2_ref(codec, -1); + return res; } + ao2_ref(codec, -1); } - ast_format_list_destroy(format_list); - return ret; + return NULL; } static void handle_cli_recalc(struct ast_cli_args *a) @@ -847,63 +898,61 @@ static void handle_cli_recalc(struct ast_cli_args *a) static char *handle_show_translation_table(struct ast_cli_args *a) { - int x; - int y; - int i; - int k; - int curlen = 0; - int longest = 0; - int f_len; - size_t f_size = 0; - const struct ast_format_list *f_list = ast_format_list_get(&f_size); + int x, y, i, k; + int longest = 0, num_codecs = 0, curlen = 0; struct ast_str *out = ast_str_create(1024); + struct ast_codec *codec; - f_len = f_size; - AST_RWLIST_RDLOCK(&translators); - ast_cli(a->fd, " Translation times between formats (in microseconds) for one second of data\n"); - ast_cli(a->fd, " Source Format (Rows) Destination Format (Columns)\n\n"); - - /* Get the length of the longest (usable?) codec name, so we know how wide the left side should be */ - for (i = 0; i < f_len; i++) { - /* translation only applies to audio right now. */ - if (AST_FORMAT_GET_TYPE(f_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) + /* Get the length of the longest (usable?) codec name, + so we know how wide the left side should be */ + for (i = 1; (codec = ast_codec_get_by_id(i)); ao2_ref(codec, -1), ++i) { + ++num_codecs; + if (codec->type != AST_MEDIA_TYPE_AUDIO) { continue; - curlen = strlen(ast_getformatname(&f_list[i].format)); + } + curlen = strlen(codec->name); if (curlen > longest) { longest = curlen; } } - for (i = -1; i < f_len; i++) { + AST_RWLIST_RDLOCK(&translators); + ast_cli(a->fd, " Translation times between formats (in microseconds) for one second of data\n"); + ast_cli(a->fd, " Source Format (Rows) Destination Format (Columns)\n\n"); + + for (i = 0; i < num_codecs; i++) { + struct ast_codec *row = i ? ast_codec_get_by_id(i) : NULL; + x = -1; - if ((i >= 0) && ((x = format2index(f_list[i].format.id)) < 0)) { - continue; - } - /* translation only applies to audio right now. */ - if (i >= 0 && (AST_FORMAT_GET_TYPE(f_list[i].format.id) != AST_FORMAT_TYPE_AUDIO)) { + if ((i > 0) && (row->type != AST_MEDIA_TYPE_AUDIO)) { + ao2_ref(row, -1); continue; } - /*Go ahead and move to next iteration if dealing with an unknown codec*/ - if (i >= 0 && !strcmp(ast_getformatname(&f_list[i].format), "unknown")) { + + if ((i > 0) && (x = codec2index(row)) == -1) { + ao2_ref(row, -1); continue; } + ast_str_set(&out, 0, " "); - for (k = -1; k < f_len; k++) { + for (k = 0; k < num_codecs; k++) { + struct ast_codec *col = k ? ast_codec_get_by_id(k) : NULL; + y = -1; - if ((k >= 0) && ((y = format2index(f_list[k].format.id)) < 0)) { + if ((k > 0) && (col->type != AST_MEDIA_TYPE_AUDIO)) { + ao2_ref(col, -1); continue; } - /* translation only applies to audio right now. */ - if (k >= 0 && (AST_FORMAT_GET_TYPE(f_list[k].format.id) != AST_FORMAT_TYPE_AUDIO)) { - continue; - } - /*Go ahead and move to next iteration if dealing with an unknown codec*/ - if (k >= 0 && !strcmp(ast_getformatname(&f_list[k].format), "unknown")) { + + if ((k > 0) && (y = codec2index(col)) == -1) { + ao2_ref(col, -1); continue; } - if (k >= 0) { - curlen = strlen(ast_getformatname(&f_list[k].format)); + + if (k > 0) { + curlen = strlen(col->name); } + if (curlen < 5) { curlen = 5; } @@ -911,12 +960,12 @@ static char *handle_show_translation_table(struct ast_cli_args *a) if (x >= 0 && y >= 0 && matrix_get(x, y)->step) { /* Actual codec output */ ast_str_append(&out, 0, "%*u", curlen + 1, (matrix_get(x, y)->table_cost/100)); - } else if (i == -1 && k >= 0) { + } else if (i == 0 && k > 0) { /* Top row - use a dynamic size */ - ast_str_append(&out, 0, "%*s", curlen + 1, ast_getformatname(&f_list[k].format)); - } else if (k == -1 && i >= 0) { + ast_str_append(&out, 0, "%*s", curlen + 1, col->name); + } else if (k == 0 && i > 0) { /* Left column - use a static size. */ - ast_str_append(&out, 0, "%*s", longest, ast_getformatname(&f_list[i].format)); + ast_str_append(&out, 0, "%*s", longest, row->name); } else if (x >= 0 && y >= 0) { /* Codec not supported */ ast_str_append(&out, 0, "%*s", curlen + 1, "-"); @@ -924,74 +973,79 @@ static char *handle_show_translation_table(struct ast_cli_args *a) /* Upper left hand corner */ ast_str_append(&out, 0, "%*s", longest, ""); } + ao2_cleanup(col); } ast_str_append(&out, 0, "\n"); ast_cli(a->fd, "%s", ast_str_buffer(out)); + ao2_cleanup(row); } ast_free(out); AST_RWLIST_UNLOCK(&translators); - ast_format_list_destroy(f_list); return CLI_SUCCESS; } -static char *handle_show_translation_path(struct ast_cli_args *a) +static char *handle_show_translation_path(struct ast_cli_args *a, const char *codec_name, unsigned int sample_rate) { - struct ast_format input_src_format; - size_t len = 0; - int i; - const struct ast_format_list *format_list = ast_format_list_get(&len); + int i = 1; struct ast_str *str = ast_str_alloca(1024); struct ast_translator *step; - char tmp[256]; + struct ast_codec *dst_codec; + struct ast_codec *src_codec = ast_codec_get(codec_name, AST_MEDIA_TYPE_AUDIO, sample_rate); - ast_format_clear(&input_src_format); - for (i = 0; i < len; i++) { - if (AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) { - continue; - } - if (!strncasecmp(format_list[i].name, a->argv[4], strlen(format_list[i].name))) { - ast_format_copy(&input_src_format, &format_list[i].format); - } - } - - if (!input_src_format.id) { - ast_cli(a->fd, "Source codec \"%s\" is not found.\n", a->argv[4]); - ast_format_list_destroy(format_list); + if (!src_codec) { + ast_cli(a->fd, "Source codec \"%s\" is not found.\n", codec_name); return CLI_FAILURE; } AST_RWLIST_RDLOCK(&translators); - ast_cli(a->fd, "--- Translation paths SRC Codec \"%s\" sample rate %d ---\n", a->argv[4], ast_format_rate(&input_src_format)); - for (i = 0; i < len; i++) { - int src; - int dst; - if ((AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) || (format_list[i].format.id == input_src_format.id)) { + ast_cli(a->fd, "--- Translation paths SRC Codec \"%s\" sample rate %u ---\n", + codec_name, src_codec->sample_rate); + + while ((dst_codec = ast_codec_get_by_id(i))) { + int src, dst; + char src_buffer[64]; + char dst_buffer[64]; + + ++i; + if (src_codec == dst_codec || + dst_codec->type != AST_MEDIA_TYPE_AUDIO) { + ao2_ref(dst_codec, -1); continue; } - dst = format2index(format_list[i].format.id); - src = format2index(input_src_format.id); - ast_str_reset(str); - if ((len >= cur_max_index) && (src >= 0) && (dst >= 0) && matrix_get(src, dst)->step) { - ast_str_append(&str, 0, "%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), matrix_get(src, dst)->step->src_format.id)); - while (src != dst) { - step = matrix_get(src, dst)->step; - if (!step) { - ast_str_reset(str); - break; + + dst = codec2index(dst_codec); + src = codec2index(src_codec); + + if (src < 0 || dst < 0) { + ast_str_set(&str, 0, "No Translation Path"); + } else { + step = matrix_get(src, dst)->step; + + if (step) { + codec_append_name(&step->src_codec, &str); + while (src != dst) { + src = step->dst_fmt_index; + step = matrix_get(src, dst)->step; + if (!step) { + ast_str_append(&str, 0, "->"); + codec_append_name(dst_codec, &str); + break; + } + ast_str_append(&str, 0, "->"); + codec_append_name(&step->src_codec, &str); } - ast_str_append(&str, 0, "->%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), step->dst_format.id)); - src = step->dst_fmt_index; } } - if (ast_strlen_zero(ast_str_buffer(str))) { - ast_str_set(&str, 0, "No Translation Path"); - } - ast_cli(a->fd, "\t%-10.10s To %-10.10s: %-60.60s\n", a->argv[4], format_list[i].name, ast_str_buffer(str)); + snprintf(src_buffer, sizeof(src_buffer), "%s:%u", src_codec->name, src_codec->sample_rate); + snprintf(dst_buffer, sizeof(dst_buffer), "%s:%u", dst_codec->name, dst_codec->sample_rate); + ast_cli(a->fd, "\t%-16.16s To %-16.16s: %-60.60s\n", + src_buffer, dst_buffer, ast_str_buffer(str)); + ast_str_reset(str); + ao2_ref(dst_codec, -1); } AST_RWLIST_UNLOCK(&translators); - - ast_format_list_destroy(format_list); + ao2_ref(src_codec, -1); return CLI_SUCCESS; } @@ -1009,8 +1063,10 @@ static char *handle_cli_core_show_translation(struct ast_cli_entry *e, int cmd, " with each conversion. If the argument 'recalc' is supplied along\n" " with optional number of seconds to test a new test will be performed\n" " as the chart is being displayed.\n" - " 2. 'core show translation paths [codec]'\n" - " This will display all the translation paths associated with a codec\n"; + " 2. 'core show translation paths [codec [sample_rate]]'\n" + " This will display all the translation paths associated with a codec.\n" + " If a codec has multiple sample rates, the sample rate must be\n" + " provided as well.\n"; return NULL; case CLI_GENERATE: if (a->pos == 3) { @@ -1019,14 +1075,22 @@ static char *handle_cli_core_show_translation(struct ast_cli_entry *e, int cmd, if (a->pos == 4 && !strcasecmp(a->argv[3], option[1])) { return complete_trans_path_choice(a->line, a->word, a->pos, a->n); } + /* BUGBUG - add tab completion for sample rates */ return NULL; } - if (a->argc > 5) + if (a->argc > 6) return CLI_SHOWUSAGE; if (a->argv[3] && !strcasecmp(a->argv[3], option[1]) && a->argc == 5) { /* show paths */ - return handle_show_translation_path(a); + return handle_show_translation_path(a, a->argv[4], 0); + } else if (a->argv[3] && !strcasecmp(a->argv[3], option[1]) && a->argc == 6) { + unsigned int sample_rate; + if (sscanf(a->argv[5], "%30u", &sample_rate) != 1) { + ast_cli(a->fd, "Invalid sample rate: %s.\n", a->argv[5]); + return CLI_FAILURE; + } + return handle_show_translation_path(a, a->argv[4], sample_rate); } else if (a->argv[3] && !strcasecmp(a->argv[3], option[0])) { /* recalc and then fall through to show table */ handle_cli_recalc(a); } else if (a->argc > 3) { /* wrong input */ @@ -1045,14 +1109,29 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) { struct ast_translator *u; char tmp[80]; + RAII_VAR(struct ast_codec *, src_codec, NULL, ao2_cleanup); + RAII_VAR(struct ast_codec *, dst_codec, NULL, ao2_cleanup); - if (add_format2index(t->src_format.id) || add_format2index(t->dst_format.id)) { + src_codec = ast_codec_get(t->src_codec.name, t->src_codec.type, t->src_codec.sample_rate); + if (!src_codec) { + ast_assert(0); + ast_log(LOG_WARNING, "Failed to register translator: unknown source codec %s\n", t->src_codec.name); + return -1; + } + + dst_codec = ast_codec_get(t->dst_codec.name, t->dst_codec.type, t->dst_codec.sample_rate); + if (!dst_codec) { + ast_log(LOG_WARNING, "Failed to register translator: unknown destination codec %s\n", t->dst_codec.name); + return -1; + } + + if (add_codec2index(src_codec) || add_codec2index(dst_codec)) { if (matrix_resize(0)) { ast_log(LOG_WARNING, "Translator matrix can not represent any more translators. Out of resources.\n"); return -1; } - add_format2index(t->src_format.id); - add_format2index(t->dst_format.id); + add_codec2index(src_codec); + add_codec2index(dst_codec); } if (!mod) { @@ -1064,15 +1143,15 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) ast_log(LOG_WARNING, "empty buf size, you need to supply one\n"); return -1; } - if (!t->table_cost && !(t->table_cost = generate_table_cost(&t->src_format, &t->dst_format))) { + if (!t->table_cost && !(t->table_cost = generate_table_cost(src_codec, dst_codec))) { ast_log(LOG_WARNING, "Table cost could not be generated for %s, " "Please set table_cost variable on translator.\n", t->name); return -1; } t->module = mod; - t->src_fmt_index = format2index(t->src_format.id); - t->dst_fmt_index = format2index(t->dst_format.id); + t->src_fmt_index = codec2index(src_codec); + t->dst_fmt_index = codec2index(dst_codec); t->active = 1; if (t->src_fmt_index < 0 || t->dst_fmt_index < 0) { @@ -1080,12 +1159,12 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) return -1; } if (t->src_fmt_index >= cur_max_index) { - ast_log(LOG_WARNING, "Source format %s is larger than cur_max_index\n", ast_getformatname(&t->src_format)); + ast_log(LOG_WARNING, "Source codec %s is larger than cur_max_index\n", t->src_codec.name); return -1; } if (t->dst_fmt_index >= cur_max_index) { - ast_log(LOG_WARNING, "Destination format %s is larger than cur_max_index\n", ast_getformatname(&t->dst_format)); + ast_log(LOG_WARNING, "Destination codec %s is larger than cur_max_index\n", t->dst_codec.name); return -1; } @@ -1106,9 +1185,9 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) generate_computational_cost(t, 1); - ast_verb(2, "Registered translator '%s' from format %s to %s, table cost, %d, computational cost %d\n", - term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)), - ast_getformatname(&t->src_format), ast_getformatname(&t->dst_format), t->table_cost, t->comp_cost); + ast_verb(2, "Registered translator '%s' from codec %s to %s, table cost, %d, computational cost %d\n", + term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)), + t->src_codec.name, t->dst_codec.name, t->table_cost, t->comp_cost); AST_RWLIST_WRLOCK(&translators); @@ -1125,7 +1204,7 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) } AST_RWLIST_TRAVERSE_SAFE_END; - /* if no existing translator was found for this format combination, + /* if no existing translator was found for this codec combination, add it to the beginning of the list */ if (t) { AST_RWLIST_INSERT_HEAD(&translators, t, list); @@ -1149,10 +1228,9 @@ int ast_unregister_translator(struct ast_translator *t) AST_RWLIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) { if (u == t) { AST_RWLIST_REMOVE_CURRENT(list); - ast_verb(2, "Unregistered translator '%s' from format %s to %s\n", + ast_verb(2, "Unregistered translator '%s' from codec %s to %s\n", term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)), - ast_getformatname(&t->src_format), - ast_getformatname(&t->dst_format)); + t->src_codec.name, t->dst_codec.name); found = 1; break; } @@ -1187,91 +1265,105 @@ void ast_translator_deactivate(struct ast_translator *t) /*! \brief Calculate our best translator source format, given costs, and a desired destination */ int ast_translator_best_choice(struct ast_format_cap *dst_cap, struct ast_format_cap *src_cap, - struct ast_format *dst_fmt_out, - struct ast_format *src_fmt_out) + struct ast_format **dst_fmt_out, + struct ast_format **src_fmt_out) { - struct ast_format best; - struct ast_format_cap *joint_cap = ast_format_cap_joint(dst_cap, src_cap); + unsigned int besttablecost = INT_MAX; + unsigned int beststeps = INT_MAX; + RAII_VAR(struct ast_format *, best, NULL, ao2_cleanup); + RAII_VAR(struct ast_format *, bestdst, NULL, ao2_cleanup); + RAII_VAR(struct ast_format_cap *, joint_cap, NULL, ao2_cleanup); + int i; + int j; - ast_format_clear(&best); + if (!(joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { + return -1; + } + ast_format_cap_get_compatible(dst_cap, src_cap, joint_cap); - if (joint_cap) { /* yes, pick one and return */ - struct ast_format tmp_fmt; + for (i = 0; i < ast_format_cap_count(joint_cap); ++i) { + struct ast_format *fmt = + ast_format_cap_get_format(joint_cap, i); - ast_format_cap_iter_start(joint_cap); - while (!ast_format_cap_iter_next(joint_cap, &tmp_fmt)) { - /* We are guaranteed to find one common format. */ - if (!best.id) { - ast_format_copy(&best, &tmp_fmt); - continue; - } - /* If there are multiple common formats, pick the one with the highest sample rate */ - if (ast_format_rate(&best) < ast_format_rate(&tmp_fmt)) { - ast_format_copy(&best, &tmp_fmt); - continue; - } + if (!fmt) { + continue; + } + + if (!best) { + /* No ao2_ref operations needed, we're done with fmt */ + best = fmt; + continue; } - ast_format_cap_iter_end(joint_cap); - /* We are done, this is a common format to both. */ - ast_format_copy(dst_fmt_out, &best); - ast_format_copy(src_fmt_out, &best); - ast_format_cap_destroy(joint_cap); + if (ast_format_get_sample_rate(best) < + ast_format_get_sample_rate(fmt)) { + ao2_replace(best, fmt); + } + ao2_ref(fmt, -1); + } + + if (best) { + ao2_replace(*dst_fmt_out, best); + ao2_replace(*src_fmt_out, best); return 0; - } else { /* No, we will need to translate */ - unsigned int besttablecost = INT_MAX; - unsigned int beststeps = INT_MAX; - struct ast_format cur_dst; - struct ast_format cur_src; - struct ast_format bestdst; + } + /* need to translate */ + AST_RWLIST_RDLOCK(&translators); - ast_format_clear(&bestdst); + for (i = 0; i < ast_format_cap_count(dst_cap); ++i) { + struct ast_format *dst = + ast_format_cap_get_format(dst_cap, i); - AST_RWLIST_RDLOCK(&translators); - ast_format_cap_iter_start(dst_cap); - while (!ast_format_cap_iter_next(dst_cap, &cur_dst)) { - ast_format_cap_iter_start(src_cap); - while (!ast_format_cap_iter_next(src_cap, &cur_src)) { - int x = format2index(cur_src.id); - int y = format2index(cur_dst.id); - struct translator_path *trans; - - if (x < 0 || y < 0) { - continue; - } - trans = matrix_get(x, y); - if (!trans || !trans->step) { - continue; - } - if (trans->table_cost < besttablecost || trans->multistep < beststeps) { - /* better than what we have so far */ - ast_format_copy(&best, &cur_src); - ast_format_copy(&bestdst, &cur_dst); - besttablecost = trans->table_cost; - beststeps = trans->multistep; - } - } - ast_format_cap_iter_end(src_cap); + if (!dst) { + continue; } - ast_format_cap_iter_end(dst_cap); - AST_RWLIST_UNLOCK(&translators); - if (best.id) { - ast_format_copy(dst_fmt_out, &bestdst); - ast_format_copy(src_fmt_out, &best); - return 0; + for (j = 0; j < ast_format_cap_count(src_cap); ++j) { + struct ast_format *src = + ast_format_cap_get_format(src_cap, j); + int x, y; + + if (!src) { + continue; + } + + x = format2index(src); + y = format2index(dst); + if (x < 0 || y < 0) { + ao2_ref(src, -1); + continue; + } + if (!matrix_get(x, y) || !(matrix_get(x, y)->step)) { + ao2_ref(src, -1); + continue; + } + if (((matrix_get(x, y)->table_cost < besttablecost) || + (matrix_get(x, y)->multistep < beststeps))) { + /* better than what we have so far */ + ao2_replace(best, src); + ao2_replace(bestdst, dst); + besttablecost = matrix_get(x, y)->table_cost; + beststeps = matrix_get(x, y)->multistep; + } + ao2_ref(src, -1); } + ao2_ref(dst, -1); + } + AST_RWLIST_UNLOCK(&translators); + if (!best) { return -1; } + ao2_replace(*dst_fmt_out, bestdst); + ao2_replace(*src_fmt_out, best); + return 0; } unsigned int ast_translate_path_steps(struct ast_format *dst_format, struct ast_format *src_format) { unsigned int res = -1; - int src, dest; /* convert bitwise format numbers into array indices */ - src = format2index(src_format->id); - dest = format2index(dst_format->id); + int src = format2index(src_format); + int dest = format2index(dst_format); if (src < 0 || dest < 0) { ast_log(LOG_WARNING, "No translator path: (%s codec is not valid)\n", src < 0 ? "starting" : "ending"); @@ -1288,25 +1380,72 @@ unsigned int ast_translate_path_steps(struct ast_format *dst_format, struct ast_ return res; } +static void check_translation_path( + struct ast_format_cap *dest, struct ast_format_cap *src, + struct ast_format_cap *result, struct ast_format *src_fmt, + enum ast_media_type type) +{ + int index, src_index = format2index(src_fmt); + /* For a given source format, traverse the list of + known formats to determine whether there exists + a translation path from the source format to the + destination format. */ + for (index = 0; (src_index >= 0) && index < cur_max_index; index++) { + struct ast_codec *codec = index2codec(index); + RAII_VAR(struct ast_format *, fmt, ast_format_create(codec), ao2_cleanup); + + ao2_ref(codec, -1); + + if (ast_format_get_type(fmt) != type) { + continue; + } + + /* if this is not a desired format, nothing to do */ + if (ast_format_cap_iscompatible_format(dest, fmt) == AST_FORMAT_CMP_NOT_EQUAL) { + continue; + } + + /* if the source is supplying this format, then + we can leave it in the result */ + if (ast_format_cap_iscompatible_format(src, fmt) == AST_FORMAT_CMP_EQUAL) { + continue; + } + + /* if we don't have a translation path from the src + to this format, remove it from the result */ + if (!matrix_get(src_index, index)->step) { + ast_format_cap_remove(result, fmt); + continue; + } + + /* now check the opposite direction */ + if (!matrix_get(index, src_index)->step) { + ast_format_cap_remove(result, fmt); + } + } + +} + void ast_translate_available_formats(struct ast_format_cap *dest, struct ast_format_cap *src, struct ast_format_cap *result) { - struct ast_format tmp_fmt; - struct ast_format cur_dest, cur_src; - int src_audio = 0; - int src_video = 0; + struct ast_format *cur_dest, *cur_src; int index; - ast_format_cap_iter_start(dest); - while (!ast_format_cap_iter_next(dest, &cur_dest)) { + for (index = 0; index < ast_format_cap_count(dest); ++index) { + if (!(cur_dest = ast_format_cap_get_format(dest, index))) { + continue; + } + /* We give preference to a joint format structure if possible */ - if (ast_format_cap_get_compatible_format(src, &cur_dest, &tmp_fmt)) { - ast_format_cap_add(result, &tmp_fmt); + if ((cur_src = ast_format_cap_get_compatible_format(src, cur_dest))) { + ast_format_cap_append(result, cur_src, 0); + ao2_ref(cur_src, -1); } else { /* Otherwise we just use the destination format */ - ast_format_cap_add(result, &cur_dest); + ast_format_cap_append(result, cur_dest, 0); } + ao2_ref(cur_dest, -1); } - ast_format_cap_iter_end(dest); /* if we don't have a source format, we just have to try all possible destination formats */ @@ -1314,91 +1453,19 @@ void ast_translate_available_formats(struct ast_format_cap *dest, struct ast_for return; } - ast_format_cap_iter_start(src); - while (!ast_format_cap_iter_next(src, &cur_src)) { - /* If we have a source audio format, get its format index */ - if (AST_FORMAT_GET_TYPE(cur_src.id) == AST_FORMAT_TYPE_AUDIO) { - src_audio = format2index(cur_src.id); - } - - /* If we have a source video format, get its format index */ - if (AST_FORMAT_GET_TYPE(cur_src.id) == AST_FORMAT_TYPE_VIDEO) { - src_video = format2index(cur_src.id); + for (index = 0; index < ast_format_cap_count(src); ++index) { + if (!(cur_src = ast_format_cap_get_format(src, index))) { + continue; } AST_RWLIST_RDLOCK(&translators); - - /* For a given source audio format, traverse the list of - known audio formats to determine whether there exists - a translation path from the source format to the - destination format. */ - for (index = 0; (src_audio >= 0) && index < cur_max_index; index++) { - ast_format_set(&tmp_fmt, index2format(index), 0); - - if (AST_FORMAT_GET_TYPE(tmp_fmt.id) != AST_FORMAT_TYPE_AUDIO) { - continue; - } - - /* if this is not a desired format, nothing to do */ - if (!ast_format_cap_iscompatible(dest, &tmp_fmt)) { - continue; - } - - /* if the source is supplying this format, then - we can leave it in the result */ - if (ast_format_cap_iscompatible(src, &tmp_fmt)) { - continue; - } - - /* if we don't have a translation path from the src - to this format, remove it from the result */ - if (!matrix_get(src_audio, index)->step) { - ast_format_cap_remove_byid(result, tmp_fmt.id); - continue; - } - - /* now check the opposite direction */ - if (!matrix_get(index, src_audio)->step) { - ast_format_cap_remove_byid(result, tmp_fmt.id); - } - } - - /* For a given source video format, traverse the list of - known video formats to determine whether there exists - a translation path from the source format to the - destination format. */ - for (index = 0; (src_video >= 0) && index < cur_max_index; index++) { - ast_format_set(&tmp_fmt, index2format(index), 0); - if (AST_FORMAT_GET_TYPE(tmp_fmt.id) != AST_FORMAT_TYPE_VIDEO) { - continue; - } - - /* if this is not a desired format, nothing to do */ - if (!ast_format_cap_iscompatible(dest, &tmp_fmt)) { - continue; - } - - /* if the source is supplying this format, then - we can leave it in the result */ - if (ast_format_cap_iscompatible(src, &tmp_fmt)) { - continue; - } - - /* if we don't have a translation path from the src - to this format, remove it from the result */ - if (!matrix_get(src_video, index)->step) { - ast_format_cap_remove_byid(result, tmp_fmt.id); - continue; - } - - /* now check the opposite direction */ - if (!matrix_get(index, src_video)->step) { - ast_format_cap_remove_byid(result, tmp_fmt.id); - } - } + check_translation_path(dest, src, result, + cur_src, AST_MEDIA_TYPE_AUDIO); + check_translation_path(dest, src, result, + cur_src, AST_MEDIA_TYPE_VIDEO); AST_RWLIST_UNLOCK(&translators); + ao2_ref(cur_src, -1); } - ast_format_cap_iter_end(src); } static void translate_shutdown(void) |