diff options
author | Matthew Jordan <mjordan@digium.com> | 2014-07-20 22:06:33 +0000 |
---|---|---|
committer | Matthew Jordan <mjordan@digium.com> | 2014-07-20 22:06:33 +0000 |
commit | a2c912e9972c91973ea66902d217746133f96026 (patch) | |
tree | 50e01d14ba62950e3f78766d5ba435ba51ca327d /main/translate.c | |
parent | b299052e203807c9a2111eb2cd919246d7589cb3 (diff) |
media formats: re-architect handling of media for performance improvements
In the old times media formats were represented using a bit field. This was
fast but had a few limitations.
1. Asterisk was limited in how many formats it could handle.
2. Formats, being a bit field, could not include any attribute information.
A format was strictly its type, e.g., "this is ulaw".
This was changed in Asterisk 10 (see
https://wiki.asterisk.org/wiki/display/AST/Media+Architecture+Proposal for
notes on that work) which led to the creation of the ast_format structure.
This structure allowed Asterisk to handle attributes and bundle information
with a format.
Additionally, ast_format_cap was created to act as a container for multiple
formats that, together, formed the capability of some entity. Another
mechanism was added to allow logic to be registered which performed format
attribute negotiation. Everywhere throughout the codebase Asterisk was
changed to use this strategy.
Unfortunately, in software, there is no free lunch. These new capabilities
came at a cost.
Performance analysis and profiling showed that we spend an inordinate
amount of time comparing, copying, and generally manipulating formats and
their related structures. Basic prototyping has shown that a reasonably
large performance improvement could be made in this area. This patch is the
result of that project, which overhauled the media format architecture
and its usage in Asterisk to improve performance.
Generally, the new philosophy for handling formats is as follows:
* The ast_format structure is reference counted. This removed a large amount
of the memory allocations and copying that was done in prior versions.
* In order to prevent race conditions while keeping things performant, the
ast_format structure is immutable by convention and lock-free. Violate this
tenet at your peril!
* Because formats are reference counted, codecs are also reference counted.
The Asterisk core generally provides built-in codecs and caches the
ast_format structures created to represent them. Generally, to prevent
inordinate amounts of module reference bumping, codecs and formats can be
added at run-time but cannot be removed.
* All compatibility with the bit field representation of codecs/formats has
been moved to a compatibility API. The primary user of this representation
is chan_iax2, which must continue to maintain its bit-field usage of formats
for interoperability concerns.
* When a format is negotiated with attributes, or when a format cannot be
represented by one of the cached formats, a new format object is created or
cloned from an existing format. That format may have the same codec
underlying it, but is a different format than a version of the format with
different attributes or without attributes.
* While formats are reference counted objects, the reference count maintained
on the format should be manipulated with care. Formats are generally cached
and will persist for the lifetime of Asterisk and do not explicitly need
to have their lifetime modified. An exception to this is when the user of a
format does not know where the format came from *and* the user may outlive
the provider of the format. This occurs, for example, when a format is read
from a channel: the channel may have a format with attributes (hence,
non-cached) and the user of the format may last longer than the channel (if
the reference to the channel is released prior to the format's reference).
For more information on this work, see the API design notes:
https://wiki.asterisk.org/wiki/display/AST/Media+Format+Rewrite
Finally, this work was the culmination of a large number of developer's
efforts. Extra thanks goes to Corey Farrell, who took on a large amount of the
work in the Asterisk core, chan_sip, and was an invaluable resource in peer
reviews throughout this project.
There were a substantial number of patches contributed during this work; the
following issues/patch names simply reflect some of the work (and will cause
the release scripts to give attribution to the individuals who work on them).
Reviews:
https://reviewboard.asterisk.org/r/3814
https://reviewboard.asterisk.org/r/3808
https://reviewboard.asterisk.org/r/3805
https://reviewboard.asterisk.org/r/3803
https://reviewboard.asterisk.org/r/3801
https://reviewboard.asterisk.org/r/3798
https://reviewboard.asterisk.org/r/3800
https://reviewboard.asterisk.org/r/3794
https://reviewboard.asterisk.org/r/3793
https://reviewboard.asterisk.org/r/3792
https://reviewboard.asterisk.org/r/3791
https://reviewboard.asterisk.org/r/3790
https://reviewboard.asterisk.org/r/3789
https://reviewboard.asterisk.org/r/3788
https://reviewboard.asterisk.org/r/3787
https://reviewboard.asterisk.org/r/3786
https://reviewboard.asterisk.org/r/3784
https://reviewboard.asterisk.org/r/3783
https://reviewboard.asterisk.org/r/3778
https://reviewboard.asterisk.org/r/3774
https://reviewboard.asterisk.org/r/3775
https://reviewboard.asterisk.org/r/3772
https://reviewboard.asterisk.org/r/3761
https://reviewboard.asterisk.org/r/3754
https://reviewboard.asterisk.org/r/3753
https://reviewboard.asterisk.org/r/3751
https://reviewboard.asterisk.org/r/3750
https://reviewboard.asterisk.org/r/3748
https://reviewboard.asterisk.org/r/3747
https://reviewboard.asterisk.org/r/3746
https://reviewboard.asterisk.org/r/3742
https://reviewboard.asterisk.org/r/3740
https://reviewboard.asterisk.org/r/3739
https://reviewboard.asterisk.org/r/3738
https://reviewboard.asterisk.org/r/3737
https://reviewboard.asterisk.org/r/3736
https://reviewboard.asterisk.org/r/3734
https://reviewboard.asterisk.org/r/3722
https://reviewboard.asterisk.org/r/3713
https://reviewboard.asterisk.org/r/3703
https://reviewboard.asterisk.org/r/3689
https://reviewboard.asterisk.org/r/3687
https://reviewboard.asterisk.org/r/3674
https://reviewboard.asterisk.org/r/3671
https://reviewboard.asterisk.org/r/3667
https://reviewboard.asterisk.org/r/3665
https://reviewboard.asterisk.org/r/3625
https://reviewboard.asterisk.org/r/3602
https://reviewboard.asterisk.org/r/3519
https://reviewboard.asterisk.org/r/3518
https://reviewboard.asterisk.org/r/3516
https://reviewboard.asterisk.org/r/3515
https://reviewboard.asterisk.org/r/3512
https://reviewboard.asterisk.org/r/3506
https://reviewboard.asterisk.org/r/3413
https://reviewboard.asterisk.org/r/3410
https://reviewboard.asterisk.org/r/3387
https://reviewboard.asterisk.org/r/3388
https://reviewboard.asterisk.org/r/3389
https://reviewboard.asterisk.org/r/3390
https://reviewboard.asterisk.org/r/3321
https://reviewboard.asterisk.org/r/3320
https://reviewboard.asterisk.org/r/3319
https://reviewboard.asterisk.org/r/3318
https://reviewboard.asterisk.org/r/3266
https://reviewboard.asterisk.org/r/3265
https://reviewboard.asterisk.org/r/3234
https://reviewboard.asterisk.org/r/3178
ASTERISK-23114 #close
Reported by: mjordan
media_formats_translation_core.diff uploaded by kharwell (License 6464)
rb3506.diff uploaded by mjordan (License 6283)
media_format_app_file.diff uploaded by kharwell (License 6464)
misc-2.diff uploaded by file (License 5000)
chan_mild-3.diff uploaded by file (License 5000)
chan_obscure.diff uploaded by file (License 5000)
jingle.diff uploaded by file (License 5000)
funcs.diff uploaded by file (License 5000)
formats.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
bridges.diff uploaded by file (License 5000)
mf-codecs-2.diff uploaded by file (License 5000)
mf-app_fax.diff uploaded by file (License 5000)
mf-apps-3.diff uploaded by file (License 5000)
media-formats-3.diff uploaded by file (License 5000)
ASTERISK-23715
rb3713.patch uploaded by coreyfarrell (License 5909)
rb3689.patch uploaded by mjordan (License 6283)
ASTERISK-23957
rb3722.patch uploaded by mjordan (License 6283)
mf-attributes-3.diff uploaded by file (License 5000)
ASTERISK-23958
Tested by: jrose
rb3822.patch uploaded by coreyfarrell (License 5909)
rb3800.patch uploaded by jrose (License 6182)
chan_sip.diff uploaded by mjordan (License 6283)
rb3747.patch uploaded by jrose (License 6182)
ASTERISK-23959 #close
Tested by: sgriepentrog, mjordan, coreyfarrell
sip_cleanup.diff uploaded by opticron (License 6273)
chan_sip_caps.diff uploaded by mjordan (License 6283)
rb3751.patch uploaded by coreyfarrell (License 5909)
chan_sip-3.diff uploaded by file (License 5000)
ASTERISK-23960 #close
Tested by: opticron
direct_media.diff uploaded by opticron (License 6273)
pjsip-direct-media.diff uploaded by file (License 5000)
format_cap_remove.diff uploaded by opticron (License 6273)
media_format_fixes.diff uploaded by opticron (License 6273)
chan_pjsip-2.diff uploaded by file (License 5000)
ASTERISK-23966 #close
Tested by: rmudgett
rb3803.patch uploaded by rmudgetti (License 5621)
chan_dahdi.diff uploaded by file (License 5000)
ASTERISK-24064 #close
Tested by: coreyfarrell, mjordan, opticron, file, rmudgett, sgriepentrog, jrose
rb3814.patch uploaded by rmudgett (License 5621)
moh_cleanup.diff uploaded by opticron (License 6273)
bridge_leak.diff uploaded by opticron (License 6273)
translate.diff uploaded by file (License 5000)
rb3795.patch uploaded by rmudgett (License 5621)
tls_fix.diff uploaded by mjordan (License 6283)
fax-mf-fix-2.diff uploaded by file (License 5000)
rtp_transfer_stuff uploaded by mjordan (License 6283)
rb3787.patch uploaded by rmudgett (License 5621)
media-formats-explicit-translate-format-3.diff uploaded by file (License 5000)
format_cache_case_fix.diff uploaded by opticron (License 6273)
rb3774.patch uploaded by rmudgett (License 5621)
rb3775.patch uploaded by rmudgett (License 5621)
rtp_engine_fix.diff uploaded by opticron (License 6273)
rtp_crash_fix.diff uploaded by opticron (License 6273)
rb3753.patch uploaded by mjordan (License 6283)
rb3750.patch uploaded by mjordan (License 6283)
rb3748.patch uploaded by rmudgett (License 5621)
media_format_fixes.diff uploaded by opticron (License 6273)
rb3740.patch uploaded by mjordan (License 6283)
rb3739.patch uploaded by mjordan (License 6283)
rb3734.patch uploaded by mjordan (License 6283)
rb3689.patch uploaded by mjordan (License 6283)
rb3674.patch uploaded by coreyfarrell (License 5909)
rb3671.patch uploaded by coreyfarrell (License 5909)
rb3667.patch uploaded by coreyfarrell (License 5909)
rb3665.patch uploaded by mjordan (License 6283)
rb3625.patch uploaded by coreyfarrell (License 5909)
rb3602.patch uploaded by coreyfarrell (License 5909)
format_compatibility-2.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419044 65c4cc65-6c06-0410-ace0-fbb531ad65f3
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) |