diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/abstract_jb.c | 8 | ||||
-rw-r--r-- | main/app.c | 24 | ||||
-rw-r--r-- | main/asterisk.c | 9 | ||||
-rw-r--r-- | main/astobj2.c | 37 | ||||
-rw-r--r-- | main/audiohook.c | 32 | ||||
-rw-r--r-- | main/bridging.c | 76 | ||||
-rw-r--r-- | main/callerid.c | 20 | ||||
-rw-r--r-- | main/ccss.c | 13 | ||||
-rw-r--r-- | main/channel.c | 439 | ||||
-rw-r--r-- | main/cli.c | 8 | ||||
-rw-r--r-- | main/data.c | 33 | ||||
-rw-r--r-- | main/dial.c | 16 | ||||
-rw-r--r-- | main/dsp.c | 14 | ||||
-rw-r--r-- | main/features.c | 40 | ||||
-rw-r--r-- | main/file.c | 214 | ||||
-rw-r--r-- | main/format.c | 558 | ||||
-rw-r--r-- | main/format_cap.c | 545 | ||||
-rw-r--r-- | main/format_pref.c | 320 | ||||
-rw-r--r-- | main/frame.c | 424 | ||||
-rw-r--r-- | main/image.c | 9 | ||||
-rw-r--r-- | main/indications.c | 10 | ||||
-rw-r--r-- | main/manager.c | 50 | ||||
-rw-r--r-- | main/pbx.c | 12 | ||||
-rw-r--r-- | main/rtp_engine.c | 435 | ||||
-rw-r--r-- | main/slinfactory.c | 18 | ||||
-rw-r--r-- | main/translate.c | 1168 | ||||
-rw-r--r-- | main/udptl.c | 8 |
27 files changed, 3206 insertions, 1334 deletions
diff --git a/main/abstract_jb.c b/main/abstract_jb.c index 4cd5ccb0d..5ff1c4b2a 100644 --- a/main/abstract_jb.c +++ b/main/abstract_jb.c @@ -398,7 +398,7 @@ static void jb_get_and_deliver(struct ast_channel *chan) } while (now >= jb->next) { - interpolation_len = ast_codec_interp_len(jb->last_format); + interpolation_len = ast_codec_interp_len(&jb->last_format); res = jbimpl->get(jbobj, &f, now, interpolation_len); @@ -409,13 +409,13 @@ static void jb_get_and_deliver(struct ast_channel *chan) case JB_IMPL_DROP: jb_framelog("\tJB_GET {now=%ld}: %s frame with ts=%ld and len=%ld\n", now, jb_get_actions[res], f->ts, f->len); - jb->last_format = f->subclass.codec; + ast_format_copy(&jb->last_format, &f->subclass.format); ast_frfree(f); break; case JB_IMPL_INTERP: /* interpolate a frame */ f = &finterp; - f->subclass.codec = jb->last_format; + ast_format_copy(&f->subclass.format, &jb->last_format); f->samples = interpolation_len * 8; f->src = "JB interpolation"; f->delivery = ast_tvadd(jb->timebase, ast_samp2tv(jb->next, 1000)); @@ -476,7 +476,7 @@ static int create_jb(struct ast_channel *chan, struct ast_frame *frr) jb->next = jbimpl->next(jbobj); /* Init last format for a first time. */ - jb->last_format = frr->subclass.codec; + ast_format_copy(&jb->last_format, &frr->subclass.format); /* Create a frame log file */ if (ast_test_flag(jbconf, AST_JB_LOG)) { diff --git a/main/app.c b/main/app.c index 0054c4cde..27858a29b 100644 --- a/main/app.c +++ b/main/app.c @@ -445,15 +445,15 @@ struct linear_state { int fd; int autoclose; int allowoverride; - int origwfmt; + struct ast_format origwfmt; }; static void linear_release(struct ast_channel *chan, void *params) { struct linear_state *ls = params; - if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt)) { - ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, ls->origwfmt); + if (ls->origwfmt.id && ast_set_write_format(chan, &ls->origwfmt)) { + ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, ls->origwfmt.id); } if (ls->autoclose) { @@ -469,12 +469,13 @@ static int linear_generator(struct ast_channel *chan, void *data, int len, int s struct linear_state *ls = data; struct ast_frame f = { .frametype = AST_FRAME_VOICE, - .subclass.codec = AST_FORMAT_SLINEAR, .data.ptr = buf + AST_FRIENDLY_OFFSET / 2, .offset = AST_FRIENDLY_OFFSET, }; int res; + ast_format_set(&f.subclass.format, AST_FORMAT_SLINEAR, 0); + len = samples * 2; if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) { ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" , len); @@ -507,9 +508,9 @@ static void *linear_alloc(struct ast_channel *chan, void *params) ast_clear_flag(chan, AST_FLAG_WRITE_INT); } - ls->origwfmt = chan->writeformat; + ast_format_copy(&ls->origwfmt, &chan->writeformat); - if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) { + if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR)) { ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name); ast_free(ls); ls = params = NULL; @@ -741,10 +742,11 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, int totalsilence = 0; int dspsilence = 0; int olddspsilence = 0; - int rfmt = 0; + struct ast_format rfmt; struct ast_silence_generator *silgen = NULL; char prependfile[80]; + ast_format_clear(&rfmt); if (silencethreshold < 0) { silencethreshold = global_silence_threshold; } @@ -815,8 +817,8 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, return -1; } ast_dsp_set_threshold(sildet, silencethreshold); - rfmt = chan->readformat; - res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); + ast_format_copy(&rfmt, &chan->readformat); + res = ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR); if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); ast_dsp_free(sildet); @@ -1005,8 +1007,8 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, ast_filedelete(prependfile, sfmt[x]); } } - if (rfmt && ast_set_read_format(chan, rfmt)) { - ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name); + if (rfmt.id && ast_set_read_format(chan, &rfmt)) { + ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(&rfmt), chan->name); } if (outmsg == 2) { ast_stream_and_wait(chan, "auth-thankyou", ""); diff --git a/main/asterisk.c b/main/asterisk.c index 8d81b5eed..7f8b6a285 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -111,6 +111,7 @@ int daemon(int, int); /* defined in libresolv of all places */ #include "asterisk/network.h" #include "asterisk/cli.h" #include "asterisk/channel.h" +#include "asterisk/translate.h" #include "asterisk/features.h" #include "asterisk/ulaw.h" #include "asterisk/alaw.h" @@ -142,6 +143,7 @@ int daemon(int, int); /* defined in libresolv of all places */ #include "asterisk/poll-compat.h" #include "asterisk/ccss.h" #include "asterisk/test.h" +#include "asterisk/format.h" #include "asterisk/aoc.h" #include "../defaults.h" @@ -3669,6 +3671,11 @@ int main(int argc, char *argv[]) } #endif + if (ast_translate_init()) { + printf("%s", term_quit()); + exit(1); + } + ast_aoc_cli_init(); ast_makesocket(); @@ -3700,6 +3707,8 @@ int main(int argc, char *argv[]) astobj2_init(); + ast_format_attr_init(); + ast_autoservice_init(); if (ast_timing_init()) { diff --git a/main/astobj2.c b/main/astobj2.c index ccfbf5388..6391844fe 100644 --- a/main/astobj2.c +++ b/main/astobj2.c @@ -137,7 +137,7 @@ enum ao2_callback_type { static int internal_ao2_ref(void *user_data, const int delta); static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *c, const uint n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn); -static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *user_data, const char *file, int line, const char *func); +static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *user_data, int flags, const char *file, int line, const char *func); static void *internal_ao2_callback(struct ao2_container *c, const enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type, char *tag, char *file, int line, const char *funcname); @@ -471,7 +471,7 @@ struct bucket_entry { * link an object to a container */ -static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *user_data, const char *file, int line, const char *func) +static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *user_data, int flags, const char *file, int line, const char *func) { int i; /* create a new list entry */ @@ -490,7 +490,9 @@ static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *use i = abs(c->hash_fn(user_data, OBJ_POINTER)); - ao2_lock(c); + if (!(flags & OBJ_NOLOCK)) { + ao2_lock(c); + } i %= c->n_buckets; p->astobj = obj; p->version = ast_atomic_fetchadd_int(&c->version, 1); @@ -501,24 +503,28 @@ static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *use return p; } -void *__ao2_link_debug(struct ao2_container *c, void *user_data, char *tag, char *file, int line, const char *funcname) +void *__ao2_link_debug(struct ao2_container *c, void *user_data, int flags, char *tag, char *file, int line, const char *funcname) { - struct bucket_entry *p = internal_ao2_link(c, user_data, file, line, funcname); + struct bucket_entry *p = internal_ao2_link(c, user_data, flags, file, line, funcname); if (p) { __ao2_ref_debug(user_data, +1, tag, file, line, funcname); - ao2_unlock(c); + if (!(flags & OBJ_NOLOCK)) { + ao2_unlock(c); + } } return p; } -void *__ao2_link(struct ao2_container *c, void *user_data) +void *__ao2_link(struct ao2_container *c, void *user_data, int flags) { - struct bucket_entry *p = internal_ao2_link(c, user_data, __FILE__, __LINE__, __PRETTY_FUNCTION__); + struct bucket_entry *p = internal_ao2_link(c, user_data, flags, __FILE__, __LINE__, __PRETTY_FUNCTION__); if (p) { __ao2_ref(user_data, +1); - ao2_unlock(c); + if (!(flags & OBJ_NOLOCK)) { + ao2_unlock(c); + } } return p; } @@ -535,23 +541,26 @@ int ao2_match_by_addr(void *user_data, void *arg, int flags) * Unlink an object from the container * and destroy the associated * bucket_entry structure. */ -void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, char *tag, +void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags, char *tag, char *file, int line, const char *funcname) { if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */ return NULL; - __ao2_callback_debug(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data, tag, file, line, funcname); + flags |= (OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA); + + __ao2_callback_debug(c, flags, ao2_match_by_addr, user_data, tag, file, line, funcname); return NULL; } -void *__ao2_unlink(struct ao2_container *c, void *user_data) +void *__ao2_unlink(struct ao2_container *c, void *user_data, int flags) { if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */ return NULL; - __ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data); + flags |= (OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA); + __ao2_callback(c, flags, ao2_match_by_addr, user_data); return NULL; } @@ -696,7 +705,7 @@ static void *internal_ao2_callback(struct ao2_container *c, * link the object into the container that will hold the results. */ if (ret && (multi_container != NULL)) { - __ao2_link(multi_container, ret); + __ao2_link(multi_container, ret, flags); ret = NULL; } diff --git a/main/audiohook.c b/main/audiohook.c index ad977ecbc..23650a79f 100644 --- a/main/audiohook.c +++ b/main/audiohook.c @@ -40,7 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") struct ast_audiohook_translate { struct ast_trans_pvt *trans_pvt; - format_t format; + struct ast_format format; }; struct ast_audiohook_list { @@ -185,11 +185,11 @@ static struct ast_frame *audiohook_read_frame_single(struct ast_audiohook *audio short buf[samples]; struct ast_frame frame = { .frametype = AST_FRAME_VOICE, - .subclass.codec = AST_FORMAT_SLINEAR, .data.ptr = buf, .datalen = sizeof(buf), .samples = samples, }; + ast_format_set(&frame.subclass.format, AST_FORMAT_SLINEAR, 0); /* Ensure the factory is able to give us the samples we want */ if (samples > ast_slinfactory_available(factory)) @@ -212,11 +212,11 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho short buf1[samples], buf2[samples], *read_buf = NULL, *write_buf = NULL, *final_buf = NULL, *data1 = NULL, *data2 = NULL; struct ast_frame frame = { .frametype = AST_FRAME_VOICE, - .subclass.codec = AST_FORMAT_SLINEAR, .data.ptr = NULL, .datalen = sizeof(buf1), .samples = samples, }; + ast_format_set(&frame.subclass.format, AST_FORMAT_SLINEAR, 0); /* Make sure both factories have the required samples */ usable_read = (ast_slinfactory_available(&audiohook->read_factory) >= samples ? 1 : 0); @@ -304,23 +304,24 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho * \param format Format of frame remote side wants back * \return Returns frame on success, NULL on failure */ -struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, format_t format) +struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, struct ast_format *format) { struct ast_frame *read_frame = NULL, *final_frame = NULL; + struct ast_format tmp_fmt; if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ? audiohook_read_frame_both(audiohook, samples) : audiohook_read_frame_single(audiohook, samples, direction)))) return NULL; /* If they don't want signed linear back out, we'll have to send it through the translation path */ - if (format != AST_FORMAT_SLINEAR) { + if (format->id != AST_FORMAT_SLINEAR) { /* Rebuild translation path if different format then previously */ - if (audiohook->format != format) { + if (ast_format_cmp(format, &audiohook->format) == AST_FORMAT_CMP_NOT_EQUAL) { if (audiohook->trans_pvt) { ast_translator_free_path(audiohook->trans_pvt); audiohook->trans_pvt = NULL; } /* Setup new translation path for this format... if we fail we can't very well return signed linear so free the frame and return nothing */ - if (!(audiohook->trans_pvt = ast_translator_build_path(format, AST_FORMAT_SLINEAR))) { + if (!(audiohook->trans_pvt = ast_translator_build_path(format, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0)))) { ast_frfree(read_frame); return NULL; } @@ -619,17 +620,18 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st struct ast_audiohook_translate *out_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->out_translate[0] : &audiohook_list->out_translate[1]); struct ast_frame *start_frame = frame, *middle_frame = frame, *end_frame = frame; struct ast_audiohook *audiohook = NULL; + struct ast_format tmp_fmt; int samples = frame->samples; /* ---Part_1. translate start_frame to SLINEAR if necessary. */ /* If the frame coming in is not signed linear we have to send it through the in_translate path */ - if (frame->subclass.codec != AST_FORMAT_SLINEAR) { - if (in_translate->format != frame->subclass.codec) { + if (frame->subclass.format.id != AST_FORMAT_SLINEAR) { + if (ast_format_cmp(&frame->subclass.format, &in_translate->format) == AST_FORMAT_CMP_NOT_EQUAL) { if (in_translate->trans_pvt) ast_translator_free_path(in_translate->trans_pvt); - if (!(in_translate->trans_pvt = ast_translator_build_path(AST_FORMAT_SLINEAR, frame->subclass.codec))) + if (!(in_translate->trans_pvt = ast_translator_build_path(ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0), &frame->subclass.format))) return frame; - in_translate->format = frame->subclass.codec; + ast_format_copy(&in_translate->format, &frame->subclass.format); } if (!(middle_frame = ast_translate(in_translate->trans_pvt, frame, 0))) return frame; @@ -707,16 +709,16 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st /* ---Part_3: Decide what to do with the end_frame (whether to transcode or not) */ if (middle_frame == end_frame) { /* Middle frame was modified and became the end frame... let's see if we need to transcode */ - if (end_frame->subclass.codec != start_frame->subclass.codec) { - if (out_translate->format != start_frame->subclass.codec) { + if (ast_format_cmp(&end_frame->subclass.format, &start_frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) { + if (ast_format_cmp(&out_translate->format, &start_frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) { if (out_translate->trans_pvt) ast_translator_free_path(out_translate->trans_pvt); - if (!(out_translate->trans_pvt = ast_translator_build_path(start_frame->subclass.codec, AST_FORMAT_SLINEAR))) { + if (!(out_translate->trans_pvt = ast_translator_build_path(&start_frame->subclass.format, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0)))) { /* We can't transcode this... drop our middle frame and return the original */ ast_frfree(middle_frame); return start_frame; } - out_translate->format = start_frame->subclass.codec; + ast_format_copy(&out_translate->format, &start_frame->subclass.format); } /* Transcode from our middle (signed linear) frame to new format of the frame that came in */ if (!(end_frame = ast_translate(out_translate->trans_pvt, middle_frame, 0))) { diff --git a/main/bridging.c b/main/bridging.c index a256cf038..9de02aa34 100644 --- a/main/bridging.c +++ b/main/bridging.c @@ -382,16 +382,12 @@ static void *bridge_thread(void *data) } /*! \brief Helper function used to find the "best" bridge technology given a specified capabilities */ -static struct ast_bridge_technology *find_best_technology(format_t capabilities) +static struct ast_bridge_technology *find_best_technology(uint32_t capabilities) { struct ast_bridge_technology *current = NULL, *best = NULL; AST_RWLIST_RDLOCK(&bridge_technologies); AST_RWLIST_TRAVERSE(&bridge_technologies, current, entry) { - char tmp1[256], tmp2[256]; - ast_debug(1, "Bridge technology %s has capabilities %s and we want %s\n", current->name, - ast_getformatname_multiple(tmp1, sizeof(tmp1), current->capabilities), - ast_getformatname_multiple(tmp2, sizeof(tmp2), capabilities)); if (current->suspended) { ast_debug(1, "Bridge technology %s is suspended. Skipping.\n", current->name); continue; @@ -448,7 +444,7 @@ static void destroy_bridge(void *obj) return; } -struct ast_bridge *ast_bridge_new(format_t capabilities, int flags) +struct ast_bridge *ast_bridge_new(uint32_t capabilities, int flags) { struct ast_bridge *bridge = NULL; struct ast_bridge_technology *bridge_technology = NULL; @@ -470,9 +466,6 @@ struct ast_bridge *ast_bridge_new(format_t capabilities, int flags) /* If no bridge technology was found we can't possibly do bridging so fail creation of the bridge */ if (!bridge_technology) { - char codec_buf[256]; - ast_debug(1, "Failed to find a bridge technology to satisfy capabilities %s\n", - ast_getformatname_multiple(codec_buf, sizeof(codec_buf), capabilities)); return NULL; } @@ -503,7 +496,7 @@ struct ast_bridge *ast_bridge_new(format_t capabilities, int flags) return bridge; } -int ast_bridge_check(format_t capabilities) +int ast_bridge_check(uint32_t capabilities) { struct ast_bridge_technology *bridge_technology = NULL; @@ -542,47 +535,51 @@ int ast_bridge_destroy(struct ast_bridge *bridge) static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) { - format_t formats[2] = {bridge_channel->chan->readformat, bridge_channel->chan->writeformat}; + struct ast_format formats[2]; + ast_format_copy(&formats[0], &bridge_channel->chan->readformat); + ast_format_copy(&formats[1], &bridge_channel->chan->writeformat); /* Are the formats currently in use something ths bridge can handle? */ - if (!(bridge->technology->formats & bridge_channel->chan->readformat)) { - format_t best_format = ast_best_codec(bridge->technology->formats); + if (!ast_format_cap_iscompatible(bridge->technology->format_capabilities, &bridge_channel->chan->readformat)) { + struct ast_format best_format; + ast_best_codec(bridge->technology->format_capabilities, &best_format); /* Read format is a no go... */ if (option_debug) { char codec_buf[512]; ast_debug(1, "Bridge technology %s wants to read any of formats %s but channel has %s\n", bridge->technology->name, - ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->formats), - ast_getformatname(formats[0])); + ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->format_capabilities), + ast_getformatname(&formats[0])); } /* Switch read format to the best one chosen */ - if (ast_set_read_format(bridge_channel->chan, best_format)) { - ast_log(LOG_WARNING, "Failed to set channel %s to read format %s\n", bridge_channel->chan->name, ast_getformatname(best_format)); + if (ast_set_read_format(bridge_channel->chan, &best_format)) { + ast_log(LOG_WARNING, "Failed to set channel %s to read format %s\n", bridge_channel->chan->name, ast_getformatname(&best_format)); return -1; } - ast_debug(1, "Bridge %p put channel %s into read format %s\n", bridge, bridge_channel->chan->name, ast_getformatname(best_format)); + ast_debug(1, "Bridge %p put channel %s into read format %s\n", bridge, bridge_channel->chan->name, ast_getformatname(&best_format)); } else { - ast_debug(1, "Bridge %p is happy that channel %s already has read format %s\n", bridge, bridge_channel->chan->name, ast_getformatname(formats[0])); + ast_debug(1, "Bridge %p is happy that channel %s already has read format %s\n", bridge, bridge_channel->chan->name, ast_getformatname(&formats[0])); } - if (!(bridge->technology->formats & formats[1])) { - int best_format = ast_best_codec(bridge->technology->formats); + if (!ast_format_cap_iscompatible(bridge->technology->format_capabilities, &formats[1])) { + struct ast_format best_format; + ast_best_codec(bridge->technology->format_capabilities, &best_format); /* Write format is a no go... */ if (option_debug) { char codec_buf[512]; ast_debug(1, "Bridge technology %s wants to write any of formats %s but channel has %s\n", bridge->technology->name, - ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->formats), - ast_getformatname(formats[1])); + ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->format_capabilities), + ast_getformatname(&formats[1])); } /* Switch write format to the best one chosen */ - if (ast_set_write_format(bridge_channel->chan, best_format)) { - ast_log(LOG_WARNING, "Failed to set channel %s to write format %s\n", bridge_channel->chan->name, ast_getformatname(best_format)); + if (ast_set_write_format(bridge_channel->chan, &best_format)) { + ast_log(LOG_WARNING, "Failed to set channel %s to write format %s\n", bridge_channel->chan->name, ast_getformatname(&best_format)); return -1; } - ast_debug(1, "Bridge %p put channel %s into write format %s\n", bridge, bridge_channel->chan->name, ast_getformatname(best_format)); + ast_debug(1, "Bridge %p put channel %s into write format %s\n", bridge, bridge_channel->chan->name, ast_getformatname(&best_format)); } else { - ast_debug(1, "Bridge %p is happy that channel %s already has write format %s\n", bridge, bridge_channel->chan->name, ast_getformatname(formats[1])); + ast_debug(1, "Bridge %p is happy that channel %s already has write format %s\n", bridge, bridge_channel->chan->name, ast_getformatname(&formats[1])); } return 0; @@ -591,7 +588,7 @@ static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_c /*! \brief Perform the smart bridge operation. Basically sees if a new bridge technology should be used instead of the current one. */ static int smart_bridge_operation(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, int count) { - format_t new_capabilities = 0; + uint32_t new_capabilities = 0; struct ast_bridge_technology *new_technology = NULL, *old_technology = bridge->technology; struct ast_bridge temp_bridge = { .technology = bridge->technology, @@ -621,9 +618,6 @@ static int smart_bridge_operation(struct ast_bridge *bridge, struct ast_bridge_c /* Attempt to find a new bridge technology to satisfy the capabilities */ if (!(new_technology = find_best_technology(new_capabilities))) { - char codec_buf[256]; - ast_debug(1, "Smart bridge operation was unable to find new bridge technology with capabilities %s to satisfy bridge %p\n", - ast_getformatname_multiple(codec_buf, sizeof(codec_buf), new_capabilities), bridge); return -1; } @@ -873,8 +867,10 @@ static void bridge_channel_dtmf_stream(struct ast_bridge *bridge, struct ast_bri /*! \brief Join a channel to a bridge and handle anything the bridge may want us to do */ static enum ast_bridge_channel_state bridge_channel_join(struct ast_bridge_channel *bridge_channel) { - int formats[2] = { bridge_channel->chan->readformat, bridge_channel->chan->writeformat }; + struct ast_format formats[2]; enum ast_bridge_channel_state state; + ast_format_copy(&formats[0], &bridge_channel->chan->readformat); + ast_format_copy(&formats[1], &bridge_channel->chan->writeformat); /* Record the thread that will be the owner of us */ bridge_channel->thread = pthread_self(); @@ -975,16 +971,16 @@ static enum ast_bridge_channel_state bridge_channel_join(struct ast_bridge_chann ao2_unlock(bridge_channel->bridge); /* Restore original formats of the channel as they came in */ - if (bridge_channel->chan->readformat != formats[0]) { - ast_debug(1, "Bridge is returning %p to read format %s(%d)\n", bridge_channel, ast_getformatname(formats[0]), formats[0]); - if (ast_set_read_format(bridge_channel->chan, formats[0])) { - ast_debug(1, "Bridge failed to return channel %p to read format %s(%d)\n", bridge_channel, ast_getformatname(formats[0]), formats[0]); + if (ast_format_cmp(&bridge_channel->chan->readformat, &formats[0]) == AST_FORMAT_CMP_NOT_EQUAL) { + ast_debug(1, "Bridge is returning %p to read format %s(%d)\n", bridge_channel, ast_getformatname(&formats[0]), formats[0].id); + if (ast_set_read_format(bridge_channel->chan, &formats[0])) { + ast_debug(1, "Bridge failed to return channel %p to read format %s(%d)\n", bridge_channel, ast_getformatname(&formats[0]), formats[0].id); } } - if (bridge_channel->chan->writeformat != formats[1]) { - ast_debug(1, "Bridge is returning %p to write format %s(%d)\n", bridge_channel, ast_getformatname(formats[1]), formats[1]); - if (ast_set_write_format(bridge_channel->chan, formats[1])) { - ast_debug(1, "Bridge failed to return channel %p to write format %s(%d)\n", bridge_channel, ast_getformatname(formats[1]), formats[1]); + if (ast_format_cmp(&bridge_channel->chan->writeformat, &formats[1]) == AST_FORMAT_CMP_NOT_EQUAL) { + ast_debug(1, "Bridge is returning %p to write format %s(%d)\n", bridge_channel, ast_getformatname(&formats[1]), formats[1].id); + if (ast_set_write_format(bridge_channel->chan, &formats[1])) { + ast_debug(1, "Bridge failed to return channel %p to write format %s(%d)\n", bridge_channel, ast_getformatname(&formats[1]), formats[1].id); } } diff --git a/main/callerid.c b/main/callerid.c index ac8fbde8b..aa53eb83e 100644 --- a/main/callerid.c +++ b/main/callerid.c @@ -71,7 +71,7 @@ float casdr1, casdi1, casdr2, casdi2; #define AST_CALLERID_UNKNOWN "<unknown>" -static inline void gen_tones(unsigned char *buf, int len, format_t codec, float ddr1, float ddi1, float ddr2, float ddi2, float *cr1, float *ci1, float *cr2, float *ci2) +static inline void gen_tones(unsigned char *buf, int len, struct ast_format *codec, float ddr1, float ddi1, float ddr2, float ddi2, float *cr1, float *ci1, float *cr2, float *ci2) { int x; float t; @@ -93,7 +93,7 @@ static inline void gen_tones(unsigned char *buf, int len, format_t codec, float } } -static inline void gen_tone(unsigned char *buf, int len, format_t codec, float ddr1, float ddi1, float *cr1, float *ci1) +static inline void gen_tone(unsigned char *buf, int len, struct ast_format *codec, float ddr1, float ddi1, float *cr1, float *ci1) { int x; float t; @@ -255,7 +255,7 @@ void callerid_get_dtmf(char *cidstring, char *number, int *flags) } } -int ast_gen_cas(unsigned char *outbuf, int sendsas, int len, format_t codec) +int ast_gen_cas(unsigned char *outbuf, int sendsas, int len, struct ast_format *codec) { int pos = 0; int saslen = 2400; @@ -300,7 +300,7 @@ static unsigned short calc_crc(unsigned short crc, unsigned char data) return crc; } -int callerid_feed_jp(struct callerid_state *cid, unsigned char *ubuf, int len, format_t codec) +int callerid_feed_jp(struct callerid_state *cid, unsigned char *ubuf, int len, struct ast_format *codec) { int mylen = len; int olen; @@ -539,7 +539,7 @@ int callerid_feed_jp(struct callerid_state *cid, unsigned char *ubuf, int len, f } -int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int len, format_t codec) +int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int len, struct ast_format *codec) { int mylen = len; int olen; @@ -791,7 +791,7 @@ static int callerid_genmsg(char *msg, int size, const char *number, const char * } -int ast_callerid_vmwi_generate(unsigned char *buf, int active, int type, format_t codec, +int ast_callerid_vmwi_generate(unsigned char *buf, int active, int type, struct ast_format *codec, const char* name, const char* number, int flags) { char msg[256]; @@ -879,7 +879,7 @@ int ast_callerid_vmwi_generate(unsigned char *buf, int active, int type, format_ return bytes; } -int callerid_generate(unsigned char *buf, const char *number, const char *name, int flags, int callwaiting, format_t codec) +int callerid_generate(unsigned char *buf, const char *number, const char *name, int flags, int callwaiting, struct ast_format *codec) { int bytes = 0; int x, sum; @@ -1038,7 +1038,7 @@ int ast_callerid_parse(char *instr, char **name, char **location) return 0; } -static int __ast_callerid_generate(unsigned char *buf, const char *name, const char *number, int callwaiting, format_t codec) +static int __ast_callerid_generate(unsigned char *buf, const char *name, const char *number, int callwaiting, struct ast_format *codec) { if (ast_strlen_zero(name)) name = NULL; @@ -1047,12 +1047,12 @@ static int __ast_callerid_generate(unsigned char *buf, const char *name, const c return callerid_generate(buf, number, name, 0, callwaiting, codec); } -int ast_callerid_generate(unsigned char *buf, const char *name, const char *number, format_t codec) +int ast_callerid_generate(unsigned char *buf, const char *name, const char *number, struct ast_format *codec) { return __ast_callerid_generate(buf, name, number, 0, codec); } -int ast_callerid_callwaiting_generate(unsigned char *buf, const char *name, const char *number, format_t codec) +int ast_callerid_callwaiting_generate(unsigned char *buf, const char *name, const char *number, struct ast_format *codec) { return __ast_callerid_generate(buf, name, number, 1, codec); } diff --git a/main/ccss.c b/main/ccss.c index 3583e8472..fc79a01dd 100644 --- a/main/ccss.c +++ b/main/ccss.c @@ -2482,19 +2482,30 @@ static void *generic_recall(void *data) struct ast_channel *chan; const char *callback_macro = ast_get_cc_callback_macro(agent->cc_params); unsigned int recall_timer = ast_get_cc_recall_timer(agent->cc_params) * 1000; + struct ast_format tmp_fmt; + struct ast_format_cap *tmp_cap = ast_format_cap_alloc_nolock(); + + if (!tmp_cap) { + return NULL; + } tech = interface; if ((target = strchr(interface, '/'))) { *target++ = '\0'; } - if (!(chan = ast_request_and_dial(tech, AST_FORMAT_SLINEAR, NULL, target, recall_timer, &reason, generic_pvt->cid_num, generic_pvt->cid_name))) { + + ast_format_cap_add(tmp_cap, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0)); + if (!(chan = ast_request_and_dial(tech, tmp_cap, NULL, target, recall_timer, &reason, generic_pvt->cid_num, generic_pvt->cid_name))) { /* Hmm, no channel. Sucks for you, bud. */ ast_log_dynamic_level(cc_logger_level, "Core %d: Failed to call back %s for reason %d\n", agent->core_id, agent->device_name, reason); ast_cc_failed(agent->core_id, "Failed to call back device %s/%s", tech, target); + ast_format_cap_destroy(tmp_cap); return NULL; } + ast_format_cap_destroy(tmp_cap); + if (!ast_strlen_zero(callback_macro)) { ast_log_dynamic_level(cc_logger_level, "Core %d: There's a callback macro configured for agent %s\n", agent->core_id, agent->device_name); diff --git a/main/channel.c b/main/channel.c index 0cabf2d7e..796dc8b01 100644 --- a/main/channel.c +++ b/main/channel.c @@ -372,12 +372,12 @@ int ast_channel_data_add_structure(struct ast_data *tree, } } - ast_data_add_codecs(tree, "oldwriteformat", chan->oldwriteformat); + ast_data_add_codec(tree, "oldwriteformat", &chan->oldwriteformat); + ast_data_add_codec(tree, "readformat", &chan->readformat); + ast_data_add_codec(tree, "writeformat", &chan->writeformat); + ast_data_add_codec(tree, "rawreadformat", &chan->rawreadformat); + ast_data_add_codec(tree, "rawwriteformat", &chan->rawwriteformat); ast_data_add_codecs(tree, "nativeformats", chan->nativeformats); - ast_data_add_codecs(tree, "readformat", chan->readformat); - ast_data_add_codecs(tree, "writeformat", chan->writeformat); - ast_data_add_codecs(tree, "rawreadformat", chan->rawreadformat); - ast_data_add_codecs(tree, "rawwriteformat", chan->rawwriteformat); /* state */ enum_node = ast_data_add_node(tree, "state"); @@ -593,7 +593,7 @@ static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd, (cl->tech->devicestate) ? "yes" : "no", (cl->tech->indicate) ? "yes" : "no", (cl->tech->transfer) ? "yes" : "no", - ast_getformatname_multiple(buf, sizeof(buf), (cl->tech->capabilities) ? cl->tech->capabilities : -1), + ast_getformatname_multiple(buf, sizeof(buf), cl->tech->capabilities), (cl->tech->send_digit_begin) ? "yes" : "no", (cl->tech->send_digit_end) ? "yes" : "no", (cl->tech->send_html) ? "yes" : "no", @@ -989,12 +989,11 @@ char *ast_transfercapability2str(int transfercapability) } /*! \brief Pick the best audio codec */ -format_t ast_best_codec(format_t fmts) +struct ast_format *ast_best_codec(struct ast_format_cap *cap, struct ast_format *result) { /* This just our opinion, expressed in code. We are asked to choose the best codec to use, given no information */ - int x; - static const format_t prefs[] = + static const enum ast_format_id prefs[] = { /*! Okay, ulaw is used by all telephony equipment, so start with it */ AST_FORMAT_ULAW, @@ -1032,19 +1031,19 @@ format_t ast_best_codec(format_t fmts) AST_FORMAT_G723_1, }; char buf[512]; + int x; - /* Strip out video */ - fmts &= AST_FORMAT_AUDIO_MASK; - /* Find the first preferred codec in the format given */ for (x = 0; x < ARRAY_LEN(prefs); x++) { - if (fmts & prefs[x]) - return prefs[x]; + if (ast_format_cap_iscompatible(cap, ast_format_set(result, prefs[x], 0))) { + return result; + } } - ast_log(LOG_WARNING, "Don't know any of %s formats\n", ast_getformatname_multiple(buf, sizeof(buf), fmts)); + ast_format_clear(result); + ast_log(LOG_WARNING, "Don't know any of %s formats\n", ast_getformatname_multiple(buf, sizeof(buf), cap)); - return 0; + return NULL; } static const struct ast_channel_tech null_tech = { @@ -1087,6 +1086,11 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char /* Channel structure allocation failure. */ return NULL; } + if (!(tmp->nativeformats = ast_format_cap_alloc())) { + ao2_ref(tmp, -1); + /* format capabilities structure allocation failure */ + return NULL; + } /* * Init file descriptors to unopened state so @@ -2426,6 +2430,8 @@ static void ast_channel_destructor(void *obj) */ ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, device_name); } + + chan->nativeformats = ast_format_cap_destroy(chan->nativeformats); } /*! \brief Free a dummy channel structure */ @@ -2658,8 +2664,15 @@ static void free_translation(struct ast_channel *clonechan) ast_translator_free_path(clonechan->readtrans); clonechan->writetrans = NULL; clonechan->readtrans = NULL; - clonechan->rawwriteformat = clonechan->nativeformats; - clonechan->rawreadformat = clonechan->nativeformats; + if (ast_format_cap_is_empty(clonechan->nativeformats)) { + ast_format_clear(&clonechan->rawwriteformat); + ast_format_clear(&clonechan->rawreadformat); + } else { + struct ast_format tmpfmt; + ast_best_codec(clonechan->nativeformats, &tmpfmt); + ast_format_copy(&clonechan->rawwriteformat, &tmpfmt); + ast_format_copy(&clonechan->rawreadformat, &tmpfmt); + } } void ast_set_hangupsource(struct ast_channel *chan, const char *source, int force) @@ -2987,7 +3000,7 @@ static int generator_force(const void *data) if (!tmp || !generate) return 0; - res = generate(chan, tmp, 0, ast_format_rate(chan->writeformat & AST_FORMAT_AUDIO_MASK) / 50); + res = generate(chan, tmp, 0, ast_format_rate(&chan->writeformat) / 50); chan->generatordata = tmp; @@ -3548,9 +3561,9 @@ static void ast_read_generator_actions(struct ast_channel *chan, struct ast_fram chan->generatordata = NULL; /* reset, to let writes go through */ - if (f->subclass.codec != chan->writeformat) { + if (ast_format_cmp(&f->subclass.format, &chan->writeformat) == AST_FORMAT_CMP_NOT_EQUAL) { float factor; - factor = ((float) ast_format_rate(chan->writeformat)) / ((float) ast_format_rate(f->subclass.codec)); + factor = ((float) ast_format_rate(&chan->writeformat)) / ((float) ast_format_rate(&f->subclass.format)); samples = (int) ( ((float) f->samples) * factor ); } else { samples = f->samples; @@ -4066,11 +4079,11 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) ast_frfree(f); f = &ast_null_frame; } - } else if ((f->frametype == AST_FRAME_VOICE) && !(f->subclass.codec & chan->nativeformats)) { + } else if ((f->frametype == AST_FRAME_VOICE) && !ast_format_cap_iscompatible(chan->nativeformats, &f->subclass.format)) { /* This frame is not one of the current native formats -- drop it on the floor */ char to[200]; ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n", - chan->name, ast_getformatname(f->subclass.codec), ast_getformatname_multiple(to, sizeof(to), chan->nativeformats)); + chan->name, ast_getformatname(&f->subclass.format), ast_getformatname_multiple(to, sizeof(to), chan->nativeformats)); ast_frfree(f); f = &ast_null_frame; } else if ((f->frametype == AST_FRAME_VOICE)) { @@ -4086,7 +4099,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) #ifndef MONITOR_CONSTANT_DELAY int jump = chan->outsmpl - chan->insmpl - 4 * f->samples; if (jump >= 0) { - jump = calc_monitor_jump((chan->outsmpl - chan->insmpl), ast_format_rate(f->subclass.codec), ast_format_rate(chan->monitor->read_stream->fmt->format)); + jump = calc_monitor_jump((chan->outsmpl - chan->insmpl), ast_format_rate(&f->subclass.format), ast_format_rate(&chan->monitor->read_stream->fmt->format)); if (ast_seekstream(chan->monitor->read_stream, jump, SEEK_FORCECUR) == -1) ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n"); chan->insmpl += (chan->outsmpl - chan->insmpl) + f->samples; @@ -4557,7 +4570,7 @@ int ast_prod(struct ast_channel *chan) /* Send an empty audio frame to get things moving */ if (chan->_state != AST_STATE_UP) { ast_debug(1, "Prodding channel '%s'\n", chan->name); - a.subclass.codec = chan->rawwriteformat; + ast_format_copy(&a.subclass.format, &chan->rawwriteformat); a.data.ptr = nothing + AST_FRIENDLY_OFFSET; a.src = "ast_prod"; /* this better match check in ast_write */ if (ast_write(chan, &a)) @@ -4808,12 +4821,12 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) if (chan->tech->write == NULL) break; /*! \todo XXX should return 0 maybe ? */ - if (ast_opt_generic_plc && fr->subclass.codec == AST_FORMAT_SLINEAR) { + if (ast_opt_generic_plc && fr->subclass.format.id == AST_FORMAT_SLINEAR) { apply_plc(chan, fr); } /* If the frame is in the raw write format, then it's easy... just use the frame - otherwise we will have to translate */ - if (fr->subclass.codec == chan->rawwriteformat) + if (ast_format_cmp(&fr->subclass.format, &chan->rawwriteformat) != AST_FORMAT_CMP_NOT_EQUAL) f = fr; else f = (chan->writetrans) ? ast_translate(chan->writetrans, fr, 0) : fr; @@ -4880,7 +4893,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) #ifndef MONITOR_CONSTANT_DELAY int jump = chan->insmpl - chan->outsmpl - 4 * cur->samples; if (jump >= 0) { - jump = calc_monitor_jump((chan->insmpl - chan->outsmpl), ast_format_rate(f->subclass.codec), ast_format_rate(chan->monitor->read_stream->fmt->format)); + jump = calc_monitor_jump((chan->insmpl - chan->outsmpl), ast_format_rate(&f->subclass.format), ast_format_rate(&chan->monitor->read_stream->fmt->format)); if (ast_seekstream(chan->monitor->write_stream, jump, SEEK_FORCECUR) == -1) ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n"); chan->outsmpl += (chan->insmpl - chan->outsmpl) + cur->samples; @@ -4967,26 +4980,33 @@ done: return res; } -static int set_format(struct ast_channel *chan, format_t fmt, format_t *rawformat, format_t *format, - struct ast_trans_pvt **trans, const int direction) +static int set_format(struct ast_channel *chan, + struct ast_format_cap *cap_set, + struct ast_format *rawformat, + struct ast_format *format, + struct ast_trans_pvt **trans, + const int direction) { - format_t native, native_fmt = ast_best_codec(fmt); + struct ast_format_cap *cap_native = chan->nativeformats; + struct ast_format best_set_fmt; + struct ast_format best_native_fmt; int res; char from[200], to[200]; - - /* Make sure we only consider audio */ - fmt &= AST_FORMAT_AUDIO_MASK; - - native = chan->nativeformats; - if (!fmt || !native) /* No audio requested */ - return 0; /* Let's try a call without any sounds (video, text) */ + ast_best_codec(cap_set, &best_set_fmt); /* See if the underlying channel driver is capable of performing transcoding for us */ - if (!ast_channel_setoption(chan, direction ? AST_OPTION_FORMAT_WRITE : AST_OPTION_FORMAT_READ, &native_fmt, sizeof(int*), 0)) { + if (!ast_channel_setoption(chan, direction ? AST_OPTION_FORMAT_WRITE : AST_OPTION_FORMAT_READ, &best_set_fmt, sizeof(best_set_fmt), 0)) { ast_debug(1, "Channel driver natively set channel %s to %s format %s\n", chan->name, - direction ? "write" : "read", ast_getformatname(native_fmt)); - chan->nativeformats = *rawformat = *format = native_fmt; + direction ? "write" : "read", ast_getformatname(&best_set_fmt)); + + ast_format_copy(format, &best_set_fmt); + ast_format_copy(rawformat, &best_set_fmt); + + ast_channel_lock(chan); + ast_format_cap_set(chan->nativeformats, &best_set_fmt); + ast_channel_unlock(chan); + if (*trans) { ast_translator_free_path(*trans); } @@ -4995,39 +5015,44 @@ static int set_format(struct ast_channel *chan, format_t fmt, format_t *rawforma } /* Find a translation path from the native format to one of the desired formats */ - if (!direction) + if (!direction) { /* reading */ - res = ast_translator_best_choice(&fmt, &native); - else + res = ast_translator_best_choice(cap_set, cap_native, &best_set_fmt, &best_native_fmt); + } else { /* writing */ - res = ast_translator_best_choice(&native, &fmt); + res = ast_translator_best_choice(cap_native, cap_set, &best_native_fmt, &best_set_fmt); + } if (res < 0) { ast_log(LOG_WARNING, "Unable to find a codec translation path from %s to %s\n", - ast_getformatname_multiple(from, sizeof(from), native), - ast_getformatname_multiple(to, sizeof(to), fmt)); + ast_getformatname_multiple(from, sizeof(from), cap_native), + ast_getformatname_multiple(to, sizeof(to), cap_set)); return -1; } - + /* Now we have a good choice for both. */ ast_channel_lock(chan); - if ((*rawformat == native) && (*format == fmt) && ((*rawformat == *format) || (*trans))) { + if ((ast_format_cmp(rawformat, &best_native_fmt) != AST_FORMAT_CMP_NOT_EQUAL) && + (ast_format_cmp(format, &best_set_fmt) != AST_FORMAT_CMP_NOT_EQUAL) && + ((ast_format_cmp(rawformat, format) != AST_FORMAT_CMP_NOT_EQUAL) || (*trans))) { /* the channel is already in these formats, so nothing to do */ ast_channel_unlock(chan); return 0; } - *rawformat = native; + ast_format_copy(rawformat, &best_native_fmt); /* User perspective is fmt */ - *format = fmt; + ast_format_copy(format, &best_set_fmt); + /* Free any read translation we have right now */ if (*trans) { ast_translator_free_path(*trans); *trans = NULL; } + /* Build a translation path from the raw format to the desired format */ - if (*format == *rawformat) { + if (ast_format_cmp(format, rawformat) != AST_FORMAT_CMP_NOT_EQUAL) { /* * If we were able to swap the native format to the format that * has been requested, then there is no need to try to build @@ -5037,29 +5062,122 @@ static int set_format(struct ast_channel *chan, format_t fmt, format_t *rawforma } else { if (!direction) { /* reading */ - *trans = ast_translator_build_path(*format, *rawformat); + *trans = ast_translator_build_path(format, rawformat); } else { /* writing */ - *trans = ast_translator_build_path(*rawformat, *format); + *trans = ast_translator_build_path(rawformat, format); } res = *trans ? 0 : -1; } ast_channel_unlock(chan); - ast_debug(1, "Set channel %s to %s format %s\n", chan->name, - direction ? "write" : "read", ast_getformatname(fmt)); + + ast_debug(1, "Set channel %s to %s format %s\n", + chan->name, + direction ? "write" : "read", + ast_getformatname(&best_set_fmt)); + return res; +} + +int ast_set_read_format(struct ast_channel *chan, struct ast_format *format) +{ + struct ast_format_cap *cap = ast_format_cap_alloc_nolock(); + int res; + if (!cap) { + return -1; + } + ast_format_cap_add(cap, format); + + res = set_format(chan, + cap, + &chan->rawreadformat, + &chan->readformat, + &chan->readtrans, + 0); + + ast_format_cap_destroy(cap); return res; } -int ast_set_read_format(struct ast_channel *chan, format_t fmt) +int ast_set_read_format_by_id(struct ast_channel *chan, enum ast_format_id id) { - return set_format(chan, fmt, &chan->rawreadformat, &chan->readformat, - &chan->readtrans, 0); + struct ast_format_cap *cap = ast_format_cap_alloc_nolock(); + struct ast_format tmp_format; + int res; + if (!cap) { + return -1; + } + ast_format_cap_add(cap, ast_format_set(&tmp_format, id, 0)); + + res = set_format(chan, + cap, + &chan->rawreadformat, + &chan->readformat, + &chan->readtrans, + 0); + + ast_format_cap_destroy(cap); + return res; } -int ast_set_write_format(struct ast_channel *chan, format_t fmt) +int ast_set_read_format_from_cap(struct ast_channel *chan, struct ast_format_cap *cap) { - return set_format(chan, fmt, &chan->rawwriteformat, &chan->writeformat, - &chan->writetrans, 1); + return set_format(chan, + cap, + &chan->rawreadformat, + &chan->readformat, + &chan->readtrans, + 0); +} + +int ast_set_write_format(struct ast_channel *chan, struct ast_format *format) +{ + struct ast_format_cap *cap = ast_format_cap_alloc_nolock(); + int res; + if (!cap) { + return -1; + } + ast_format_cap_add(cap, format); + + res = set_format(chan, + cap, + &chan->rawwriteformat, + &chan->writeformat, + &chan->writetrans, + 1); + + ast_format_cap_destroy(cap); + return res; +} + +int ast_set_write_format_by_id(struct ast_channel *chan, enum ast_format_id id) +{ + struct ast_format_cap *cap = ast_format_cap_alloc_nolock(); + struct ast_format tmp_format; + int res; + if (!cap) { + return -1; + } + ast_format_cap_add(cap, ast_format_set(&tmp_format, id, 0)); + + res = set_format(chan, + cap, + &chan->rawwriteformat, + &chan->writeformat, + &chan->writetrans, + 1); + + ast_format_cap_destroy(cap); + return res; +} + +int ast_set_write_format_from_cap(struct ast_channel *chan, struct ast_format_cap *cap) +{ + return set_format(chan, + cap, + &chan->rawwriteformat, + &chan->writeformat, + &chan->writetrans, + 1); } const char *ast_channel_reason2str(int reason) @@ -5098,7 +5216,7 @@ static void handle_cause(int cause, int *outstate) } } -struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_channel *orig, int *timeout, format_t format, struct outgoing_helper *oh, int *outstate) +struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_channel *orig, int *timeout, struct ast_format_cap *cap, struct outgoing_helper *oh, int *outstate) { char tmpchan[256]; struct ast_channel *new = NULL; @@ -5121,7 +5239,7 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan data = tmpchan; type = "Local"; } - if (!(new = ast_request(type, format, orig, data, &cause))) { + if (!(new = ast_request(type, cap, orig, data, &cause))) { ast_log(LOG_NOTICE, "Unable to create channel for call forward to '%s/%s' (cause = %d)\n", type, data, cause); handle_cause(cause, outstate); ast_hangup(orig); @@ -5179,7 +5297,7 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan return new; } -struct ast_channel *__ast_request_and_dial(const char *type, format_t format, const struct ast_channel *requestor, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh) +struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh) { int dummy_outstate; int cause = 0; @@ -5193,7 +5311,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, format_t format, co else outstate = &dummy_outstate; /* make outstate always a valid pointer */ - chan = ast_request(type, format, requestor, data, &cause); + chan = ast_request(type, cap, requestor, data, &cause); if (!chan) { ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data); handle_cause(cause, outstate); @@ -5245,7 +5363,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, format_t format, co if (timeout > -1) timeout = res; if (!ast_strlen_zero(chan->call_forward)) { - if (!(chan = ast_call_forward(NULL, chan, NULL, format, oh, outstate))) { + if (!(chan = ast_call_forward(NULL, chan, NULL, cap, oh, outstate))) { return NULL; } continue; @@ -5338,9 +5456,9 @@ struct ast_channel *__ast_request_and_dial(const char *type, format_t format, co return chan; } -struct ast_channel *ast_request_and_dial(const char *type, format_t format, const struct ast_channel *requestor, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname) +struct ast_channel *ast_request_and_dial(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname) { - return __ast_request_and_dial(type, format, requestor, data, timeout, outstate, cidnum, cidname, NULL); + return __ast_request_and_dial(type, cap, requestor, data, timeout, outstate, cidnum, cidname, NULL); } static int set_security_requirements(const struct ast_channel *requestor, struct ast_channel *out) @@ -5383,16 +5501,12 @@ static int set_security_requirements(const struct ast_channel *requestor, struct return 0; } -struct ast_channel *ast_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause) +struct ast_channel *ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_channel *requestor, void *data, int *cause) { struct chanlist *chan; struct ast_channel *c; - format_t capabilities; - format_t fmt; int res; int foo; - format_t videoformat = format & AST_FORMAT_VIDEO_MASK; - format_t textformat = format & AST_FORMAT_TEXT_MASK; if (!cause) cause = &foo; @@ -5404,21 +5518,27 @@ struct ast_channel *ast_request(const char *type, format_t format, const struct } AST_RWLIST_TRAVERSE(&backends, chan, list) { + struct ast_format_cap *tmp_cap; + struct ast_format tmp_fmt; + struct ast_format best_audio_fmt; + struct ast_format_cap *joint_cap; + if (strcasecmp(type, chan->tech->type)) continue; - capabilities = chan->tech->capabilities; - fmt = format & AST_FORMAT_AUDIO_MASK; - if (fmt) { + ast_format_clear(&best_audio_fmt); + /* find the best audio format to use */ + if ((tmp_cap = ast_format_cap_get_type(request_cap, AST_FORMAT_TYPE_AUDIO))) { /* We have audio - is it possible to connect the various calls to each other? (Avoid this check for calls without audio, like text+video calls) */ - res = ast_translator_best_choice(&fmt, &capabilities); + res = ast_translator_best_choice(tmp_cap, chan->tech->capabilities, &tmp_fmt, &best_audio_fmt); + ast_format_cap_destroy(tmp_cap); if (res < 0) { char tmp1[256], tmp2[256]; ast_log(LOG_WARNING, "No translator path exists for channel type %s (native %s) to %s\n", type, ast_getformatname_multiple(tmp1, sizeof(tmp1), chan->tech->capabilities), - ast_getformatname_multiple(tmp2, sizeof(tmp2), format)); + ast_getformatname_multiple(tmp2, sizeof(tmp2), request_cap)); *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; AST_RWLIST_UNLOCK(&backends); return NULL; @@ -5428,8 +5548,21 @@ struct ast_channel *ast_request(const char *type, format_t format, const struct if (!chan->tech->requester) return NULL; - if (!(c = chan->tech->requester(type, capabilities | videoformat | textformat, requestor, data, cause))) + /* XXX Only the audio format calculated as being the best for translation + * purposes is used for the request. This needs to be re-evaluated. It may be + * a better choice to send all the audio formats capable of being translated + * during the request and allow the channel drivers to pick the best one. */ + if (!(joint_cap = ast_format_cap_dup(request_cap))) { return NULL; + } + ast_format_cap_remove_bytype(joint_cap, AST_FORMAT_TYPE_AUDIO); + ast_format_cap_add(joint_cap, &best_audio_fmt); + + if (!(c = chan->tech->requester(type, joint_cap, requestor, data, cause))) { + ast_format_cap_destroy(joint_cap); + return NULL; + } + joint_cap = ast_format_cap_destroy(joint_cap); if (set_security_requirements(requestor, c)) { ast_log(LOG_WARNING, "Setting security requirements failed\n"); @@ -5610,7 +5743,10 @@ int ast_channel_sendurl(struct ast_channel *chan, const char *url) /*! \brief Set up translation from one channel to another */ static int ast_channel_make_compatible_helper(struct ast_channel *from, struct ast_channel *to) { - format_t src, dst; + struct ast_format_cap *src_cap = from->nativeformats; /* shallow copy, do not destroy */ + struct ast_format_cap *dst_cap = to->nativeformats; /* shallow copy, do not destroy */ + struct ast_format best_src_fmt; + struct ast_format best_dst_fmt; int use_slin; /* See if the channel driver can natively make these two channels compatible */ @@ -5619,20 +5755,17 @@ static int ast_channel_make_compatible_helper(struct ast_channel *from, struct a return 0; } - if (from->readformat == to->writeformat && from->writeformat == to->readformat) { + if ((ast_format_cmp(&from->readformat, &to->writeformat) != AST_FORMAT_CMP_NOT_EQUAL) && + (ast_format_cmp(&to->readformat, &from->writeformat) != AST_FORMAT_CMP_NOT_EQUAL)) { /* Already compatible! Moving on ... */ return 0; } - /* Set up translation from the 'from' channel to the 'to' channel */ - src = from->nativeformats; - dst = to->nativeformats; - /* If there's no audio in this call, don't bother with trying to find a translation path */ - if ((src & AST_FORMAT_AUDIO_MASK) == 0 || (dst & AST_FORMAT_AUDIO_MASK) == 0) + if (!ast_format_cap_has_type(src_cap, AST_FORMAT_TYPE_AUDIO) || !ast_format_cap_has_type(dst_cap, AST_FORMAT_TYPE_AUDIO)) return 0; - if (ast_translator_best_choice(&dst, &src) < 0) { + if (ast_translator_best_choice(dst_cap, src_cap, &best_src_fmt, &best_dst_fmt) < 0) { ast_log(LOG_WARNING, "No path to translate from %s to %s\n", from->name, to->name); return -1; } @@ -5643,16 +5776,20 @@ static int ast_channel_make_compatible_helper(struct ast_channel *from, struct a * no direct conversion available. If generic PLC is * desired, then transcoding via SLINEAR is a requirement */ - use_slin = (src == AST_FORMAT_SLINEAR || dst == AST_FORMAT_SLINEAR); - if ((src != dst) && (ast_opt_generic_plc || ast_opt_transcode_via_slin) && - (ast_translate_path_steps(dst, src) != 1 || use_slin)) - dst = AST_FORMAT_SLINEAR; - if (ast_set_read_format(from, dst) < 0) { - ast_log(LOG_WARNING, "Unable to set read format on channel %s to %s\n", from->name, ast_getformatname(dst)); + use_slin = (best_src_fmt.id == AST_FORMAT_SLINEAR || best_dst_fmt.id == AST_FORMAT_SLINEAR); + if ((ast_format_cmp(&best_src_fmt, &best_dst_fmt) == AST_FORMAT_CMP_NOT_EQUAL) && + (ast_opt_generic_plc || ast_opt_transcode_via_slin) && + (ast_translate_path_steps(&best_dst_fmt, &best_src_fmt) != 1 || use_slin)) { + + ast_format_set(&best_dst_fmt, AST_FORMAT_SLINEAR, 0); + } + + if (ast_set_read_format(from, &best_dst_fmt) < 0) { + ast_log(LOG_WARNING, "Unable to set read format on channel %s to %s\n", from->name, ast_getformatname(&best_dst_fmt)); return -1; } - if (ast_set_write_format(to, dst) < 0) { - ast_log(LOG_WARNING, "Unable to set write format on channel %s to %s\n", to->name, ast_getformatname(dst)); + if (ast_set_write_format(to, &best_dst_fmt) < 0) { + ast_log(LOG_WARNING, "Unable to set write format on channel %s to %s\n", to->name, ast_getformatname(&best_dst_fmt)); return -1; } return 0; @@ -6199,8 +6336,7 @@ static void masquerade_colp_transfer(struct ast_channel *transferee, struct xfer */ int ast_do_masquerade(struct ast_channel *original) { - format_t x; - int i; + int x, i; int res=0; int origstate; int visible_indication; @@ -6218,13 +6354,17 @@ int ast_do_masquerade(struct ast_channel *original) struct ast_cdr *cdr; struct ast_datastore *xfer_ds; struct xfer_masquerade_ds *xfer_colp; - format_t rformat = original->readformat; - format_t wformat = original->writeformat; + struct ast_format rformat; + struct ast_format wformat; + struct ast_format tmp_format; char newn[AST_CHANNEL_NAME]; char orig[AST_CHANNEL_NAME]; char masqn[AST_CHANNEL_NAME]; char zombn[AST_CHANNEL_NAME]; + ast_format_copy(&rformat, &original->readformat); + ast_format_copy(&wformat, &original->writeformat); + /* XXX This operation is a bit odd. We're essentially putting the guts of * the clone channel into the original channel. Start by killing off the * original channel's backend. While the features are nice, which is the @@ -6383,12 +6523,13 @@ int ast_do_masquerade(struct ast_channel *original) } /* Swap the raw formats */ - x = original->rawreadformat; - original->rawreadformat = clonechan->rawreadformat; - clonechan->rawreadformat = x; - x = original->rawwriteformat; - original->rawwriteformat = clonechan->rawwriteformat; - clonechan->rawwriteformat = x; + ast_format_copy(&tmp_format, &original->rawreadformat); + ast_format_copy(&original->rawreadformat, &clonechan->rawreadformat); + ast_format_copy(&clonechan->rawreadformat, &tmp_format); + + ast_format_copy(&tmp_format, &original->rawwriteformat); + ast_format_copy(&original->rawwriteformat, &clonechan->rawwriteformat); + ast_format_copy(&clonechan->rawwriteformat, &tmp_format); clonechan->_softhangup = AST_SOFTHANGUP_DEV; @@ -6486,16 +6627,16 @@ int ast_do_masquerade(struct ast_channel *original) ast_channel_set_fd(original, AST_TIMING_FD, original->timingfd); /* Our native formats are different now */ - original->nativeformats = clonechan->nativeformats; + ast_format_cap_copy(original->nativeformats, clonechan->nativeformats); /* Context, extension, priority, app data, jump table, remain the same */ /* pvt switches. pbx stays the same, as does next */ /* Set the write format */ - ast_set_write_format(original, wformat); + ast_set_write_format(original, &wformat); /* Set the read format */ - ast_set_read_format(original, rformat); + ast_set_read_format(original, &rformat); /* Copy the music class */ ast_string_field_set(original, musicclass, clonechan->musicclass); @@ -6509,7 +6650,7 @@ int ast_do_masquerade(struct ast_channel *original) } ast_debug(1, "Putting channel %s in %s/%s formats\n", original->name, - ast_getformatname(wformat), ast_getformatname(rformat)); + ast_getformatname(&wformat), ast_getformatname(&rformat)); /* Okay. Last thing is to let the channel driver know about all this mess, so he can fix up everything as best as possible */ @@ -6753,8 +6894,8 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct struct ast_channel *cs[3]; struct ast_frame *f; enum ast_bridge_result res = AST_BRIDGE_COMPLETE; - format_t o0nativeformats; - format_t o1nativeformats; + struct ast_format_cap *o0nativeformats; + struct ast_format_cap *o1nativeformats; int watch_c0_dtmf; int watch_c1_dtmf; void *pvt0, *pvt1; @@ -6762,13 +6903,20 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct int frame_put_in_jb = 0; int jb_in_use; int to; - + + o0nativeformats = ast_format_cap_dup(c0->nativeformats); + o1nativeformats = ast_format_cap_dup(c1->nativeformats); + + if (!o0nativeformats || !o1nativeformats) { + ast_format_cap_destroy(o0nativeformats); /* NULL safe */ + ast_format_cap_destroy(o1nativeformats); /* NULL safe */ + return AST_BRIDGE_FAILED; + } + cs[0] = c0; cs[1] = c1; pvt0 = c0->tech_pvt; pvt1 = c1->tech_pvt; - o0nativeformats = c0->nativeformats; - o1nativeformats = c1->nativeformats; watch_c0_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_0; watch_c1_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_1; @@ -6790,8 +6938,8 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct struct ast_channel *who, *other; if ((c0->tech_pvt != pvt0) || (c1->tech_pvt != pvt1) || - (o0nativeformats != c0->nativeformats) || - (o1nativeformats != c1->nativeformats)) { + (!ast_format_cap_identical(o0nativeformats, c0->nativeformats)) || + (!ast_format_cap_identical(o1nativeformats, c1->nativeformats))) { /* Check for Masquerade, codec changes, etc */ res = AST_BRIDGE_RETRY; break; @@ -6941,6 +7089,9 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct ast_poll_channel_del(c0, c1); + ast_format_cap_destroy(o0nativeformats); + ast_format_cap_destroy(o1nativeformats); + return res; } @@ -7050,8 +7201,8 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha { struct ast_channel *chans[2] = { c0, c1 }; enum ast_bridge_result res = AST_BRIDGE_COMPLETE; - format_t o0nativeformats; - format_t o1nativeformats; + struct ast_format_cap *o0nativeformats; + struct ast_format_cap *o1nativeformats; long time_left_ms=0; char caller_warning = 0; char callee_warning = 0; @@ -7072,6 +7223,16 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha ast_test_flag(c1, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1)) return -1; + o0nativeformats = ast_format_cap_dup(c0->nativeformats); + o1nativeformats = ast_format_cap_dup(c1->nativeformats); + if (!o0nativeformats || !o1nativeformats) { + ast_format_cap_destroy(o0nativeformats); + ast_format_cap_destroy(o1nativeformats); + ast_log(LOG_WARNING, "failed to copy native formats\n"); + return -1; + } + + *fo = NULL; if (ast_tvzero(config->start_time)) { @@ -7092,9 +7253,6 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha ast_set_owners_and_peers(c0, c1); - o0nativeformats = c0->nativeformats; - o1nativeformats = c1->nativeformats; - if (config->feature_timer && !ast_tvzero(config->nexteventts)) { config->nexteventts = ast_tvadd(config->feature_start_time, ast_samp2tv(config->feature_timer, 1000)); } else if (config->timelimit) { @@ -7245,6 +7403,8 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha c0->_bridge = NULL; c1->_bridge = NULL; + ast_format_cap_destroy(o0nativeformats); + ast_format_cap_destroy(o1nativeformats); return res; } else { ast_clear_flag(c0, AST_FLAG_NBRIDGE); @@ -7264,16 +7424,21 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha } } - if (((c0->writeformat != c1->readformat) || (c0->readformat != c1->writeformat) || - (c0->nativeformats != o0nativeformats) || (c1->nativeformats != o1nativeformats)) && + if (((ast_format_cmp(&c1->readformat, &c0->writeformat) == AST_FORMAT_CMP_NOT_EQUAL) || + (ast_format_cmp(&c0->readformat, &c1->writeformat) == AST_FORMAT_CMP_NOT_EQUAL) || + !ast_format_cap_identical(c0->nativeformats, o0nativeformats) || + !ast_format_cap_identical(c1->nativeformats, o1nativeformats)) && !(c0->generator || c1->generator)) { if (ast_channel_make_compatible(c0, c1)) { ast_log(LOG_WARNING, "Can't make %s and %s compatible\n", c0->name, c1->name); manager_bridge_event(0, 1, c0, c1); + ast_format_cap_destroy(o0nativeformats); + ast_format_cap_destroy(o1nativeformats); return AST_BRIDGE_FAILED; } - o0nativeformats = c0->nativeformats; - o1nativeformats = c1->nativeformats; + + ast_format_cap_copy(o0nativeformats, c0->nativeformats); + ast_format_cap_copy(o1nativeformats, c1->nativeformats); } update_bridge_vars(c0, c1); @@ -7310,6 +7475,8 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha S_COR(c1->caller.id.number.valid, c1->caller.id.number.str, "<unknown>")); ast_debug(1, "Bridge stops bridging channels %s and %s\n", c0->name, c1->name); + ast_format_cap_destroy(o0nativeformats); + ast_format_cap_destroy(o1nativeformats); return res; } @@ -7356,7 +7523,7 @@ struct tonepair_state { int v1_2; int v2_2; int v3_2; - format_t origwfmt; + struct ast_format origwfmt; int pos; int duration; int modulate; @@ -7370,7 +7537,7 @@ static void tonepair_release(struct ast_channel *chan, void *params) struct tonepair_state *ts = params; if (chan) - ast_set_write_format(chan, ts->origwfmt); + ast_set_write_format(chan, &ts->origwfmt); ast_free(ts); } @@ -7381,8 +7548,8 @@ static void *tonepair_alloc(struct ast_channel *chan, void *params) if (!(ts = ast_calloc(1, sizeof(*ts)))) return NULL; - ts->origwfmt = chan->writeformat; - if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) { + ast_format_copy(&ts->origwfmt, &chan->writeformat); + if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR)) { ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name); tonepair_release(NULL, ts); ts = NULL; @@ -7436,7 +7603,7 @@ static int tonepair_generator(struct ast_channel *chan, void *data, int len, int ts->data[x] = ts->v3_1 + ts->v3_2; } ts->f.frametype = AST_FRAME_VOICE; - ts->f.subclass.codec = AST_FORMAT_SLINEAR; + ast_format_set(&ts->f.subclass.format, AST_FORMAT_SLINEAR, 0); ts->f.datalen = len; ts->f.samples = samples; ts->f.offset = AST_FRIENDLY_OFFSET; @@ -7778,11 +7945,11 @@ static int silence_generator_generate(struct ast_channel *chan, void *data, int short buf[samples]; struct ast_frame frame = { .frametype = AST_FRAME_VOICE, - .subclass.codec = AST_FORMAT_SLINEAR, .data.ptr = buf, .samples = samples, .datalen = sizeof(buf), }; + ast_format_set(&frame.subclass.format, AST_FORMAT_SLINEAR, 0); memset(buf, 0, sizeof(buf)); @@ -7799,7 +7966,7 @@ static struct ast_generator silence_generator = { }; struct ast_silence_generator { - int old_write_format; + struct ast_format old_write_format; }; struct ast_silence_generator *ast_channel_start_silence_generator(struct ast_channel *chan) @@ -7810,9 +7977,9 @@ struct ast_silence_generator *ast_channel_start_silence_generator(struct ast_cha return NULL; } - state->old_write_format = chan->writeformat; + ast_format_copy(&state->old_write_format, &chan->writeformat); - if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { + if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) { ast_log(LOG_ERROR, "Could not set write format to SLINEAR\n"); ast_free(state); return NULL; @@ -7834,7 +8001,7 @@ void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_sil ast_debug(1, "Stopped silence generator on '%s'\n", chan->name); - if (ast_set_write_format(chan, state->old_write_format) < 0) + if (ast_set_write_format(chan, &state->old_write_format) < 0) ast_log(LOG_ERROR, "Could not return write format to its original state\n"); ast_free(state); diff --git a/main/cli.c b/main/cli.c index 7f4c1791f..671a2bac7 100644 --- a/main/cli.c +++ b/main/cli.c @@ -1387,7 +1387,7 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar struct timeval now; struct ast_str *out = ast_str_thread_get(&ast_str_thread_global_buf, 16); char cdrtime[256]; - char nf[256], wf[256], rf[256]; + char nf[256]; struct ast_str *write_transpath = ast_str_alloca(256); struct ast_str *read_transpath = ast_str_alloca(256); long elapsed_seconds=0; @@ -1469,9 +1469,9 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar S_OR(c->dialed.number.str, "(N/A)"), c->language, ast_state2str(c->_state), c->_state, c->rings, - ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), - ast_getformatname_multiple(wf, sizeof(wf), c->writeformat), - ast_getformatname_multiple(rf, sizeof(rf), c->readformat), + ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), + ast_getformatname(&c->writeformat), + ast_getformatname(&c->readformat), c->writetrans ? "Yes" : "No", ast_translate_path_to_str(c->writetrans, &write_transpath), c->readtrans ? "Yes" : "No", diff --git a/main/data.c b/main/data.c index 3b2c124ab..3ca2f7c27 100644 --- a/main/data.c +++ b/main/data.c @@ -3100,7 +3100,7 @@ static int manager_data_get(struct mansession *s, const struct message *m) return RESULT_SUCCESS; } -int ast_data_add_codecs(struct ast_data *root, const char *node_name, format_t capability) +int ast_data_add_codec(struct ast_data *root, const char *node_name, struct ast_format *format) { struct ast_data *codecs, *codec; size_t fmlist_size; @@ -3113,7 +3113,36 @@ int ast_data_add_codecs(struct ast_data *root, const char *node_name, format_t c } fmlist = ast_get_format_list(&fmlist_size); for (x = 0; x < fmlist_size; x++) { - if (fmlist[x].bits & capability) { + if (fmlist[x].id == format->id) { + codec = ast_data_add_node(codecs, "codec"); + if (!codec) { + return -1; + } + ast_data_add_str(codec, "name", fmlist[x].name); + ast_data_add_int(codec, "samplespersecond", fmlist[x].samplespersecond); + ast_data_add_str(codec, "description", fmlist[x].desc); + ast_data_add_int(codec, "frame_length", fmlist[x].fr_len); + } + } + + return 0; +} + +int ast_data_add_codecs(struct ast_data *root, const char *node_name, struct ast_format_cap *cap) +{ + struct ast_data *codecs, *codec; + size_t fmlist_size; + const struct ast_format_list *fmlist; + struct ast_format tmp_fmt; + int x; + + codecs = ast_data_add_node(root, node_name); + if (!codecs) { + return -1; + } + fmlist = ast_get_format_list(&fmlist_size); + for (x = 0; x < fmlist_size; x++) { + if (ast_format_cap_iscompatible(cap, ast_format_set(&tmp_fmt, fmlist[x].id, 0))) { codec = ast_data_add_node(codecs, "codec"); if (!codec) { return -1; diff --git a/main/dial.c b/main/dial.c index ba1a2bb53..5c30e287d 100644 --- a/main/dial.c +++ b/main/dial.c @@ -248,13 +248,27 @@ static int begin_dial_channel(struct ast_dial_channel *channel, struct ast_chann { char numsubst[AST_MAX_EXTENSION]; int res = 1; + struct ast_format_cap *cap_all_audio = NULL; + struct ast_format_cap *cap_request; /* Copy device string over */ ast_copy_string(numsubst, channel->device, sizeof(numsubst)); + if (chan) { + cap_request = chan->nativeformats; + } else { + cap_all_audio = ast_format_cap_alloc_nolock(); + ast_format_cap_add_all_by_type(cap_all_audio, AST_FORMAT_TYPE_AUDIO); + cap_request = cap_all_audio; + } + /* If we fail to create our owner channel bail out */ - if (!(channel->owner = ast_request(channel->tech, chan ? chan->nativeformats : AST_FORMAT_AUDIO_MASK, chan, numsubst, &channel->cause))) + if (!(channel->owner = ast_request(channel->tech, cap_request, chan, numsubst, &channel->cause))) { + cap_all_audio = ast_format_cap_destroy(cap_all_audio); return -1; + } + cap_request = NULL; + cap_all_audio = ast_format_cap_destroy(cap_all_audio); channel->owner->appl = "AppDial2"; channel->owner->data = "(Outgoing Line)"; diff --git a/main/dsp.c b/main/dsp.c index cba01b5f1..69989afe8 100644 --- a/main/dsp.c +++ b/main/dsp.c @@ -1106,7 +1106,7 @@ int ast_dsp_call_progress(struct ast_dsp *dsp, struct ast_frame *inf) ast_log(LOG_WARNING, "Can't check call progress of non-voice frames\n"); return 0; } - if (inf->subclass.codec != AST_FORMAT_SLINEAR) { + if (inf->subclass.format.id != AST_FORMAT_SLINEAR) { ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n"); return 0; } @@ -1280,7 +1280,7 @@ int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence) ast_log(LOG_WARNING, "Can't calculate silence on a non-voice frame\n"); return 0; } - if (f->subclass.codec != AST_FORMAT_SLINEAR) { + if (f->subclass.format.id != AST_FORMAT_SLINEAR) { ast_log(LOG_WARNING, "Can only calculate silence on signed-linear frames :(\n"); return 0; } @@ -1298,7 +1298,7 @@ int ast_dsp_noise(struct ast_dsp *dsp, struct ast_frame *f, int *totalnoise) ast_log(LOG_WARNING, "Can't calculate noise on a non-voice frame\n"); return 0; } - if (f->subclass.codec != AST_FORMAT_SLINEAR) { + if (f->subclass.format.id != AST_FORMAT_SLINEAR) { ast_log(LOG_WARNING, "Can only calculate noise on signed-linear frames :(\n"); return 0; } @@ -1329,7 +1329,7 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, odata = af->data.ptr; len = af->datalen; /* Make sure we have short data */ - switch (af->subclass.codec) { + switch (af->subclass.format.id) { case AST_FORMAT_SLINEAR: shortdata = af->data.ptr; len = af->datalen / 2; @@ -1350,7 +1350,7 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, default: /*Display warning only once. Otherwise you would get hundreds of warnings every second */ if (dsp->display_inband_dtmf_warning) - ast_log(LOG_WARNING, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_getformatname(af->subclass.codec)); + ast_log(LOG_WARNING, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_getformatname(&af->subclass.format)); dsp->display_inband_dtmf_warning = 0; return af; } @@ -1479,7 +1479,7 @@ done: memset(shortdata + dsp->mute_data[x].start, 0, sizeof(int16_t) * (dsp->mute_data[x].end - dsp->mute_data[x].start)); } - switch (af->subclass.codec) { + switch (af->subclass.format.id) { case AST_FORMAT_SLINEAR: break; case AST_FORMAT_ULAW: @@ -1491,6 +1491,8 @@ done: for (x = 0; x < len; x++) { odata[x] = AST_LIN2A((unsigned short) shortdata[x]); } + /* fall through */ + default: break; } diff --git a/main/features.c b/main/features.c index 413f8170a..f35a2475a 100644 --- a/main/features.c +++ b/main/features.c @@ -743,7 +743,7 @@ static void check_goto_on_transfer(struct ast_channel *chan) static struct ast_channel *feature_request_and_dial(struct ast_channel *caller, const char *caller_name, struct ast_channel *requestor, - struct ast_channel *transferee, const char *type, format_t format, void *data, + struct ast_channel *transferee, const char *type, struct ast_format_cap *cap, void *data, int timeout, int *outstate, const char *language); /*! @@ -1315,17 +1315,20 @@ static int fake_fixup(struct ast_channel *clonechan, struct ast_channel *origina static struct ast_channel *create_test_channel(const struct ast_channel_tech *fake_tech) { struct ast_channel *test_channel1; + struct ast_format tmp_fmt; if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, 0, "TestChannel1"))) { return NULL; } /* normally this is done in the channel driver */ - test_channel1->nativeformats = AST_FORMAT_GSM; - test_channel1->writeformat = AST_FORMAT_GSM; - test_channel1->rawwriteformat = AST_FORMAT_GSM; - test_channel1->readformat = AST_FORMAT_GSM; - test_channel1->rawreadformat = AST_FORMAT_GSM; + ast_format_cap_add(test_channel1->nativeformats, ast_format_set(&tmp_fmt, AST_FORMAT_GSM, 0)); + + ast_format_set(&test_channel1->writeformat, AST_FORMAT_GSM, 0); + ast_format_set(&test_channel1->rawwriteformat, AST_FORMAT_GSM, 0); + ast_format_set(&test_channel1->readformat, AST_FORMAT_GSM, 0); + ast_format_set(&test_channel1->rawreadformat, AST_FORMAT_GSM, 0); + test_channel1->tech = fake_tech; return test_channel1; @@ -2136,7 +2139,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st /* Dial party C */ newchan = feature_request_and_dial(transferer, transferer_name_orig, transferer, - transferee, "Local", ast_best_codec(transferer->nativeformats), xferto, + transferee, "Local", transferer->nativeformats, xferto, atxfernoanswertimeout, &outstate, transferer->language); ast_debug(2, "Dial party C result: newchan:%d, outstate:%d\n", !!newchan, outstate); @@ -2243,14 +2246,13 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st transferer_tech, transferer_name); newchan = feature_request_and_dial(transferer, transferer_name_orig, transferee, transferee, transferer_tech, - ast_best_codec(transferee->nativeformats), transferer_name, + transferee->nativeformats, transferer_name, atxfernoanswertimeout, &outstate, transferer->language); ast_debug(2, "Dial party B result: newchan:%d, outstate:%d\n", !!newchan, outstate); if (newchan || ast_check_hangup(transferee)) { break; } - ++tries; if (atxfercallbackretries <= tries) { /* No more callback tries remaining. */ @@ -2272,7 +2274,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st ast_debug(1, "We're retrying to call %s/%s\n", "Local", xferto); newchan = feature_request_and_dial(transferer, transferer_name_orig, transferer, transferee, "Local", - ast_best_codec(transferee->nativeformats), xferto, + transferee->nativeformats, xferto, atxfernoanswertimeout, &outstate, transferer->language); ast_debug(2, "Redial party C result: newchan:%d, outstate:%d\n", !!newchan, outstate); @@ -2980,7 +2982,7 @@ static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer, */ static struct ast_channel *feature_request_and_dial(struct ast_channel *caller, const char *caller_name, struct ast_channel *requestor, - struct ast_channel *transferee, const char *type, format_t format, void *data, + struct ast_channel *transferee, const char *type, struct ast_format_cap *cap, void *data, int timeout, int *outstate, const char *language) { int state = 0; @@ -2996,12 +2998,21 @@ static struct ast_channel *feature_request_and_dial(struct ast_channel *caller, struct timeval started; int x, len = 0; char *disconnect_code = NULL, *dialed_code = NULL; + struct ast_format_cap *tmp_cap; + struct ast_format best_audio_fmt; struct ast_frame *f; AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames; + tmp_cap = ast_format_cap_alloc_nolock(); + if (!tmp_cap) { + return NULL; + } + ast_best_codec(cap, &best_audio_fmt); + ast_format_cap_add(tmp_cap, &best_audio_fmt); + caller_hungup = ast_check_hangup(caller); - if (!(chan = ast_request(type, format, requestor, data, &cause))) { + if (!(chan = ast_request(type, tmp_cap, requestor, data, &cause))) { ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data); switch (cause) { case AST_CAUSE_BUSY: @@ -3119,8 +3130,7 @@ static struct ast_channel *feature_request_and_dial(struct ast_channel *caller, } } else if (chan == active_channel) { if (!ast_strlen_zero(chan->call_forward)) { - state = 0; - chan = ast_call_forward(caller, chan, NULL, format, NULL, &state); + chan = ast_call_forward(caller, chan, NULL, tmp_cap, NULL, &state); if (!chan) { break; } @@ -3252,6 +3262,8 @@ done: chan = NULL; } + tmp_cap = ast_format_cap_destroy(tmp_cap); + if (outstate) *outstate = state; diff --git a/main/file.c b/main/file.c index c2ab096cd..57bf1570c 100644 --- a/main/file.c +++ b/main/file.c @@ -59,11 +59,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") */ int ast_language_is_prefix = 1; -static AST_RWLIST_HEAD_STATIC(formats, ast_format); +static AST_RWLIST_HEAD_STATIC(formats, ast_format_def); -int __ast_format_register(const struct ast_format *f, struct ast_module *mod) +int __ast_format_def_register(const struct ast_format_def *f, struct ast_module *mod) { - struct ast_format *tmp; + struct ast_format_def *tmp; AST_RWLIST_WRLOCK(&formats); AST_RWLIST_TRAVERSE(&formats, tmp, list) { @@ -98,9 +98,9 @@ int __ast_format_register(const struct ast_format *f, struct ast_module *mod) return 0; } -int ast_format_unregister(const char *name) +int ast_format_def_unregister(const char *name) { - struct ast_format *tmp; + struct ast_format_def *tmp; int res = -1; AST_RWLIST_WRLOCK(&formats); @@ -130,8 +130,8 @@ int ast_stopstream(struct ast_channel *tmp) if (tmp->stream) { ast_closestream(tmp->stream); tmp->stream = NULL; - if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat)) - ast_log(LOG_WARNING, "Unable to restore format back to %s\n", ast_getformatname(tmp->oldwriteformat)); + if (tmp->oldwriteformat.id && ast_set_write_format(tmp, &tmp->oldwriteformat)) + ast_log(LOG_WARNING, "Unable to restore format back to %s\n", ast_getformatname(&tmp->oldwriteformat)); } /* Stop the video stream too */ if (tmp->vstream != NULL) { @@ -149,10 +149,10 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f) int res = -1; int alt = 0; if (f->frametype == AST_FRAME_VIDEO) { - if (fs->fmt->format & AST_FORMAT_AUDIO_MASK) { + if (AST_FORMAT_GET_TYPE(fs->fmt->format.id) == AST_FORMAT_TYPE_AUDIO) { /* This is the audio portion. Call the video one... */ if (!fs->vfs && fs->filename) { - const char *type = ast_getformatname(f->subclass.codec & ~0x1); + const char *type = ast_getformatname(&f->subclass.format); fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode); ast_debug(1, "Opened video output file\n"); } @@ -168,7 +168,7 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f) ast_log(LOG_WARNING, "Tried to write non-voice frame\n"); return -1; } - if (((fs->fmt->format | alt) & f->subclass.codec) == f->subclass.codec) { + if (ast_format_cmp(&f->subclass.format, &fs->fmt->format) != AST_FORMAT_CMP_NOT_EQUAL) { res = fs->fmt->write(fs, f); if (res < 0) ast_log(LOG_WARNING, "Natural write failed\n"); @@ -177,18 +177,18 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f) } else { /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't the one we've setup a translator for, we do the "wrong thing" XXX */ - if (fs->trans && f->subclass.codec != fs->lastwriteformat) { + if (fs->trans && (ast_format_cmp(&f->subclass.format, &fs->lastwriteformat) != AST_FORMAT_CMP_EQUAL)) { ast_translator_free_path(fs->trans); fs->trans = NULL; } if (!fs->trans) - fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass.codec); + fs->trans = ast_translator_build_path(&fs->fmt->format, &f->subclass.format); if (!fs->trans) ast_log(LOG_WARNING, "Unable to translate to format %s, source format %s\n", - fs->fmt->name, ast_getformatname(f->subclass.codec)); + fs->fmt->name, ast_getformatname(&f->subclass.format)); else { struct ast_frame *trf; - fs->lastwriteformat = f->subclass.codec; + ast_format_copy(&fs->lastwriteformat, &f->subclass.format); /* Get the translated frame but don't consume the original in case they're using it on another stream */ if ((trf = ast_translate(fs->trans, f, 0))) { struct ast_frame *cur; @@ -296,7 +296,7 @@ static void filestream_destructor(void *arg) /* Stop a running stream if there is one */ if (f->owner) { - if (f->fmt->format < AST_FORMAT_AUDIO_MASK) { + if (AST_FORMAT_GET_TYPE(f->fmt->format.id) == AST_FORMAT_TYPE_AUDIO) { f->owner->stream = NULL; AST_SCHED_DEL(f->owner->sched, f->owner->streamid); ast_settimeout(f->owner, 0, NULL, NULL); @@ -335,7 +335,7 @@ static void filestream_destructor(void *arg) ast_module_unref(f->fmt->module); } -static struct ast_filestream *get_filestream(struct ast_format *fmt, FILE *bfile) +static struct ast_filestream *get_filestream(struct ast_format_def *fmt, FILE *bfile) { struct ast_filestream *s; @@ -361,7 +361,7 @@ enum wrap_fn { WRAP_OPEN, WRAP_REWRITE }; static int fn_wrapper(struct ast_filestream *s, const char *comment, enum wrap_fn mode) { - struct ast_format *f = s->fmt; + struct ast_format_def *f = s->fmt; int ret = -1; int (*openfn)(struct ast_filestream *s); @@ -396,18 +396,20 @@ enum file_action { }; /*! + * \internal * \brief perform various actions on a file. Second argument - * arg2 depends on the command: - * unused for EXISTS and DELETE + * \note arg2 depends on the command: + * unused for DELETE + * optional ast_format_cap holding all the formats found for a file, for EXISTS. * destination file name (const char *) for COPY and RENAME * struct ast_channel * for OPEN * if fmt is NULL, OPEN will return the first matching entry, * whereas other functions will run on all matching entries. */ -static format_t ast_filehelper(const char *filename, const void *arg2, const char *fmt, const enum file_action action) +static int filehelper(const char *filename, const void *arg2, const char *fmt, const enum file_action action) { - struct ast_format *f; - format_t res = (action == ACTION_EXISTS) ? 0 : -1; + struct ast_format_def *f; + int res = (action == ACTION_EXISTS) ? 0 : -1; AST_RWLIST_RDLOCK(&formats); /* Check for a specific format */ @@ -441,9 +443,9 @@ static format_t ast_filehelper(const char *filename, const void *arg2, const cha FILE *bfile; struct ast_filestream *s; - if ( !(chan->writeformat & f->format) && - !((f->format & AST_FORMAT_AUDIO_MASK && fmt) || - (f->format & AST_FORMAT_VIDEO_MASK && fmt))) { + if ((ast_format_cmp(&chan->writeformat, &f->format) == AST_FORMAT_CMP_NOT_EQUAL) && + !(((AST_FORMAT_GET_TYPE(f->format.id) == AST_FORMAT_TYPE_AUDIO) && fmt) || + ((AST_FORMAT_GET_TYPE(f->format.id) == AST_FORMAT_TYPE_VIDEO) && fmt))) { ast_free(fn); continue; /* not a supported format */ } @@ -471,7 +473,7 @@ static format_t ast_filehelper(const char *filename, const void *arg2, const cha s->fmt = f; s->trans = NULL; s->filename = NULL; - if (s->fmt->format & AST_FORMAT_AUDIO_MASK) { + if (AST_FORMAT_GET_TYPE(s->fmt->format.id) == AST_FORMAT_TYPE_AUDIO) { if (chan->stream) ast_closestream(chan->stream); chan->stream = s; @@ -488,7 +490,12 @@ static format_t ast_filehelper(const char *filename, const void *arg2, const cha break; /* will never get here */ case ACTION_EXISTS: /* return the matching format */ - res |= f->format; + /* if arg2 is present, it is a format capabilities structure. + * Add this format to the set of formats this file can be played in */ + if (arg2) { + ast_format_cap_add((struct ast_format_cap *) arg2, &f->format); + } + res = 1; /* file does exist and format it exists in is returned in arg2 */ break; case ACTION_DELETE: @@ -527,11 +534,17 @@ static int is_absolute_path(const char *filename) return filename[0] == '/'; } -static format_t fileexists_test(const char *filename, const char *fmt, const char *lang, - char *buf, int buflen) +/*! + * \brief test if a file exists for a given format. + * \note result_cap is OPTIONAL + * \retval 1, true and result_cap represents format capabilities file exists in. + * \retval 0, false + */ +static int fileexists_test(const char *filename, const char *fmt, const char *lang, + char *buf, int buflen, struct ast_format_cap *result_cap) { if (buf == NULL) { - return -1; + return 0; } if (ast_language_is_prefix && !is_absolute_path(filename)) { /* new layout */ @@ -550,25 +563,36 @@ static format_t fileexists_test(const char *filename, const char *fmt, const cha } } - return ast_filehelper(buf, NULL, fmt, ACTION_EXISTS); + return filehelper(buf, result_cap, fmt, ACTION_EXISTS); } /*! * \brief helper routine to locate a file with a given format * and language preference. - * Try preflang, preflang with stripped '_' suffices, or NULL. + * + * \note Try preflang, preflang with stripped '_' suffices, or NULL. * - * The last parameter(s) point to a buffer of sufficient size, + * \note The last parameter(s) point to a buffer of sufficient size, * which on success is filled with the matching filename. + * + * \param filename, name of the file. + * \param fmt, format to look for the file in. OPTIONAL + * \param preflang, the perfered language + * \param buf, returns the matching filename + * \param buflen, size of the buf + * \param result_cap, OPTIONAL format capabilities result structure + * returns what formats the file was found in. + * + * \retval 1, true. file exists and result format is set + * \retval 0, false. file does not exist. */ -static format_t fileexists_core(const char *filename, const char *fmt, const char *preflang, - char *buf, int buflen) +static int fileexists_core(const char *filename, const char *fmt, const char *preflang, + char *buf, int buflen, struct ast_format_cap *result_cap) { - format_t res = -1; char *lang; if (buf == NULL) { - return -1; + return 0; } /* We try languages in the following order: @@ -584,8 +608,8 @@ static format_t fileexists_core(const char *filename, const char *fmt, const cha while (!ast_strlen_zero(lang)) { char *end; - if ((res = fileexists_test(filename, fmt, lang, buf, buflen)) > 0) { - return res; + if (fileexists_test(filename, fmt, lang, buf, buflen, result_cap)) { + return 1; } if ((end = strrchr(lang, '_')) != NULL) { @@ -597,14 +621,14 @@ static format_t fileexists_core(const char *filename, const char *fmt, const cha } /* Try without any language */ - if ((res = fileexists_test(filename, fmt, NULL, buf, buflen)) > 0) { - return res; + if (fileexists_test(filename, fmt, NULL, buf, buflen, result_cap)) { + return 1; } /* Finally try the default language unless it was already tried before */ if ((ast_strlen_zero(preflang) || strcmp(preflang, DEFAULT_LANGUAGE)) && (ast_strlen_zero(lang) || strcmp(lang, DEFAULT_LANGUAGE))) { - if ((res = fileexists_test(filename, fmt, DEFAULT_LANGUAGE, buf, buflen)) > 0) { - return res; + if ((fileexists_test(filename, fmt, DEFAULT_LANGUAGE, buf, buflen, result_cap)) > 0) { + return 1; } } @@ -623,7 +647,8 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char * language and format, set up a suitable translator, * and open the stream. */ - format_t fmts, res; + struct ast_format_cap *file_fmt_cap; + int res; int buflen; char *buf; @@ -639,20 +664,29 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char buf = alloca(buflen); if (buf == NULL) return NULL; - fmts = fileexists_core(filename, NULL, preflang, buf, buflen); - if (fmts > 0) - fmts &= AST_FORMAT_AUDIO_MASK; - if (fmts < 1) { + + if (!(file_fmt_cap = ast_format_cap_alloc_nolock())) { + return NULL; + } + if (!fileexists_core(filename, NULL, preflang, buf, buflen, file_fmt_cap) || + !ast_format_cap_has_type(file_fmt_cap, AST_FORMAT_TYPE_AUDIO)) { + ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename); + file_fmt_cap = ast_format_cap_destroy(file_fmt_cap); return NULL; } - chan->oldwriteformat = chan->writeformat; - /* Set the channel to a format we can work with */ - res = ast_set_write_format(chan, fmts); + + /* Set the channel to a format we can work with and save off the previous format. */ + ast_format_copy(&chan->oldwriteformat, &chan->writeformat); + /* Set the channel to the best format that exists for the file. */ + res = ast_set_write_format_from_cap(chan, file_fmt_cap); + /* don't need this anymore now that the channel's write format is set. */ + file_fmt_cap = ast_format_cap_destroy(file_fmt_cap); + if (res == -1) { /* No format available that works with this channel */ return NULL; } - res = ast_filehelper(buf, chan, NULL, ACTION_OPEN); + res = filehelper(buf, chan, NULL, ACTION_OPEN); if (res >= 0) return chan->stream; return NULL; @@ -663,9 +697,12 @@ struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *fil /* As above, but for video. But here we don't have translators * so we must enforce a format. */ - format_t format; + struct ast_format tmp_fmt; + struct ast_format_cap *tmp_cap; char *buf; int buflen; + const char *fmt; + int fd; if (preflang == NULL) preflang = ""; @@ -674,20 +711,39 @@ struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *fil if (buf == NULL) return NULL; - for (format = AST_FORMAT_AUDIO_MASK + 1; format <= AST_FORMAT_VIDEO_MASK; format = format << 1) { - int fd; - const char *fmt; + /* is the channel capable of video without translation ?*/ + if (!ast_format_cap_has_type(chan->nativeformats, AST_FORMAT_TYPE_VIDEO)) { + return NULL; + } + if (!(tmp_cap = ast_format_cap_alloc_nolock())) { + return NULL; + } + /* Video is supported, so see what video formats exist for this file */ + if (!fileexists_core(filename, NULL, preflang, buf, buflen, tmp_cap)) { + tmp_cap = ast_format_cap_destroy(tmp_cap); + return NULL; + } - if (!(chan->nativeformats & format)) + /* iterate over file formats and pick the first one compatible with the channel's native formats */ + ast_format_cap_iter_start(tmp_cap); + while (!ast_format_cap_iter_next(tmp_cap, &tmp_fmt)) { + fmt = ast_getformatname(&tmp_fmt); + if ((AST_FORMAT_GET_TYPE(tmp_fmt.id) != AST_FORMAT_TYPE_VIDEO) || + !ast_format_cap_iscompatible(chan->nativeformats, &tmp_fmt)) { continue; - fmt = ast_getformatname(format); - if ( fileexists_core(filename, fmt, preflang, buf, buflen) < 1) /* no valid format */ - continue; - fd = ast_filehelper(buf, chan, fmt, ACTION_OPEN); - if (fd >= 0) + } + + fd = filehelper(buf, chan, fmt, ACTION_OPEN); + if (fd >= 0) { + ast_format_cap_iter_end(tmp_cap); + tmp_cap = ast_format_cap_destroy(tmp_cap); return chan->vstream; + } ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename); } + ast_format_cap_iter_end(tmp_cap); + tmp_cap = ast_format_cap_destroy(tmp_cap); + return NULL; } @@ -759,7 +815,7 @@ static enum fsread_res ast_readaudio_callback(struct ast_filestream *s) if (whennext != s->lasttimeout) { if (s->owner->timingfd > -1) { - float samp_rate = (float) ast_format_rate(s->fmt->format); + float samp_rate = (float) ast_format_rate(&s->fmt->format); unsigned int rate; rate = (unsigned int) roundf(samp_rate / ((float) whennext)); @@ -767,7 +823,7 @@ static enum fsread_res ast_readaudio_callback(struct ast_filestream *s) ast_settimeout(s->owner, rate, ast_fsread_audio, s); } else { s->owner->streamid = ast_sched_add(s->owner->sched, - whennext / (ast_format_rate(s->fmt->format) / 1000), ast_fsread_audio, s); + whennext / (ast_format_rate(&s->fmt->format) / 1000), ast_fsread_audio, s); } s->lasttimeout = whennext; return FSREAD_SUCCESS_NOSCHED; @@ -818,7 +874,7 @@ static enum fsread_res ast_readvideo_callback(struct ast_filestream *s) if (whennext != s->lasttimeout) { s->owner->vstreamid = ast_sched_add(s->owner->sched, - whennext / (ast_format_rate(s->fmt->format) / 1000), + whennext / (ast_format_rate(&s->fmt->format) / 1000), ast_fsread_video, s); s->lasttimeout = whennext; return FSREAD_SUCCESS_NOSCHED; @@ -850,7 +906,7 @@ int ast_playstream(struct ast_filestream *s) { enum fsread_res res; - if (s->fmt->format & AST_FORMAT_AUDIO_MASK) + if (AST_FORMAT_GET_TYPE(s->fmt->format.id) == AST_FORMAT_TYPE_AUDIO) res = ast_readaudio_callback(s); else res = ast_readvideo_callback(s); @@ -892,7 +948,7 @@ int ast_closestream(struct ast_filestream *f) /* Stop a running stream if there is one */ if (f->owner) { - if (f->fmt->format < AST_FORMAT_AUDIO_MASK) { + if (AST_FORMAT_GET_TYPE(f->fmt->format.id) == AST_FORMAT_TYPE_AUDIO) { f->owner->stream = NULL; AST_SCHED_DEL(f->owner->sched, f->owner->streamid); ast_settimeout(f->owner, 0, NULL, NULL); @@ -921,22 +977,22 @@ int ast_fileexists(const char *filename, const char *fmt, const char *preflang) buf = alloca(buflen); if (buf == NULL) return 0; - return fileexists_core(filename, fmt, preflang, buf, buflen); + return fileexists_core(filename, fmt, preflang, buf, buflen, NULL) ? 1 : 0; } int ast_filedelete(const char *filename, const char *fmt) { - return ast_filehelper(filename, NULL, fmt, ACTION_DELETE); + return filehelper(filename, NULL, fmt, ACTION_DELETE); } int ast_filerename(const char *filename, const char *filename2, const char *fmt) { - return ast_filehelper(filename, filename2, fmt, ACTION_RENAME); + return filehelper(filename, filename2, fmt, ACTION_RENAME); } int ast_filecopy(const char *filename, const char *filename2, const char *fmt) { - return ast_filehelper(filename, filename2, fmt, ACTION_COPY); + return filehelper(filename, filename2, fmt, ACTION_COPY); } int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang) @@ -966,7 +1022,7 @@ int ast_streamfile(struct ast_channel *chan, const char *filename, const char *p vfs = ast_openvstream(chan, filename, preflang); if (vfs) { - ast_debug(1, "Ooh, found a video stream, too, format %s\n", ast_getformatname(vfs->fmt->format)); + ast_debug(1, "Ooh, found a video stream, too, format %s\n", ast_getformatname(&vfs->fmt->format)); } if (ast_test_flag(chan, AST_FLAG_MASQ_NOSTREAM)) @@ -978,7 +1034,7 @@ int ast_streamfile(struct ast_channel *chan, const char *filename, const char *p res = ast_playstream(fs); if (!res && vfs) res = ast_playstream(vfs); - ast_verb(3, "<%s> Playing '%s.%s' (language '%s')\n", chan->name, filename, ast_getformatname(chan->writeformat), preflang ? preflang : "default"); + ast_verb(3, "<%s> Playing '%s.%s' (language '%s')\n", chan->name, filename, ast_getformatname(&chan->writeformat), preflang ? preflang : "default"); return res; } @@ -986,7 +1042,7 @@ int ast_streamfile(struct ast_channel *chan, const char *filename, const char *p struct ast_filestream *ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode) { FILE *bfile; - struct ast_format *f; + struct ast_format_def *f; struct ast_filestream *fs = NULL; char *fn; int format_found = 0; @@ -1036,7 +1092,7 @@ struct ast_filestream *ast_writefile(const char *filename, const char *type, con int fd, myflags = 0; /* compiler claims this variable can be used before initialization... */ FILE *bfile = NULL; - struct ast_format *f; + struct ast_format_def *f; struct ast_filestream *fs = NULL; char *buf = NULL; size_t size = 0; @@ -1359,8 +1415,8 @@ int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char * char *ast_format_str_reduce(char *fmts) { - struct ast_format *f; - struct ast_format *fmts_ptr[AST_MAX_FORMATS]; + struct ast_format_def *f; + struct ast_format_def *fmts_ptr[AST_MAX_FORMATS]; char *fmts_str[AST_MAX_FORMATS]; char *stringp, *type; char *orig = fmts; @@ -1437,7 +1493,7 @@ static char *handle_cli_core_show_file_formats(struct ast_cli_entry *e, int cmd, { #define FORMAT "%-10s %-10s %-20s\n" #define FORMAT2 "%-10s %-10s %-20s\n" - struct ast_format *f; + struct ast_format_def *f; int count_fmt = 0; switch (cmd) { @@ -1459,7 +1515,7 @@ static char *handle_cli_core_show_file_formats(struct ast_cli_entry *e, int cmd, AST_RWLIST_RDLOCK(&formats); AST_RWLIST_TRAVERSE(&formats, f, list) { - ast_cli(a->fd, FORMAT2, ast_getformatname(f->format), f->name, f->exts); + ast_cli(a->fd, FORMAT2, ast_getformatname(&f->format), f->name, f->exts); count_fmt++; } AST_RWLIST_UNLOCK(&formats); diff --git a/main/format.c b/main/format.c new file mode 100644 index 000000000..d77d244a6 --- /dev/null +++ b/main/format.c @@ -0,0 +1,558 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2010, Digium, Inc. + * + * David Vossel <dvossel@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Format API + * + * \author David Vossel <dvossel@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); + +#include "asterisk/_private.h" +#include "asterisk/version.h" +#include "asterisk/format.h" +#include "asterisk/astobj2.h" +#include "asterisk/lock.h" + +/*! This is the container for all the format attribute interfaces. + * An ao2 container was chosen for fast lookup. */ +static struct ao2_container *interfaces; + +/*! This is the lock used to protect the interfaces container. Yes, ao2_containers + * do have their own locking, but we need the capability of performing read/write + * locks on this specific container. */ +static ast_rwlock_t ilock; + +/*! a wrapper is used put interfaces into the ao2 container. */ +struct interface_ao2_wrapper { + enum ast_format_id id; + const struct ast_format_attr_interface *interface; + /*! a read write lock must be used to protect the wrapper instead + * of the ao2 lock. */ + ast_rwlock_t wraplock; +}; + +static int interface_cmp_cb(void *obj, void *arg, int flags) +{ + struct interface_ao2_wrapper *wrapper1 = obj; + struct interface_ao2_wrapper *wrapper2 = arg; + + return (wrapper2->id == wrapper1->id) ? CMP_MATCH | CMP_STOP : 0; +} + +static int interface_hash_cb(const void *obj, const int flags) +{ + const struct interface_ao2_wrapper *wrapper = obj; + return wrapper->id; +} + +static void interface_destroy_cb(void *obj) +{ + struct interface_ao2_wrapper *wrapper = obj; + ast_rwlock_destroy(&wrapper->wraplock); +} + +void ast_format_copy(struct ast_format *dst, const struct ast_format *src) +{ + memcpy(dst, src, sizeof(struct ast_format)); +} + +void ast_format_set_video_mark(struct ast_format *format) +{ + format->fattr.rtp_marker_bit = 1; +} + +int ast_format_get_video_mark(const struct ast_format *format) +{ + return format->fattr.rtp_marker_bit; +} + +static struct interface_ao2_wrapper *find_interface(const struct ast_format *format) +{ + struct interface_ao2_wrapper *wrapper; + struct interface_ao2_wrapper tmp_wrapper = { + .id = format->id, + }; + + ast_rwlock_rdlock(&ilock); + if (!(wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_NOLOCK)))) { + ast_rwlock_unlock(&ilock); + return NULL; + } + ast_rwlock_unlock(&ilock); + + return wrapper; +} + +/*! \internal + * \brief set format attributes using an interface + */ +static int format_set_helper(struct ast_format *format, va_list ap) +{ + struct interface_ao2_wrapper *wrapper; + + if (!(wrapper = find_interface(format))) { + ast_log(LOG_WARNING, "Could not find format interface to set.\n"); + return -1; + } + + ast_rwlock_rdlock(&wrapper->wraplock); + if (!wrapper->interface || !wrapper->interface->format_attr_set) { + ast_rwlock_unlock(&wrapper->wraplock); + ao2_ref(wrapper, -1); + return -1; + } + + wrapper->interface->format_attr_set(&format->fattr, ap); + + ast_rwlock_unlock(&wrapper->wraplock); + ao2_ref(wrapper, -1); + + return 0; +} + +struct ast_format *ast_format_append(struct ast_format *format, ... ) +{ + va_list ap; + va_start(ap, format); + format_set_helper(format, ap); + va_end(ap); + + return format; +} + +struct ast_format *ast_format_set(struct ast_format *format, enum ast_format_id id, int set_attributes, ... ) +{ + /* initialize the structure before setting it. */ + ast_format_clear(format); + + format->id = id; + + if (set_attributes) { + va_list ap; + va_start(ap, set_attributes); + format_set_helper(format, ap); + va_end(ap); + } + + return format; +} + +void ast_format_clear(struct ast_format *format) +{ + format->id = 0; + memset(&format->fattr, 0, sizeof(format->fattr)); +} + +/*! \internal + * \brief determine if a list of attribute key value pairs are set on a format + */ +static int format_isset_helper(struct ast_format *format, va_list ap) +{ + int res; + struct interface_ao2_wrapper *wrapper; + struct ast_format tmp = { + .id = format->id, + .fattr = { { 0, }, }, + }; + + if (!(wrapper = find_interface(format))) { + return -1; + } + + ast_rwlock_rdlock(&wrapper->wraplock); + if (!wrapper->interface || + !wrapper->interface->format_attr_set || + !wrapper->interface->format_attr_cmp) { + + ast_rwlock_unlock(&wrapper->wraplock); + ao2_ref(wrapper, -1); + return -1; + } + + wrapper->interface->format_attr_set(&tmp.fattr, ap); + + /* use our tmp structure to tell if the attributes are set or not */ + res = wrapper->interface->format_attr_cmp(&tmp.fattr, &format->fattr); + + ast_rwlock_unlock(&wrapper->wraplock); + ao2_ref(wrapper, -1); + + return (res == AST_FORMAT_CMP_NOT_EQUAL) ? -1 : 0; +} + +int ast_format_isset(struct ast_format *format, ... ) +{ + va_list ap; + int res; + + va_start(ap, format); + res = format_isset_helper(format, ap); + va_end(ap); + return res; +} + + +/*! \internal + * \brief cmp format attributes using an interface + */ +static enum ast_format_cmp_res format_cmp_helper(const struct ast_format *format1, const struct ast_format *format2) +{ + enum ast_format_cmp_res res = AST_FORMAT_CMP_EQUAL; + struct interface_ao2_wrapper *wrapper; + + if (!(wrapper = find_interface(format1))) { + return res; + } + + ast_rwlock_rdlock(&wrapper->wraplock); + if (!wrapper->interface || !wrapper->interface->format_attr_cmp) { + ast_rwlock_unlock(&wrapper->wraplock); + ao2_ref(wrapper, -1); + return res; + } + + res = wrapper->interface->format_attr_cmp(&format1->fattr, &format2->fattr); + + ast_rwlock_unlock(&wrapper->wraplock); + ao2_ref(wrapper, -1); + + return res; +} + +enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2) +{ + if (format1->id != format2->id) { + return AST_FORMAT_CMP_NOT_EQUAL; + } + + return format_cmp_helper(format1, format2); +} + +/*! \internal + * \brief get joint format attributes using an interface + */ +static int format_joint_helper(const struct ast_format *format1, const struct ast_format *format2, struct ast_format *result) +{ + int res = 0; + struct interface_ao2_wrapper *wrapper; + + if (!(wrapper = find_interface(format1))) { + /* if no interface is present, we assume formats are joint by id alone */ + return res; + } + + ast_rwlock_rdlock(&wrapper->wraplock); + if (wrapper->interface && wrapper->interface->format_attr_get_joint) { + res = wrapper->interface->format_attr_get_joint(&format1->fattr, &format2->fattr, &result->fattr); + } + ast_rwlock_unlock(&wrapper->wraplock); + + ao2_ref(wrapper, -1); + + return res; +} + +int ast_format_joint(const struct ast_format *format1, const struct ast_format *format2, struct ast_format *result) +{ + if (format1->id != format2->id) { + return -1; + } + result->id = format1->id; + return format_joint_helper(format1, format2, result); +} + + +uint64_t ast_format_id_to_old_bitfield(enum ast_format_id id) +{ + switch (id) { + /*! G.723.1 compression */ + case AST_FORMAT_G723_1: + return (1ULL << 0); + /*! GSM compression */ + case AST_FORMAT_GSM: + return (1ULL << 1); + /*! Raw mu-law data (G.711) */ + case AST_FORMAT_ULAW: + return (1ULL << 2); + /*! Raw A-law data (G.711) */ + case AST_FORMAT_ALAW: + return (1ULL << 3); + /*! ADPCM (G.726, 32kbps, AAL2 codeword packing) */ + case AST_FORMAT_G726_AAL2: + return (1ULL << 4); + /*! ADPCM (IMA) */ + case AST_FORMAT_ADPCM: + return (1ULL << 5); + /*! Raw 16-bit Signed Linear (8000 Hz) PCM */ + case AST_FORMAT_SLINEAR: + return (1ULL << 6); + /*! LPC10, 180 samples/frame */ + case AST_FORMAT_LPC10: + return (1ULL << 7); + /*! G.729A audio */ + case AST_FORMAT_G729A: + return (1ULL << 8); + /*! SpeeX Free Compression */ + case AST_FORMAT_SPEEX: + return (1ULL << 9); + /*! iLBC Free Compression */ + case AST_FORMAT_ILBC: + return (1ULL << 10); + /*! ADPCM (G.726, 32kbps, RFC3551 codeword packing) */ + case AST_FORMAT_G726: + return (1ULL << 11); + /*! G.722 */ + case AST_FORMAT_G722: + return (1ULL << 12); + /*! G.722.1 (also known as Siren7, 32kbps assumed) */ + case AST_FORMAT_SIREN7: + return (1ULL << 13); + /*! G.722.1 Annex C (also known as Siren14, 48kbps assumed) */ + case AST_FORMAT_SIREN14: + return (1ULL << 14); + /*! Raw 16-bit Signed Linear (16000 Hz) PCM */ + case AST_FORMAT_SLINEAR16: + return (1ULL << 15); + /*! G.719 (64 kbps assumed) */ + case AST_FORMAT_G719: + return (1ULL << 32); + /*! SpeeX Wideband (16kHz) Free Compression */ + case AST_FORMAT_SPEEX16: + return (1ULL << 33); + /*! Raw mu-law data (G.711) */ + case AST_FORMAT_TESTLAW: + return (1ULL << 47); + + /*! H.261 Video */ + case AST_FORMAT_H261: + return (1ULL << 18); + /*! H.263 Video */ + case AST_FORMAT_H263: + return (1ULL << 19); + /*! H.263+ Video */ + case AST_FORMAT_H263_PLUS: + return (1ULL << 20); + /*! H.264 Video */ + case AST_FORMAT_H264: + return (1ULL << 21); + /*! MPEG4 Video */ + case AST_FORMAT_MP4_VIDEO: + return (1ULL << 22); + + /*! JPEG Images */ + case AST_FORMAT_JPEG: + return (1ULL << 16); + /*! PNG Images */ + case AST_FORMAT_PNG: + return (1ULL << 17); + + /*! T.140 RED Text format RFC 4103 */ + case AST_FORMAT_T140RED: + return (1ULL << 26); + /*! T.140 Text format - ITU T.140, RFC 4103 */ + case AST_FORMAT_T140: + return (1ULL << 27); + } + + return 0; + +} +uint64_t ast_format_to_old_bitfield(const struct ast_format *format) +{ + return ast_format_id_to_old_bitfield(format->id); +} + +struct ast_format *ast_format_from_old_bitfield(struct ast_format *dst, uint64_t src) +{ + switch (src) { + /*! G.723.1 compression */ + case (1ULL << 0): + return ast_format_set(dst, AST_FORMAT_G723_1, 0); + /*! GSM compression */ + case (1ULL << 1): + return ast_format_set(dst, AST_FORMAT_GSM, 0); + /*! Raw mu-law data (G.711) */ + case (1ULL << 2): + return ast_format_set(dst, AST_FORMAT_ULAW, 0); + /*! Raw A-law data (G.711) */ + case (1ULL << 3): + return ast_format_set(dst, AST_FORMAT_ALAW, 0); + /*! ADPCM (G.726, 32kbps, AAL2 codeword packing) */ + case (1ULL << 4): + return ast_format_set(dst, AST_FORMAT_G726_AAL2, 0); + /*! ADPCM (IMA) */ + case (1ULL << 5): + return ast_format_set(dst, AST_FORMAT_ADPCM, 0); + /*! Raw 16-bit Signed Linear (8000 Hz) PCM */ + case (1ULL << 6): + return ast_format_set(dst, AST_FORMAT_SLINEAR, 0); + /*! LPC10, 180 samples/frame */ + case (1ULL << 7): + return ast_format_set(dst, AST_FORMAT_LPC10, 0); + /*! G.729A audio */ + case (1ULL << 8): + return ast_format_set(dst, AST_FORMAT_G729A, 0); + /*! SpeeX Free Compression */ + case (1ULL << 9): + return ast_format_set(dst, AST_FORMAT_SPEEX, 0); + /*! iLBC Free Compression */ + case (1ULL << 10): + return ast_format_set(dst, AST_FORMAT_ILBC, 0); + /*! ADPCM (G.726, 32kbps, RFC3551 codeword packing) */ + case (1ULL << 11): + return ast_format_set(dst, AST_FORMAT_G726, 0); + /*! G.722 */ + case (1ULL << 12): + return ast_format_set(dst, AST_FORMAT_G722, 0); + /*! G.722.1 (also known as Siren7, 32kbps assumed) */ + case (1ULL << 13): + return ast_format_set(dst, AST_FORMAT_SIREN7, 0); + /*! G.722.1 Annex C (also known as Siren14, 48kbps assumed) */ + case (1ULL << 14): + return ast_format_set(dst, AST_FORMAT_SIREN14, 0); + /*! Raw 16-bit Signed Linear (16000 Hz) PCM */ + case (1ULL << 15): + return ast_format_set(dst, AST_FORMAT_SLINEAR16, 0); + /*! G.719 (64 kbps assumed) */ + case (1ULL << 32): + return ast_format_set(dst, AST_FORMAT_G719, 0); + /*! SpeeX Wideband (16kHz) Free Compression */ + case (1ULL << 33): + return ast_format_set(dst, AST_FORMAT_SPEEX16, 0); + /*! Raw mu-law data (G.711) */ + case (1ULL << 47): + return ast_format_set(dst, AST_FORMAT_TESTLAW, 0); + + /*! H.261 Video */ + case (1ULL << 18): + return ast_format_set(dst, AST_FORMAT_H261, 0); + /*! H.263 Video */ + case (1ULL << 19): + return ast_format_set(dst, AST_FORMAT_H263, 0); + /*! H.263+ Video */ + case (1ULL << 20): + return ast_format_set(dst, AST_FORMAT_H263_PLUS, 0); + /*! H.264 Video */ + case (1ULL << 21): + return ast_format_set(dst, AST_FORMAT_H264, 0); + /*! MPEG4 Video */ + case (1ULL << 22): + return ast_format_set(dst, AST_FORMAT_MP4_VIDEO, 0); + + /*! JPEG Images */ + case (1ULL << 16): + return ast_format_set(dst, AST_FORMAT_JPEG, 0); + /*! PNG Images */ + case (1ULL << 17): + return ast_format_set(dst, AST_FORMAT_PNG, 0); + + /*! T.140 RED Text format RFC 4103 */ + case (1ULL << 26): + return ast_format_set(dst, AST_FORMAT_T140RED, 0); + /*! T.140 Text format - ITU T.140, RFC 4103 */ + case (1ULL << 27): + return ast_format_set(dst, AST_FORMAT_T140, 0); + } + ast_format_clear(dst); + return NULL; +} + +enum ast_format_id ast_format_id_from_old_bitfield(uint64_t src) +{ + struct ast_format dst; + if (ast_format_from_old_bitfield(&dst, src)) { + return dst.id; + } + return 0; +} + +int ast_format_attr_init() +{ + if (ast_rwlock_init(&ilock)) { + return -1; + } + if (!(interfaces = ao2_container_alloc(283, interface_hash_cb, interface_cmp_cb))) { + ast_rwlock_destroy(&ilock); + return -1; + } + return 0; +} + +int ast_format_attr_reg_interface(const struct ast_format_attr_interface *interface) +{ + struct interface_ao2_wrapper *wrapper; + struct interface_ao2_wrapper tmp_wrapper = { + .id = interface->id, + }; + + /* check for duplicates first*/ + ast_rwlock_wrlock(&ilock); + if ((wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_NOLOCK)))) { + ast_rwlock_unlock(&ilock); + ast_log(LOG_WARNING, "Can not register attribute interface for format id %d, interface already exists.\n", interface->id); + ao2_ref(wrapper, -1); + return -1; + } + ast_rwlock_unlock(&ilock); + + if (!(wrapper = ao2_alloc(sizeof(*wrapper), interface_destroy_cb))) { + return -1; + } + + wrapper->interface = interface; + wrapper->id = interface->id; + ast_rwlock_init(&wrapper->wraplock); + + /* use the write lock whenever the interface container is modified */ + ast_rwlock_wrlock(&ilock); + ao2_link(interfaces, wrapper); + ast_rwlock_unlock(&ilock); + + ao2_ref(wrapper, -1); + + return 0; +} + +int ast_format_attr_unreg_interface(const struct ast_format_attr_interface *interface) +{ + struct interface_ao2_wrapper *wrapper; + struct interface_ao2_wrapper tmp_wrapper = { + .id = interface->id, + }; + + /* use the write lock whenever the interface container is modified */ + ast_rwlock_wrlock(&ilock); + if (!(wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_UNLINK | OBJ_NOLOCK)))) { + ast_rwlock_unlock(&ilock); + return -1; + } + ast_rwlock_unlock(&ilock); + + ast_rwlock_wrlock(&wrapper->wraplock); + wrapper->interface = NULL; + ast_rwlock_unlock(&wrapper->wraplock); + + ao2_ref(wrapper, -1); + + return 0; +} diff --git a/main/format_cap.c b/main/format_cap.c new file mode 100644 index 000000000..1d566050f --- /dev/null +++ b/main/format_cap.c @@ -0,0 +1,545 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2010, Digium, Inc. + * + * David Vossel <dvossel@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Format Capability API + * + * \author David Vossel <dvossel@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); + +#include "asterisk/_private.h" +#include "asterisk/version.h" +#include "asterisk/format.h" +#include "asterisk/format_cap.h" +#include "asterisk/frame.h" +#include "asterisk/astobj2.h" +#include "asterisk/utils.h" + + +struct ast_format_cap { + /* The capabilities structure is just an ao2 container of ast_formats */ + struct ao2_container *formats; + struct ao2_iterator it; + int nolock; +}; + +/*! format exists within capabilities structure if it is identical to + * another format, or if the format is a proper subset of another format. */ +static int cmp_cb(void *obj, void *arg, int flags) +{ + struct ast_format *format1 = arg; + struct ast_format *format2 = obj; + enum ast_format_cmp_res res = ast_format_cmp(format1, format2); + + return ((res == AST_FORMAT_CMP_EQUAL) || + (res == AST_FORMAT_CMP_SUBSET)) ? + CMP_MATCH | CMP_STOP : + 0; +} + +static int hash_cb(const void *obj, const int flags) +{ + const struct ast_format *format = obj; + return format->id; +} + +static struct ast_format_cap *cap_alloc_helper(int nolock) +{ + struct ast_format_cap *cap = ast_calloc(1, sizeof(*cap)); + + if (!cap) { + return NULL; + } + cap->nolock = nolock ? OBJ_NOLOCK : 0; + if (!(cap->formats = ao2_container_alloc(283, hash_cb, cmp_cb))) { + ast_free(cap); + return NULL; + } + + return cap; +} + +struct ast_format_cap *ast_format_cap_alloc_nolock(void) +{ + return cap_alloc_helper(1); +} + +struct ast_format_cap *ast_format_cap_alloc(void) +{ + return cap_alloc_helper(0); +} + +void *ast_format_cap_destroy(struct ast_format_cap *cap) +{ + if (!cap) { + return NULL; + } + ao2_ref(cap->formats, -1); + ast_free(cap); + return NULL; +} + +void ast_format_cap_add(struct ast_format_cap *cap, struct ast_format *format) +{ + struct ast_format *fnew; + + if (!format || !format->id) { + return; + } + if (!(fnew = ao2_alloc(sizeof(struct ast_format), NULL))) { + return; + } + ast_format_copy(fnew, format); + if (cap->nolock) { + ao2_link_nolock(cap->formats, fnew); + } else { + ao2_link(cap->formats, fnew); + } + ao2_ref(fnew, -1); +} + +void ast_format_cap_add_all_by_type(struct ast_format_cap *cap, enum ast_format_type type) +{ + int x; + size_t f_len = 0; + struct ast_format tmp_fmt; + const struct ast_format_list *f_list = ast_get_format_list(&f_len); + + for (x = 0; x < f_len; x++) { + if (AST_FORMAT_GET_TYPE(f_list[x].id) == type) { + ast_format_cap_add(cap, ast_format_set(&tmp_fmt, f_list[x].id, 0)); + } + } +} + +void ast_format_cap_add_all(struct ast_format_cap *cap) +{ + int x; + size_t f_len = 0; + struct ast_format tmp_fmt; + const struct ast_format_list *f_list = ast_get_format_list(&f_len); + + for (x = 0; x < f_len; x++) { + ast_format_cap_add(cap, ast_format_set(&tmp_fmt, f_list[x].id, 0)); + } +} + +static int append_cb(void *obj, void *arg, int flag) +{ + struct ast_format_cap *result = (struct ast_format_cap *) arg; + struct ast_format *format = (struct ast_format *) obj; + + if (!ast_format_cap_iscompatible(result, format)) { + ast_format_cap_add(result, format); + } + + return 0; +} + +void ast_format_cap_append(struct ast_format_cap *dst, const struct ast_format_cap *src) +{ + ao2_callback(src->formats, OBJ_NODATA | src->nolock, append_cb, dst); +} + +static int copy_cb(void *obj, void *arg, int flag) +{ + struct ast_format_cap *result = (struct ast_format_cap *) arg; + struct ast_format *format = (struct ast_format *) obj; + + ast_format_cap_add(result, format); + return 0; +} + +void ast_format_cap_copy(struct ast_format_cap *dst, const struct ast_format_cap *src) +{ + ast_format_cap_remove_all(dst); + ao2_callback(src->formats, OBJ_NODATA | src->nolock, copy_cb, dst); +} + +struct ast_format_cap *ast_format_cap_dup(const struct ast_format_cap *cap) +{ + struct ast_format_cap *dst; + if (cap->nolock) { + dst = ast_format_cap_alloc_nolock(); + } else { + dst = ast_format_cap_alloc(); + } + if (!dst) { + return NULL; + } + ao2_callback(cap->formats, OBJ_NODATA | cap->nolock, copy_cb, dst); + return dst; +} + +int ast_format_cap_is_empty(const struct ast_format_cap *cap) +{ + if (!cap) { + return 1; + } + return ao2_container_count(cap->formats) == 0 ? 1 : 0; +} + +static int find_exact_cb(void *obj, void *arg, int flag) +{ + struct ast_format *format1 = (struct ast_format *) arg; + struct ast_format *format2 = (struct ast_format *) obj; + + return (ast_format_cmp(format1, format2) == AST_FORMAT_CMP_EQUAL) ? CMP_MATCH : 0; +} + +int ast_format_cap_remove(struct ast_format_cap *cap, struct ast_format *format) +{ + struct ast_format *fremove; + fremove = ao2_callback(cap->formats, OBJ_POINTER | OBJ_UNLINK | cap->nolock, find_exact_cb, format); + + if (fremove) { + ao2_ref(fremove, -1); + return 0; + } + + return -1; +} + +struct multiple_by_id_data { + struct ast_format *format; + int match_found; +}; + +static int multiple_by_id_cb(void *obj, void *arg, int flag) +{ + struct multiple_by_id_data *data = arg; + struct ast_format *format = obj; + int res; + + res = (format->id == data->format->id) ? CMP_MATCH : 0; + if (res) { + data->match_found = 1; + } + + return res; +} + +int ast_format_cap_remove_byid(struct ast_format_cap *cap, enum ast_format_id id) +{ + struct ast_format format = { + .id = id, + }; + struct multiple_by_id_data data = { + .format = &format, + .match_found = 0, + }; + + ao2_callback(cap->formats, + OBJ_NODATA | cap->nolock | OBJ_MULTIPLE | OBJ_UNLINK, + multiple_by_id_cb, + &data); + + /* match_found will be set if at least one item was removed */ + if (data.match_found) { + return 0; + } + + return -1; +} + +static int multiple_by_type_cb(void *obj, void *arg, int flag) +{ + int *type = arg; + struct ast_format *format = obj; + return ((AST_FORMAT_GET_TYPE(format->id)) == *type) ? CMP_MATCH : 0; +} + +void ast_format_cap_remove_bytype(struct ast_format_cap *cap, enum ast_format_type type) +{ + ao2_callback(cap->formats, + OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE | cap->nolock, + multiple_by_type_cb, + &type); +} + +void ast_format_cap_remove_all(struct ast_format_cap *cap) +{ + ao2_callback(cap->formats, OBJ_NODATA | cap->nolock | OBJ_MULTIPLE | OBJ_UNLINK, NULL, NULL); +} + +void ast_format_cap_set(struct ast_format_cap *cap, struct ast_format *format) +{ + ast_format_cap_remove_all(cap); + ast_format_cap_add(cap, format); +} + +int ast_format_cap_iscompatible(const struct ast_format_cap *cap, const struct ast_format *format) +{ + struct ast_format *f; + struct ast_format_cap *tmp_cap = (struct ast_format_cap *) cap; + f = ao2_find(tmp_cap->formats, (struct ast_format *) format, OBJ_POINTER | tmp_cap->nolock); + + if (f) { + ao2_ref(f, -1); + return 1; + } + + return 0; +} + +/*! \internal + * \brief this struct is just used for the ast_format_cap_joint function so we can provide + * both a format and a result ast_format_cap structure as arguments to the find_joint_cb + * ao2 callback function. + */ +struct find_joint_data { + /*! format to compare to for joint capabilities */ + struct ast_format *format; + /*! if joint formmat exists with above format, add it to the result container */ + struct ast_format_cap *joint_cap; + int joint_found; +}; + +static int find_joint_cb(void *obj, void *arg, int flag) +{ + struct ast_format *format = obj; + struct find_joint_data *data = arg; + + struct ast_format tmp = { 0, }; + if (!ast_format_joint(format, data->format, &tmp)) { + if (data->joint_cap) { + ast_format_cap_add(data->joint_cap, &tmp); + } + data->joint_found++; + } + + return 0; +} + +int ast_format_cap_has_joint(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2) +{ + struct ao2_iterator it; + struct ast_format *tmp; + struct find_joint_data data = { + .joint_found = 0, + .joint_cap = NULL, + }; + + it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0); + while ((tmp = ao2_iterator_next(&it))) { + data.format = tmp; + ao2_callback(cap2->formats, + OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock, + find_joint_cb, + &data); + ao2_ref(tmp, -1); + } + ao2_iterator_destroy(&it); + + return data.joint_found ? 1 : 0; +} + +int ast_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2) +{ + struct ao2_iterator it; + struct ast_format *tmp; + + if (ao2_container_count(cap1->formats) != ao2_container_count(cap2->formats)) { + return 0; /* if they are not the same size, they are not identical */ + } + + it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0); + while ((tmp = ao2_iterator_next(&it))) { + if (!ast_format_cap_iscompatible(cap2, tmp)) { + ao2_ref(tmp, -1); + ao2_iterator_destroy(&it); + return 0; + } + ao2_ref(tmp, -1); + } + ao2_iterator_destroy(&it); + + return 1; +} + +struct ast_format_cap *ast_format_cap_joint(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2) +{ + struct ao2_iterator it; + struct ast_format_cap *result = ast_format_cap_alloc_nolock(); + struct ast_format *tmp; + struct find_joint_data data = { + .joint_found = 0, + .joint_cap = result, + }; + if (!result) { + return NULL; + } + + it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0); + while ((tmp = ao2_iterator_next(&it))) { + data.format = tmp; + ao2_callback(cap2->formats, + OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock, + find_joint_cb, + &data); + ao2_ref(tmp, -1); + } + ao2_iterator_destroy(&it); + + if (ao2_container_count(result->formats)) { + return result; + } + + result = ast_format_cap_destroy(result); + return NULL; +} + +int ast_format_cap_joint_copy(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result) +{ + struct ao2_iterator it; + struct ast_format *tmp; + struct find_joint_data data = { + .joint_cap = result, + .joint_found = 0, + }; + + ast_format_cap_remove_all(result); + it = ao2_iterator_init(cap1->formats, cap2->nolock ? AO2_ITERATOR_DONTLOCK : 0); + while ((tmp = ao2_iterator_next(&it))) { + data.format = tmp; + ao2_callback(cap2->formats, + OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock, + find_joint_cb, + &data); + ao2_ref(tmp, -1); + } + ao2_iterator_destroy(&it); + + return ao2_container_count(result->formats) ? 1 : 0; +} + +struct ast_format_cap *ast_format_cap_get_type(const struct ast_format_cap *cap, enum ast_format_type ftype) +{ + struct ao2_iterator it; + struct ast_format_cap *result = ast_format_cap_alloc_nolock(); + struct ast_format *tmp; + + if (!result) { + return NULL; + } + + /* for each format in cap1, see if that format is + * compatible with cap2. If so copy it to the result */ + it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0); + while ((tmp = ao2_iterator_next(&it))) { + if (AST_FORMAT_GET_TYPE(tmp->id) == ftype) { + /* copy format */ + ast_format_cap_add(result, tmp); + } + ao2_ref(tmp, -1); + } + ao2_iterator_destroy(&it); + + if (ao2_container_count(result->formats)) { + return result; + } + result = ast_format_cap_destroy(result); + + /* Remember to always free the NULL before returning it. */ + ast_free(NULL); + return NULL; +} + + +int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_format_type type) +{ + struct ao2_iterator it; + struct ast_format *tmp; + + it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0); + while ((tmp = ao2_iterator_next(&it))) { + if (AST_FORMAT_GET_TYPE(tmp->id) == type) { + ao2_ref(tmp, -1); + ao2_iterator_destroy(&it); + return 1; + } + ao2_ref(tmp, -1); + } + ao2_iterator_destroy(&it); + + return 0; +} + +void ast_format_cap_iter_start(struct ast_format_cap *cap) +{ + if (!cap->nolock) { + ao2_lock(cap->formats); + } + cap->it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0); +} + +void ast_format_cap_iter_end(struct ast_format_cap *cap) +{ + ao2_iterator_destroy(&cap->it); + if (!cap->nolock) { + ao2_unlock(cap->formats); + } +} + +int ast_format_cap_iter_next(struct ast_format_cap *cap, struct ast_format *format) +{ + struct ast_format *tmp = ao2_iterator_next(&cap->it); + + if (!tmp) { + return -1; + } + ast_format_copy(format, tmp); + ao2_ref(tmp, -1); + + return 0; +} + +uint64_t ast_format_cap_to_old_bitfield(const struct ast_format_cap *cap) +{ + uint64_t res = 0; + struct ao2_iterator it; + struct ast_format *tmp; + + it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0); + while ((tmp = ao2_iterator_next(&it))) { + res |= ast_format_to_old_bitfield(tmp); + ao2_ref(tmp, -1); + } + ao2_iterator_destroy(&it); + return res; +} + +void ast_format_cap_from_old_bitfield(struct ast_format_cap *dst, uint64_t src) +{ + uint64_t tmp = 0; + int x; + struct ast_format tmp_format = { 0, }; + + ast_format_cap_remove_all(dst); + for (x = 0; x < 64; x++) { + tmp = (1ULL << x); + if (tmp & src) { + ast_format_cap_add(dst, ast_format_from_old_bitfield(&tmp_format, tmp)); + } + } +} diff --git a/main/format_pref.c b/main/format_pref.c new file mode 100644 index 000000000..26801b648 --- /dev/null +++ b/main/format_pref.c @@ -0,0 +1,320 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2010, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Format Preference API + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); + +#include "asterisk/_private.h" +#include "asterisk/version.h" +#include "asterisk/frame.h" +#include "asterisk/channel.h" +#include "asterisk/utils.h" + +void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, int right) +{ + size_t f_len; + const struct ast_format_list *f_list = ast_get_format_list(&f_len); + int x, differential = (int) 'A', mem; + char *from, *to; + + /* TODO re-evaluate this function. It is using the order of the formats specified + * in the global format list in a way that may not be safe. */ + if (right) { + from = pref->order; + to = buf; + mem = size; + } else { + to = pref->order; + from = buf; + mem = AST_CODEC_PREF_SIZE; + } + + memset(to, 0, mem); + for (x = 0; x < AST_CODEC_PREF_SIZE; x++) { + if (!from[x]) { + break; + } + to[x] = right ? (from[x] + differential) : (from[x] - differential); + if (!right && to[x] && (to[x] < f_len)) { + ast_format_set(&pref->formats[x], f_list[to[x]-1].id , 0); + } + } +} + +int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size) +{ + int x; + struct ast_format format; + size_t total_len, slen; + char *formatname; + + memset(buf, 0, size); + total_len = size; + buf[0] = '('; + total_len--; + for (x = 0; x < AST_CODEC_PREF_SIZE; x++) { + if (total_len <= 0) + break; + if (!(ast_codec_pref_index(pref, x, &format))) + break; + if ((formatname = ast_getformatname(&format))) { + slen = strlen(formatname); + if (slen > total_len) + break; + strncat(buf, formatname, total_len - 1); /* safe */ + total_len -= slen; + } + if (total_len && x < AST_CODEC_PREF_SIZE - 1 && ast_codec_pref_index(pref, x + 1, &format)) { + strncat(buf, "|", total_len - 1); /* safe */ + total_len--; + } + } + if (total_len) { + strncat(buf, ")", total_len - 1); /* safe */ + total_len--; + } + + return size - total_len; +} + +struct ast_format *ast_codec_pref_index(struct ast_codec_pref *pref, int idx, struct ast_format *result) +{ + if ((idx >= 0) && (idx < sizeof(pref->order)) && pref->formats[idx].id) { + ast_format_copy(result, &pref->formats[idx]); + } else { + ast_format_clear(result); + return NULL; + } + + return result; +} + +/*! \brief Remove codec from pref list */ +void ast_codec_pref_remove(struct ast_codec_pref *pref, struct ast_format *format) +{ + struct ast_codec_pref oldorder; + int x, y = 0; + size_t f_len = 0; + const struct ast_format_list *f_list = ast_get_format_list(&f_len); + + if (!pref->order[0]) + return; + + memcpy(&oldorder, pref, sizeof(oldorder)); + memset(pref, 0, sizeof(*pref)); + + for (x = 0; x < f_len; x++) { + if (!oldorder.order[x]) + break; + if (f_list[oldorder.order[x]-1].id != format->id) { + pref->order[y] = oldorder.order[x]; + ast_format_copy(&pref->formats[y], &oldorder.formats[x]); + pref->framing[y++] = oldorder.framing[x]; + } + } +} + +/*! \brief Append codec to list */ +int ast_codec_pref_append(struct ast_codec_pref *pref, struct ast_format *format) +{ + int x, newindex = 0; + size_t f_len = 0; + const struct ast_format_list *f_list = ast_get_format_list(&f_len); + + ast_codec_pref_remove(pref, format); + + for (x = 0; x < f_len; x++) { + if (f_list[x].id == format->id) { + newindex = x + 1; + break; + } + } + + if (newindex) { + for (x = 0; x < f_len; x++) { + if (!pref->order[x]) { + pref->order[x] = newindex; + ast_format_copy(&pref->formats[x], format); + break; + } + } + } + + return x; +} + +/*! \brief Prepend codec to list */ +void ast_codec_pref_prepend(struct ast_codec_pref *pref, struct ast_format *format, int only_if_existing) +{ + int x, newindex = 0; + size_t f_len = 0; + const struct ast_format_list *f_list = ast_get_format_list(&f_len); + + /* First step is to get the codecs "index number" */ + for (x = 0; x < f_len; x++) { + if (f_list[x].id == format->id) { + newindex = x + 1; + break; + } + } + /* Done if its unknown */ + if (!newindex) + return; + + /* Now find any existing occurrence, or the end */ + for (x = 0; x < AST_CODEC_PREF_SIZE; x++) { + if (!pref->order[x] || pref->order[x] == newindex) + break; + } + + if (only_if_existing && !pref->order[x]) + return; + + /* Move down to make space to insert - either all the way to the end, + or as far as the existing location (which will be overwritten) */ + for (; x > 0; x--) { + pref->order[x] = pref->order[x - 1]; + pref->framing[x] = pref->framing[x - 1]; + ast_format_copy(&pref->formats[x], &pref->formats[x - 1]); + } + + /* And insert the new entry */ + pref->order[0] = newindex; + pref->framing[0] = 0; /* ? */ + ast_format_copy(&pref->formats[0], format); +} + +/*! \brief Set packet size for codec */ +int ast_codec_pref_setsize(struct ast_codec_pref *pref, struct ast_format *format, int framems) +{ + int x, idx = -1; + size_t f_len = 0; + const struct ast_format_list *f_list = ast_get_format_list(&f_len); + + for (x = 0; x < f_len; x++) { + if (f_list[x].id == format->id) { + idx = x; + break; + } + } + + if (idx < 0) + return -1; + + /* size validation */ + if (!framems) + framems = f_list[idx].def_ms; + + if (f_list[idx].inc_ms && framems % f_list[idx].inc_ms) /* avoid division by zero */ + framems -= framems % f_list[idx].inc_ms; + + if (framems < f_list[idx].min_ms) + framems = f_list[idx].min_ms; + + if (framems > f_list[idx].max_ms) + framems = f_list[idx].max_ms; + + for (x = 0; x < f_len; x++) { + if (pref->order[x] == (idx + 1)) { + pref->framing[x] = framems; + break; + } + } + + return x; +} + +/*! \brief Get packet size for codec */ +struct ast_format_list ast_codec_pref_getsize(struct ast_codec_pref *pref, struct ast_format *format) +{ + int x, idx = -1, framems = 0; + struct ast_format_list fmt = { 0, }; + size_t f_len = 0; + const struct ast_format_list *f_list = ast_get_format_list(&f_len); + + for (x = 0; x < f_len; x++) { + if (f_list[x].id == format->id) { + fmt = f_list[x]; + idx = x; + break; + } + } + + for (x = 0; x < f_len; x++) { + if (pref->order[x] == (idx + 1)) { + framems = pref->framing[x]; + break; + } + } + + /* size validation */ + if (!framems) + framems = f_list[idx].def_ms; + + if (f_list[idx].inc_ms && framems % f_list[idx].inc_ms) /* avoid division by zero */ + framems -= framems % f_list[idx].inc_ms; + + if (framems < f_list[idx].min_ms) + framems = f_list[idx].min_ms; + + if (framems > f_list[idx].max_ms) + framems = f_list[idx].max_ms; + + fmt.cur_ms = framems; + + return fmt; +} + +/*! \brief Pick a codec */ +struct ast_format *ast_codec_choose(struct ast_codec_pref *pref, struct ast_format_cap *cap, int find_best, struct ast_format *result) +{ + int x, slot, found; + size_t f_len = 0; + struct ast_format tmp_fmt; + + const struct ast_format_list *f_list = ast_get_format_list(&f_len); + + ast_format_clear(result); + + for (x = 0; x < f_len; x++) { + slot = pref->order[x]; + + if (!slot) + break; + if (ast_format_cap_iscompatible(cap, ast_format_set(&tmp_fmt, f_list[slot-1].id, 0))) { + found = 1; /*format is found and stored in tmp_fmt */ + break; + } + } + if (found && (AST_FORMAT_GET_TYPE(tmp_fmt.id) == AST_FORMAT_TYPE_AUDIO)) { + ast_format_copy(result, &tmp_fmt); + return result; + } + + ast_debug(4, "Could not find preferred codec - %s\n", find_best ? "Going for the best codec" : "Returning zero codec"); + + return find_best ? ast_best_codec(cap, result) : NULL; +} + + diff --git a/main/frame.c b/main/frame.c index 9f599a991..840fa4b75 100644 --- a/main/frame.c +++ b/main/frame.c @@ -80,7 +80,7 @@ enum frame_type { struct ast_smoother { int size; - format_t format; + struct ast_format format; int flags; float samplesperbyte; unsigned int opt_needs_swap:1; @@ -207,12 +207,12 @@ int __ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f, int swap) ast_log(LOG_WARNING, "Huh? Can't smooth a non-voice frame!\n"); return -1; } - if (!s->format) { - s->format = f->subclass.codec; + if (!s->format.id) { + ast_format_copy(&s->format, &f->subclass.format); s->samplesperbyte = (float)f->samples / (float)f->datalen; - } else if (s->format != f->subclass.codec) { + } else if (ast_format_cmp(&s->format, &f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) { ast_log(LOG_WARNING, "Smoother was working on %s format frames, now trying to feed %s?\n", - ast_getformatname(s->format), ast_getformatname(f->subclass.codec)); + ast_getformatname(&s->format), ast_getformatname(&f->subclass.format)); return -1; } if (s->len + f->datalen > SMOOTHER_SIZE) { @@ -263,7 +263,7 @@ struct ast_frame *ast_smoother_read(struct ast_smoother *s) len = s->len; /* Make frame */ s->f.frametype = AST_FRAME_VOICE; - s->f.subclass.codec = s->format; + ast_format_copy(&s->f.subclass.format, &s->format); s->f.data.ptr = s->framedata + AST_FRIENDLY_OFFSET; s->f.offset = AST_FRIENDLY_OFFSET; s->f.datalen = len; @@ -280,7 +280,7 @@ struct ast_frame *ast_smoother_read(struct ast_smoother *s) memmove(s->data, s->data + len, s->len); if (!ast_tvzero(s->delivery)) { /* If we have delivery time, increment it, otherwise, leave it at 0 */ - s->delivery = ast_tvadd(s->delivery, ast_samp2tv(s->f.samples, ast_format_rate(s->format))); + s->delivery = ast_tvadd(s->delivery, ast_samp2tv(s->f.samples, ast_format_rate(&s->format))); } } /* Return frame */ @@ -408,7 +408,7 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr) return NULL; } out->frametype = fr->frametype; - out->subclass.codec = fr->subclass.codec; + ast_format_copy(&out->subclass.format, &fr->subclass.format); out->datalen = fr->datalen; out->samples = fr->samples; out->offset = fr->offset; @@ -515,7 +515,7 @@ struct ast_frame *ast_frdup(const struct ast_frame *f) } out->frametype = f->frametype; - out->subclass.codec = f->subclass.codec; + ast_format_copy(&out->subclass.format, &f->subclass.format); out->datalen = f->datalen; out->samples = f->samples; out->delivery = f->delivery; @@ -566,12 +566,12 @@ const struct ast_format_list *ast_get_format_list(size_t *size) return AST_FORMAT_LIST; } -char* ast_getformatname(format_t format) +char* ast_getformatname(struct ast_format *format) { int x; char *ret = "unknown"; for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].bits == format) { + if (AST_FORMAT_LIST[x].id == format->id) { ret = AST_FORMAT_LIST[x].name; break; } @@ -579,21 +579,23 @@ char* ast_getformatname(format_t format) return ret; } -char *ast_getformatname_multiple(char *buf, size_t size, format_t format) +char *ast_getformatname_multiple(char *buf, size_t size, struct ast_format_cap *cap) { int x; unsigned len; char *start, *end = buf; + struct ast_format tmp_fmt; if (!size) return buf; - snprintf(end, size, "0x%llx (", (unsigned long long) format); + snprintf(end, size, "("); len = strlen(end); end += len; size -= len; start = end; for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].bits & format) { + ast_format_set(&tmp_fmt, AST_FORMAT_LIST[x].id, 0); + if (ast_format_cap_iscompatible(cap, &tmp_fmt)) { snprintf(end, size, "%s|", AST_FORMAT_LIST[x].name); len = strlen(end); end += len; @@ -629,31 +631,28 @@ static const char *ast_expand_codec_alias(const char *in) return in; } -format_t ast_getformatbyname(const char *name) +struct ast_format *ast_getformatbyname(const char *name, struct ast_format *result) { - int x, all; - format_t format = 0; + int x; - all = strcasecmp(name, "all") ? 0 : 1; for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (all || - !strcasecmp(AST_FORMAT_LIST[x].name,name) || - !strcasecmp(AST_FORMAT_LIST[x].name, ast_expand_codec_alias(name))) { - format |= AST_FORMAT_LIST[x].bits; - if (!all) - break; + if (!strcasecmp(AST_FORMAT_LIST[x].name,name) || + !strcasecmp(AST_FORMAT_LIST[x].name, ast_expand_codec_alias(name))) { + + ast_format_set(result, AST_FORMAT_LIST[x].id, 0); + return result; } } - return format; + return NULL; } -char *ast_codec2str(format_t codec) +char *ast_codec2str(struct ast_format *format) { int x; char *ret = "unknown"; for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].bits == codec) { + if (AST_FORMAT_LIST[x].id == format->id) { ret = AST_FORMAT_LIST[x].desc; break; } @@ -663,8 +662,7 @@ char *ast_codec2str(format_t codec) static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - int i, found=0; - char hex[25]; + int x, found=0; switch (cmd) { case CLI_INIT: @@ -684,25 +682,25 @@ static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args * ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n" "\tIt does not indicate anything about your configuration.\n"); - ast_cli(a->fd, "%19s %9s %20s TYPE %8s %s\n","INT","BINARY","HEX","NAME","DESCRIPTION"); + ast_cli(a->fd, "%19s %8s %8s %s\n","ID","TYPE","NAME","DESCRIPTION"); ast_cli(a->fd, "-----------------------------------------------------------------------------------\n"); - for (i = 0; i < 63; i++) { + for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { if (a->argc == 4) { if (!strcasecmp(a->argv[3], "audio")) { - if (!((1LL << i) & AST_FORMAT_AUDIO_MASK)) { + if (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) != AST_FORMAT_TYPE_AUDIO) { continue; } } else if (!strcasecmp(a->argv[3], "video")) { - if (!((1LL << i) & AST_FORMAT_VIDEO_MASK)) { + if (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) != AST_FORMAT_TYPE_VIDEO) { continue; } } else if (!strcasecmp(a->argv[3], "image")) { - if (i != 16 && i != 17) { + if (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) != AST_FORMAT_TYPE_IMAGE) { continue; } } else if (!strcasecmp(a->argv[3], "text")) { - if (!((1LL << i) & AST_FORMAT_TEXT_MASK)) { + if (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) != AST_FORMAT_TYPE_TEXT) { continue; } } else { @@ -710,14 +708,15 @@ static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args * } } - snprintf(hex, sizeof(hex), "(0x%llx)", 1LL << i); - ast_cli(a->fd, "%19llu (1 << %2d) %20s %5s %8s (%s)\n", 1LL << i, i, hex, - ((1LL << i) & AST_FORMAT_AUDIO_MASK) ? "audio" : - i == 16 || i == 17 ? "image" : - ((1LL << i) & AST_FORMAT_VIDEO_MASK) ? "video" : - ((1LL << i) & AST_FORMAT_TEXT_MASK) ? "text" : + ast_cli(a->fd, "%19u %5s %8s (%s)\n", + AST_FORMAT_LIST[x].id, + (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) == AST_FORMAT_TYPE_AUDIO) ? "audio" : + (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) == AST_FORMAT_TYPE_TEXT) ? "image" : + (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) == AST_FORMAT_TYPE_VIDEO) ? "video" : + (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) == AST_FORMAT_TYPE_TEXT) ? "text" : "(unk)", - ast_getformatname(1LL << i), ast_codec2str(1LL << i)); + AST_FORMAT_LIST[x].name, + AST_FORMAT_LIST[x].desc); found = 1; } @@ -730,9 +729,9 @@ static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args * static char *show_codec_n(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - format_t codec; - int i, found = 0; - long long type_punned_codec; + enum ast_format_id format_id; + int x, found = 0; + int type_punned_codec; switch (cmd) { case CLI_INIT: @@ -748,19 +747,21 @@ static char *show_codec_n(struct ast_cli_entry *e, int cmd, struct ast_cli_args if (a->argc != 4) return CLI_SHOWUSAGE; - if (sscanf(a->argv[3], "%30lld", &type_punned_codec) != 1) { + if (sscanf(a->argv[3], "%30d", &type_punned_codec) != 1) { return CLI_SHOWUSAGE; } - codec = type_punned_codec; + format_id = type_punned_codec; - for (i = 0; i < 63; i++) - if (codec & (1LL << i)) { + for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { + if (AST_FORMAT_LIST[x].id == format_id) { found = 1; - ast_cli(a->fd, "%11llu (1 << %2d) %s\n", 1LL << i, i, ast_codec2str(1LL << i)); + ast_cli(a->fd, "%11u %s\n", (unsigned int) format_id, AST_FORMAT_LIST[x].desc); + break; } + } if (!found) - ast_cli(a->fd, "Codec %lld not found\n", (long long) codec); + ast_cli(a->fd, "Codec %d not found\n", format_id); return CLI_SUCCESS; } @@ -896,7 +897,7 @@ void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix) break; case AST_FRAME_IMAGE: strcpy(ftype, "Image"); - snprintf(subclass, sizeof(subclass), "Image format %s\n", ast_getformatname(f->subclass.codec)); + snprintf(subclass, sizeof(subclass), "Image format %s\n", ast_getformatname(&f->subclass.format)); break; case AST_FRAME_HTML: strcpy(ftype, "HTML"); @@ -984,271 +985,11 @@ int init_framer(void) return 0; } -void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, int right) -{ - int x, differential = (int) 'A', mem; - char *from, *to; - - if (right) { - from = pref->order; - to = buf; - mem = size; - } else { - to = pref->order; - from = buf; - mem = sizeof(format_t) * 8; - } - - memset(to, 0, mem); - for (x = 0; x < sizeof(format_t) * 8; x++) { - if (!from[x]) - break; - to[x] = right ? (from[x] + differential) : (from[x] - differential); - } -} - -int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size) -{ - int x; - format_t codec; - size_t total_len, slen; - char *formatname; - - memset(buf, 0, size); - total_len = size; - buf[0] = '('; - total_len--; - for (x = 0; x < sizeof(format_t) * 8; x++) { - if (total_len <= 0) - break; - if (!(codec = ast_codec_pref_index(pref,x))) - break; - if ((formatname = ast_getformatname(codec))) { - slen = strlen(formatname); - if (slen > total_len) - break; - strncat(buf, formatname, total_len - 1); /* safe */ - total_len -= slen; - } - if (total_len && x < sizeof(format_t) * 8 - 1 && ast_codec_pref_index(pref, x + 1)) { - strncat(buf, "|", total_len - 1); /* safe */ - total_len--; - } - } - if (total_len) { - strncat(buf, ")", total_len - 1); /* safe */ - total_len--; - } - - return size - total_len; -} - -format_t ast_codec_pref_index(struct ast_codec_pref *pref, int idx) -{ - int slot = 0; - - if ((idx >= 0) && (idx < sizeof(pref->order))) { - slot = pref->order[idx]; - } - - return slot ? AST_FORMAT_LIST[slot - 1].bits : 0; -} - -/*! \brief Remove codec from pref list */ -void ast_codec_pref_remove(struct ast_codec_pref *pref, format_t format) -{ - struct ast_codec_pref oldorder; - int x, y = 0; - int slot; - int size; - - if (!pref->order[0]) - return; - - memcpy(&oldorder, pref, sizeof(oldorder)); - memset(pref, 0, sizeof(*pref)); - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - slot = oldorder.order[x]; - size = oldorder.framing[x]; - if (! slot) - break; - if (AST_FORMAT_LIST[slot-1].bits != format) { - pref->order[y] = slot; - pref->framing[y++] = size; - } - } -} - -/*! \brief Append codec to list */ -int ast_codec_pref_append(struct ast_codec_pref *pref, format_t format) -{ - int x, newindex = 0; - - ast_codec_pref_remove(pref, format); - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].bits == format) { - newindex = x + 1; - break; - } - } - - if (newindex) { - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (!pref->order[x]) { - pref->order[x] = newindex; - break; - } - } - } - - return x; -} - -/*! \brief Prepend codec to list */ -void ast_codec_pref_prepend(struct ast_codec_pref *pref, format_t format, int only_if_existing) +int ast_parse_allow_disallow(struct ast_codec_pref *pref, struct ast_format_cap *cap, const char *list, int allowing) { - int x, newindex = 0; - - /* First step is to get the codecs "index number" */ - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].bits == format) { - newindex = x + 1; - break; - } - } - /* Done if its unknown */ - if (!newindex) - return; - - /* Now find any existing occurrence, or the end */ - for (x = 0; x < sizeof(format_t) * 8; x++) { - if (!pref->order[x] || pref->order[x] == newindex) - break; - } - - if (only_if_existing && !pref->order[x]) - return; - - /* Move down to make space to insert - either all the way to the end, - or as far as the existing location (which will be overwritten) */ - for (; x > 0; x--) { - pref->order[x] = pref->order[x - 1]; - pref->framing[x] = pref->framing[x - 1]; - } - - /* And insert the new entry */ - pref->order[0] = newindex; - pref->framing[0] = 0; /* ? */ -} - -/*! \brief Set packet size for codec */ -int ast_codec_pref_setsize(struct ast_codec_pref *pref, format_t format, int framems) -{ - int x, idx = -1; - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].bits == format) { - idx = x; - break; - } - } - - if (idx < 0) - return -1; - - /* size validation */ - if (!framems) - framems = AST_FORMAT_LIST[idx].def_ms; - - if (AST_FORMAT_LIST[idx].inc_ms && framems % AST_FORMAT_LIST[idx].inc_ms) /* avoid division by zero */ - framems -= framems % AST_FORMAT_LIST[idx].inc_ms; - - if (framems < AST_FORMAT_LIST[idx].min_ms) - framems = AST_FORMAT_LIST[idx].min_ms; - - if (framems > AST_FORMAT_LIST[idx].max_ms) - framems = AST_FORMAT_LIST[idx].max_ms; - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (pref->order[x] == (idx + 1)) { - pref->framing[x] = framems; - break; - } - } - - return x; -} - -/*! \brief Get packet size for codec */ -struct ast_format_list ast_codec_pref_getsize(struct ast_codec_pref *pref, format_t format) -{ - int x, idx = -1, framems = 0; - struct ast_format_list fmt = { 0, }; - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].bits == format) { - fmt = AST_FORMAT_LIST[x]; - idx = x; - break; - } - } - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (pref->order[x] == (idx + 1)) { - framems = pref->framing[x]; - break; - } - } - - /* size validation */ - if (!framems) - framems = AST_FORMAT_LIST[idx].def_ms; - - if (AST_FORMAT_LIST[idx].inc_ms && framems % AST_FORMAT_LIST[idx].inc_ms) /* avoid division by zero */ - framems -= framems % AST_FORMAT_LIST[idx].inc_ms; - - if (framems < AST_FORMAT_LIST[idx].min_ms) - framems = AST_FORMAT_LIST[idx].min_ms; - - if (framems > AST_FORMAT_LIST[idx].max_ms) - framems = AST_FORMAT_LIST[idx].max_ms; - - fmt.cur_ms = framems; - - return fmt; -} - -/*! \brief Pick a codec */ -format_t ast_codec_choose(struct ast_codec_pref *pref, format_t formats, int find_best) -{ - int x, slot; - format_t ret = 0; - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - slot = pref->order[x]; - - if (!slot) - break; - if (formats & AST_FORMAT_LIST[slot-1].bits) { - ret = AST_FORMAT_LIST[slot-1].bits; - break; - } - } - if (ret & AST_FORMAT_AUDIO_MASK) - return ret; - - ast_debug(4, "Could not find preferred codec - %s\n", find_best ? "Going for the best codec" : "Returning zero codec"); - - return find_best ? ast_best_codec(formats) : 0; -} - -int ast_parse_allow_disallow(struct ast_codec_pref *pref, format_t *mask, const char *list, int allowing) -{ - int errors = 0, framems = 0; + int errors = 0, framems = 0, all = 0; char *parse = NULL, *this = NULL, *psize = NULL; - format_t format = 0; + struct ast_format format; parse = ast_strdupa(list); while ((this = strsep(&parse, ","))) { @@ -1263,30 +1004,38 @@ int ast_parse_allow_disallow(struct ast_codec_pref *pref, format_t *mask, const ast_log(LOG_WARNING, "Bad packetization value for codec %s\n", this); } } - if (!(format = ast_getformatbyname(this))) { + all = strcasecmp(this, "all") ? 0 : 1; + + if (!all && !ast_getformatbyname(this, &format)) { ast_log(LOG_WARNING, "Cannot %s unknown format '%s'\n", allowing ? "allow" : "disallow", this); errors++; continue; } - if (mask) { - if (allowing) - *mask |= format; - else - *mask &= ~format; + if (cap) { + if (allowing) { + if (all) { + ast_format_cap_add_all(cap); + } else { + ast_format_cap_add(cap, &format); + } + } else { + if (all) { + ast_format_cap_remove_all(cap); + } else { + ast_format_cap_remove(cap, &format); + } + } } - /* Set up a preference list for audio. Do not include video in preferences - since we can not transcode video and have to use whatever is offered - */ - if (pref && (format & AST_FORMAT_AUDIO_MASK)) { - if (strcasecmp(this, "all")) { + if (pref) { + if (!all) { if (allowing) { - ast_codec_pref_append(pref, format); - ast_codec_pref_setsize(pref, format, framems); + ast_codec_pref_append(pref, &format); + ast_codec_pref_setsize(pref, &format, framems); + } else { + ast_codec_pref_remove(pref, &format); } - else - ast_codec_pref_remove(pref, format); } else if (!allowing) { memset(pref, 0, sizeof(*pref)); } @@ -1445,9 +1194,8 @@ static int speex_samples(unsigned char *data, int len) int ast_codec_get_samples(struct ast_frame *f) { int samples = 0; - char tmp[64]; - switch (f->subclass.codec) { + switch (f->subclass.format.id) { case AST_FORMAT_SPEEX: samples = speex_samples(f->data.ptr, f->datalen); break; @@ -1499,17 +1247,17 @@ int ast_codec_get_samples(struct ast_frame *f) samples = (int) f->datalen * ((float) 48000 / 8000); break; default: - ast_log(LOG_WARNING, "Unable to calculate samples for format %s\n", ast_getformatname_multiple(tmp, sizeof(tmp), f->subclass.codec)); + ast_log(LOG_WARNING, "Unable to calculate samples for format %s\n", ast_getformatname(&f->subclass.format)); } return samples; } -int ast_codec_get_len(format_t format, int samples) +int ast_codec_get_len(struct ast_format *format, int samples) { int len = 0; /* XXX Still need speex, and lpc10 XXX */ - switch(format) { + switch(format->id) { case AST_FORMAT_G723_1: len = (samples / 240) * 20; break; @@ -1562,7 +1310,7 @@ int ast_frame_adjust_volume(struct ast_frame *f, int adjustment) short *fdata = f->data.ptr; short adjust_value = abs(adjustment); - if ((f->frametype != AST_FRAME_VOICE) || (f->subclass.codec != AST_FORMAT_SLINEAR)) + if ((f->frametype != AST_FRAME_VOICE) || (f->subclass.format.id != AST_FORMAT_SLINEAR)) return -1; if (!adjustment) @@ -1584,10 +1332,10 @@ int ast_frame_slinear_sum(struct ast_frame *f1, struct ast_frame *f2) int count; short *data1, *data2; - if ((f1->frametype != AST_FRAME_VOICE) || (f1->subclass.codec != AST_FORMAT_SLINEAR)) + if ((f1->frametype != AST_FRAME_VOICE) || (f1->subclass.format.id != AST_FORMAT_SLINEAR)) return -1; - if ((f2->frametype != AST_FRAME_VOICE) || (f2->subclass.codec != AST_FORMAT_SLINEAR)) + if ((f2->frametype != AST_FRAME_VOICE) || (f2->subclass.format.id != AST_FORMAT_SLINEAR)) return -1; if (f1->samples != f2->samples) diff --git a/main/image.c b/main/image.c index 5da07ce06..3aa226653 100644 --- a/main/image.c +++ b/main/image.c @@ -96,7 +96,7 @@ static void make_filename(char *buf, int len, const char *filename, const char * } } -struct ast_frame *ast_read_image(const char *filename, const char *preflang, int format) +struct ast_frame *ast_read_image(const char *filename, const char *preflang, struct ast_format *format) { struct ast_imager *i; char buf[256]; @@ -109,7 +109,8 @@ struct ast_frame *ast_read_image(const char *filename, const char *preflang, int AST_RWLIST_RDLOCK(&imagers); AST_RWLIST_TRAVERSE(&imagers, i, list) { - if (i->format & format) { + /* if NULL image format, just pick the first one, otherwise match it. */ + if (!format || (ast_format_cmp(&i->format, format) == AST_FORMAT_CMP_EQUAL)) { char *stringp=NULL; ast_copy_string(tmp, i->exts, sizeof(tmp)); stringp = tmp; @@ -157,7 +158,7 @@ int ast_send_image(struct ast_channel *chan, const char *filename) struct ast_frame *f; int res = -1; if (chan->tech->send_image) { - f = ast_read_image(filename, chan->language, -1); + f = ast_read_image(filename, chan->language, NULL); if (f) { res = chan->tech->send_image(chan, f); ast_frfree(f); @@ -189,7 +190,7 @@ static char *handle_core_show_image_formats(struct ast_cli_entry *e, int cmd, st ast_cli(a->fd, FORMAT, "----", "----------", "-----------", "------"); AST_RWLIST_RDLOCK(&imagers); AST_RWLIST_TRAVERSE(&imagers, i, list) { - ast_cli(a->fd, FORMAT2, i->name, i->exts, i->desc, ast_getformatname(i->format)); + ast_cli(a->fd, FORMAT2, i->name, i->exts, i->desc, ast_getformatname(&i->format)); count_fmt++; } AST_RWLIST_UNLOCK(&imagers); diff --git a/main/indications.c b/main/indications.c index dcd597ac7..f93239380 100644 --- a/main/indications.c +++ b/main/indications.c @@ -116,7 +116,7 @@ struct playtones_state { int npos; int oldnpos; int pos; - int origwfmt; + struct ast_format origwfmt; struct ast_frame f; unsigned char offset[AST_FRIENDLY_OFFSET]; short data[4000]; @@ -127,7 +127,7 @@ static void playtones_release(struct ast_channel *chan, void *params) struct playtones_state *ps = params; if (chan) { - ast_set_write_format(chan, ps->origwfmt); + ast_set_write_format(chan, &ps->origwfmt); } if (ps->items) { @@ -147,9 +147,9 @@ static void *playtones_alloc(struct ast_channel *chan, void *params) return NULL; } - ps->origwfmt = chan->writeformat; + ast_format_copy(&ps->origwfmt, &chan->writeformat); - if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) { + if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR)) { ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name); playtones_release(NULL, ps); ps = NULL; @@ -223,7 +223,7 @@ static int playtones_generator(struct ast_channel *chan, void *data, int len, in } ps->f.frametype = AST_FRAME_VOICE; - ps->f.subclass.codec = AST_FORMAT_SLINEAR; + ast_format_set(&ps->f.subclass.format, AST_FORMAT_SLINEAR, 0); ps->f.datalen = len; ps->f.samples = samples; ps->f.offset = AST_FRIENDLY_OFFSET; diff --git a/main/manager.c b/main/manager.c index 40804bf31..27fa6d8dd 100644 --- a/main/manager.c +++ b/main/manager.c @@ -3506,7 +3506,7 @@ struct fast_originate_helper { /*! data can contain a channel name, extension number, username, password, etc. */ char data[512]; int timeout; - format_t format; /*!< Codecs used for a call */ + struct ast_format_cap *cap; /*!< Codecs used for a call */ char app[AST_MAX_APP]; char appdata[AST_MAX_EXTENSION]; char cid_name[AST_MAX_EXTENSION]; @@ -3528,12 +3528,12 @@ static void *fast_originate(void *data) char requested_channel[AST_CHANNEL_NAME]; if (!ast_strlen_zero(in->app)) { - res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1, + res = ast_pbx_outgoing_app(in->tech, in->cap, in->data, in->timeout, in->app, in->appdata, &reason, 1, S_OR(in->cid_num, NULL), S_OR(in->cid_name, NULL), in->vars, in->account, &chan); } else { - res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, + res = ast_pbx_outgoing_exten(in->tech, in->cap, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, S_OR(in->cid_num, NULL), S_OR(in->cid_name, NULL), in->vars, in->account, &chan); @@ -3565,6 +3565,7 @@ static void *fast_originate(void *data) if (chan) { ast_channel_unlock(chan); } + in->cap = ast_format_cap_destroy(in->cap); ast_free(in); return NULL; } @@ -3822,29 +3823,39 @@ static int action_originate(struct mansession *s, const struct message *m) int reason = 0; char tmp[256]; char tmp2[256]; - format_t format = AST_FORMAT_SLINEAR; - + struct ast_format_cap *cap = ast_format_cap_alloc_nolock(); + struct ast_format tmp_fmt; pthread_t th; + + if (!cap) { + astman_send_error(s, m, "Internal Error. Memory allocation failure."); + } + ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0)); + if (ast_strlen_zero(name)) { astman_send_error(s, m, "Channel not specified"); - return 0; + res = 0; + goto fast_orig_cleanup; } if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) { if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) { astman_send_error(s, m, "Invalid priority"); - return 0; + res = 0; + goto fast_orig_cleanup; } } if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%30d", &to) != 1)) { astman_send_error(s, m, "Invalid timeout"); - return 0; + res = 0; + goto fast_orig_cleanup; } ast_copy_string(tmp, name, sizeof(tmp)); tech = tmp; data = strchr(tmp, '/'); if (!data) { astman_send_error(s, m, "Invalid channel"); - return 0; + res = 0; + goto fast_orig_cleanup; } *data++ = '\0'; ast_copy_string(tmp2, callerid, sizeof(tmp2)); @@ -3861,8 +3872,8 @@ static int action_originate(struct mansession *s, const struct message *m) } } if (!ast_strlen_zero(codecs)) { - format = 0; - ast_parse_allow_disallow(NULL, &format, codecs, 1); + ast_format_cap_remove_all(cap); + ast_parse_allow_disallow(NULL, cap, codecs, 1); } /* Allocate requested channel variables */ vars = astman_get_variables(m); @@ -3888,10 +3899,12 @@ static int action_originate(struct mansession *s, const struct message *m) ast_copy_string(fast->context, context, sizeof(fast->context)); ast_copy_string(fast->exten, exten, sizeof(fast->exten)); ast_copy_string(fast->account, account, sizeof(fast->account)); - fast->format = format; + fast->cap = cap; + cap = NULL; /* transfered originate helper the capabilities structure. It is now responsible for freeing it. */ fast->timeout = to; fast->priority = pi; if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) { + ast_format_cap_destroy(fast->cap); ast_free(fast); res = -1; } else { @@ -3912,18 +3925,20 @@ static int action_originate(struct mansession *s, const struct message *m) strstr(appdata, "EVAL") /* NoOp(${EVAL(${some_var_containing_SHELL})}) */ )) { astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have."); - return 0; + res = 0; + goto fast_orig_cleanup; } - res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL); + res = ast_pbx_outgoing_app(tech, cap, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL); } else { if (exten && context && pi) { - res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL); + res = ast_pbx_outgoing_exten(tech, cap, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL); } else { astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'"); if (vars) { ast_variables_destroy(vars); } - return 0; + res = 0; + goto fast_orig_cleanup; } } if (!res) { @@ -3931,6 +3946,9 @@ static int action_originate(struct mansession *s, const struct message *m) } else { astman_send_error(s, m, "Originate failed"); } + +fast_orig_cleanup: + ast_format_cap_destroy(cap); return 0; } diff --git a/main/pbx.c b/main/pbx.c index bceceb00f..1d8324e6e 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -8503,7 +8503,7 @@ static int ast_pbx_outgoing_cdr_failed(void) return 0; /* success */ } -int ast_pbx_outgoing_exten(const char *type, format_t format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel) +int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel) { struct ast_channel *chan; struct async_stat *as; @@ -8520,7 +8520,7 @@ int ast_pbx_outgoing_exten(const char *type, format_t format, void *data, int ti oh.vars = vars; oh.parent_channel = NULL; - chan = __ast_request_and_dial(type, format, NULL, data, timeout, reason, cid_num, cid_name, &oh); + chan = __ast_request_and_dial(type, cap, NULL, data, timeout, reason, cid_num, cid_name, &oh); if (channel) { *channel = chan; if (chan) @@ -8610,7 +8610,7 @@ int ast_pbx_outgoing_exten(const char *type, format_t format, void *data, int ti res = -1; goto outgoing_exten_cleanup; } - chan = ast_request_and_dial(type, format, NULL, data, timeout, reason, cid_num, cid_name); + chan = ast_request_and_dial(type, cap, NULL, data, timeout, reason, cid_num, cid_name); if (channel) { *channel = chan; if (chan) @@ -8669,7 +8669,7 @@ static void *ast_pbx_run_app(void *data) return NULL; } -int ast_pbx_outgoing_app(const char *type, format_t format, void *data, int timeout, const char *app, const char *appdata, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel) +int ast_pbx_outgoing_app(const char *type, struct ast_format_cap *cap, void *data, int timeout, const char *app, const char *appdata, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel) { struct ast_channel *chan; struct app_tmp *tmp; @@ -8687,7 +8687,7 @@ int ast_pbx_outgoing_app(const char *type, format_t format, void *data, int time goto outgoing_app_cleanup; } if (synchronous) { - chan = __ast_request_and_dial(type, format, NULL, data, timeout, reason, cid_num, cid_name, &oh); + chan = __ast_request_and_dial(type, cap, NULL, data, timeout, reason, cid_num, cid_name, &oh); if (chan) { ast_set_variables(chan, vars); if (account) @@ -8752,7 +8752,7 @@ int ast_pbx_outgoing_app(const char *type, format_t format, void *data, int time res = -1; goto outgoing_app_cleanup; } - chan = __ast_request_and_dial(type, format, NULL, data, timeout, reason, cid_num, cid_name, &oh); + chan = __ast_request_and_dial(type, cap, NULL, data, timeout, reason, cid_num, cid_name, &oh); if (!chan) { ast_free(as); res = -1; diff --git a/main/rtp_engine.c b/main/rtp_engine.c index 4a4dbf595..738b58fae 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -89,43 +89,43 @@ static const struct ast_rtp_mime_type { char *subtype; unsigned int sample_rate; } ast_rtp_mime_types[] = { - {{1, AST_FORMAT_G723_1}, "audio", "G723", 8000}, - {{1, AST_FORMAT_GSM}, "audio", "GSM", 8000}, - {{1, AST_FORMAT_ULAW}, "audio", "PCMU", 8000}, - {{1, AST_FORMAT_ULAW}, "audio", "G711U", 8000}, - {{1, AST_FORMAT_ALAW}, "audio", "PCMA", 8000}, - {{1, AST_FORMAT_ALAW}, "audio", "G711A", 8000}, - {{1, AST_FORMAT_G726}, "audio", "G726-32", 8000}, - {{1, AST_FORMAT_ADPCM}, "audio", "DVI4", 8000}, - {{1, AST_FORMAT_SLINEAR}, "audio", "L16", 8000}, - {{1, AST_FORMAT_SLINEAR16}, "audio", "L16", 16000}, - {{1, AST_FORMAT_LPC10}, "audio", "LPC", 8000}, - {{1, AST_FORMAT_G729A}, "audio", "G729", 8000}, - {{1, AST_FORMAT_G729A}, "audio", "G729A", 8000}, - {{1, AST_FORMAT_G729A}, "audio", "G.729", 8000}, - {{1, AST_FORMAT_SPEEX}, "audio", "speex", 8000}, - {{1, AST_FORMAT_SPEEX16}, "audio", "speex", 16000}, - {{1, AST_FORMAT_ILBC}, "audio", "iLBC", 8000}, + {{1, {.id = AST_FORMAT_G723_1}, 0}, "audio", "G723", 8000}, + {{1, {.id = AST_FORMAT_GSM}, 0}, "audio", "GSM", 8000}, + {{1, {.id = AST_FORMAT_ULAW}, 0}, "audio", "PCMU", 8000}, + {{1, {.id = AST_FORMAT_ULAW}, 0}, "audio", "G711U", 8000}, + {{1, {.id = AST_FORMAT_ALAW}, 0}, "audio", "PCMA", 8000}, + {{1, {.id = AST_FORMAT_ALAW}, 0}, "audio", "G711A", 8000}, + {{1, {.id = AST_FORMAT_G726}, 0}, "audio", "G726-32", 8000}, + {{1, {.id = AST_FORMAT_ADPCM}, 0}, "audio", "DVI4", 8000}, + {{1, {.id = AST_FORMAT_SLINEAR}, 0}, "audio", "L16", 8000}, + {{1, {.id = AST_FORMAT_SLINEAR16}, 0}, "audio", "L16", 16000}, + {{1, {.id = AST_FORMAT_LPC10}, 0}, "audio", "LPC", 8000}, + {{1, {.id = AST_FORMAT_G729A}, 0}, "audio", "G729", 8000}, + {{1, {.id = AST_FORMAT_G729A}, 0}, "audio", "G729A", 8000}, + {{1, {.id = AST_FORMAT_G729A}, 0}, "audio", "G.729", 8000}, + {{1, {.id = AST_FORMAT_SPEEX}, 0}, "audio", "speex", 8000}, + {{1, {.id = AST_FORMAT_SPEEX16}, 0}, "audio", "speex", 16000}, + {{1, {.id = AST_FORMAT_ILBC}, 0}, "audio", "iLBC", 8000}, /* this is the sample rate listed in the RTP profile for the G.722 codec, *NOT* the actual sample rate of the media stream */ - {{1, AST_FORMAT_G722}, "audio", "G722", 8000}, - {{1, AST_FORMAT_G726_AAL2}, "audio", "AAL2-G726-32", 8000}, - {{0, AST_RTP_DTMF}, "audio", "telephone-event", 8000}, - {{0, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event", 8000}, - {{0, AST_RTP_CN}, "audio", "CN", 8000}, - {{1, AST_FORMAT_JPEG}, "video", "JPEG", 90000}, - {{1, AST_FORMAT_PNG}, "video", "PNG", 90000}, - {{1, AST_FORMAT_H261}, "video", "H261", 90000}, - {{1, AST_FORMAT_H263}, "video", "H263", 90000}, - {{1, AST_FORMAT_H263_PLUS}, "video", "h263-1998", 90000}, - {{1, AST_FORMAT_H264}, "video", "H264", 90000}, - {{1, AST_FORMAT_MP4_VIDEO}, "video", "MP4V-ES", 90000}, - {{1, AST_FORMAT_T140RED}, "text", "RED", 1000}, - {{1, AST_FORMAT_T140}, "text", "T140", 1000}, - {{1, AST_FORMAT_SIREN7}, "audio", "G7221", 16000}, - {{1, AST_FORMAT_SIREN14}, "audio", "G7221", 32000}, - {{1, AST_FORMAT_G719}, "audio", "G719", 48000}, + {{1, {.id = AST_FORMAT_G722}, 0}, "audio", "G722", 8000}, + {{1, {.id = AST_FORMAT_G726_AAL2}, 0}, "audio", "AAL2-G726-32", 8000}, + {{0, {.id = 0}, AST_RTP_DTMF}, "audio", "telephone-event", 8000}, + {{0, {.id = 0}, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event", 8000}, + {{0, {.id = 0}, AST_RTP_CN}, "audio", "CN", 8000}, + {{1, {.id = AST_FORMAT_JPEG}, 0}, "video", "JPEG", 90000}, + {{1, {.id = AST_FORMAT_PNG}, 0}, "video", "PNG", 90000}, + {{1, {.id = AST_FORMAT_H261}, 0}, "video", "H261", 90000}, + {{1, {.id = AST_FORMAT_H263}, 0}, "video", "H263", 90000}, + {{1, {.id = AST_FORMAT_H263_PLUS}, 0}, "video", "h263-1998", 90000}, + {{1, {.id = AST_FORMAT_H264}, 0}, "video", "H264", 90000}, + {{1, {.id = AST_FORMAT_MP4_VIDEO}, 0}, "video", "MP4V-ES", 90000}, + {{1, {.id = AST_FORMAT_T140RED}, 0}, "text", "RED", 1000}, + {{1, {.id = AST_FORMAT_T140}, 0}, "text", "T140", 1000}, + {{1, {.id = AST_FORMAT_SIREN7}, 0}, "audio", "G7221", 16000}, + {{1, {.id = AST_FORMAT_SIREN14}, 0}, "audio", "G7221", 32000}, + {{1, {.id = AST_FORMAT_G719}, 0}, "audio", "G719", 48000}, }; /*! @@ -139,44 +139,44 @@ static const struct ast_rtp_mime_type { * assigned values */ static const struct ast_rtp_payload_type static_RTP_PT[AST_RTP_MAX_PT] = { - [0] = {1, AST_FORMAT_ULAW}, + [0] = {1, {.id = AST_FORMAT_ULAW}, 0}, #ifdef USE_DEPRECATED_G726 - [2] = {1, AST_FORMAT_G726}, /* Technically this is G.721, but if Cisco can do it, so can we... */ + [2] = {1, {.id = AST_FORMAT_G726}, 0},/* Technically this is G.721, but if Cisco can do it, so can we... */ #endif - [3] = {1, AST_FORMAT_GSM}, - [4] = {1, AST_FORMAT_G723_1}, - [5] = {1, AST_FORMAT_ADPCM}, /* 8 kHz */ - [6] = {1, AST_FORMAT_ADPCM}, /* 16 kHz */ - [7] = {1, AST_FORMAT_LPC10}, - [8] = {1, AST_FORMAT_ALAW}, - [9] = {1, AST_FORMAT_G722}, - [10] = {1, AST_FORMAT_SLINEAR}, /* 2 channels */ - [11] = {1, AST_FORMAT_SLINEAR}, /* 1 channel */ - [13] = {0, AST_RTP_CN}, - [16] = {1, AST_FORMAT_ADPCM}, /* 11.025 kHz */ - [17] = {1, AST_FORMAT_ADPCM}, /* 22.050 kHz */ - [18] = {1, AST_FORMAT_G729A}, - [19] = {0, AST_RTP_CN}, /* Also used for CN */ - [26] = {1, AST_FORMAT_JPEG}, - [31] = {1, AST_FORMAT_H261}, - [34] = {1, AST_FORMAT_H263}, - [97] = {1, AST_FORMAT_ILBC}, - [98] = {1, AST_FORMAT_H263_PLUS}, - [99] = {1, AST_FORMAT_H264}, - [101] = {0, AST_RTP_DTMF}, - [102] = {1, AST_FORMAT_SIREN7}, - [103] = {1, AST_FORMAT_H263_PLUS}, - [104] = {1, AST_FORMAT_MP4_VIDEO}, - [105] = {1, AST_FORMAT_T140RED}, /* Real time text chat (with redundancy encoding) */ - [106] = {1, AST_FORMAT_T140}, /* Real time text chat */ - [110] = {1, AST_FORMAT_SPEEX}, - [111] = {1, AST_FORMAT_G726}, - [112] = {1, AST_FORMAT_G726_AAL2}, - [115] = {1, AST_FORMAT_SIREN14}, - [116] = {1, AST_FORMAT_G719}, - [117] = {1, AST_FORMAT_SPEEX16}, - [118] = {1, AST_FORMAT_SLINEAR16}, /* 16 Khz signed linear */ - [121] = {0, AST_RTP_CISCO_DTMF}, /* Must be type 121 */ + [3] = {1, {.id = AST_FORMAT_GSM}, 0}, + [4] = {1, {.id = AST_FORMAT_G723_1}, 0}, + [5] = {1, {.id = AST_FORMAT_ADPCM}, 0},/* 8 kHz */ + [6] = {1, {.id = AST_FORMAT_ADPCM}, 0}, /* 16 kHz */ + [7] = {1, {.id = AST_FORMAT_LPC10}, 0}, + [8] = {1, {.id = AST_FORMAT_ALAW}, 0}, + [9] = {1, {.id = AST_FORMAT_G722}, 0}, + [10] = {1, {.id = AST_FORMAT_SLINEAR}, 0}, /* 2 channels */ + [11] = {1, {.id = AST_FORMAT_SLINEAR}, 0}, /* 1 channel */ + [13] = {0, {.id = 0}, AST_RTP_CN}, + [16] = {1, {.id = AST_FORMAT_ADPCM}, 0}, /* 11.025 kHz */ + [17] = {1, {.id = AST_FORMAT_ADPCM}, 0}, /* 22.050 kHz */ + [18] = {1, {.id = AST_FORMAT_G729A}, 0}, + [19] = {0, {.id = 0}, AST_RTP_CN}, /* Also used for CN */ + [26] = {1, {.id = AST_FORMAT_JPEG}, 0}, + [31] = {1, {.id = AST_FORMAT_H261}, 0}, + [34] = {1, {.id = AST_FORMAT_H263}, 0}, + [97] = {1, {.id = AST_FORMAT_ILBC}, 0}, + [98] = {1, {.id = AST_FORMAT_H263_PLUS}, 0}, + [99] = {1, {.id = AST_FORMAT_H264}, 0}, + [101] = {0, {.id = 0}, AST_RTP_DTMF}, + [102] = {1, {.id = AST_FORMAT_SIREN7}, 0}, + [103] = {1, {.id = AST_FORMAT_H263_PLUS}, 0}, + [104] = {1, {.id = AST_FORMAT_MP4_VIDEO}, 0}, + [105] = {1, {.id = AST_FORMAT_T140RED}, 0}, /* Real time text chat (with redundancy encoding) */ + [106] = {1, {.id = AST_FORMAT_T140}, 0}, /* Real time text chat */ + [110] = {1, {.id = AST_FORMAT_SPEEX}, 0}, + [111] = {1, {.id = AST_FORMAT_G726}, 0}, + [112] = {1, {.id = AST_FORMAT_G726_AAL2}, 0}, + [115] = {1, {.id = AST_FORMAT_SIREN14}, 0}, + [116] = {1, {.id = AST_FORMAT_G719}, 0}, + [117] = {1, {.id = AST_FORMAT_SPEEX16}, 0}, + [118] = {1, {.id = AST_FORMAT_SLINEAR16}, 0}, /* 16 Khz signed linear */ + [121] = {0, {.id = 0}, AST_RTP_CISCO_DTMF}, /* Must be type 121 */ }; int ast_rtp_engine_register2(struct ast_rtp_engine *engine, struct ast_module *module) @@ -485,9 +485,10 @@ void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp for (i = 0; i < AST_RTP_MAX_PT; i++) { codecs->payloads[i].asterisk_format = 0; - codecs->payloads[i].code = 0; + codecs->payloads[i].rtp_code = 0; + ast_format_clear(&codecs->payloads[i].format); if (instance && instance->engine && instance->engine->payload_set) { - instance->engine->payload_set(instance, i, 0, 0); + instance->engine->payload_set(instance, i, 0, NULL, 0); } } } @@ -497,11 +498,13 @@ void ast_rtp_codecs_payloads_default(struct ast_rtp_codecs *codecs, struct ast_r int i; for (i = 0; i < AST_RTP_MAX_PT; i++) { - if (static_RTP_PT[i].code) { + if (static_RTP_PT[i].rtp_code || static_RTP_PT[i].asterisk_format) { + codecs->payloads[i].asterisk_format = static_RTP_PT[i].asterisk_format; - codecs->payloads[i].code = static_RTP_PT[i].code; + codecs->payloads[i].rtp_code = static_RTP_PT[i].rtp_code; + ast_format_copy(&codecs->payloads[i].format, &static_RTP_PT[i].format); if (instance && instance->engine && instance->engine->payload_set) { - instance->engine->payload_set(instance, i, codecs->payloads[i].asterisk_format, codecs->payloads[i].code); + instance->engine->payload_set(instance, i, codecs->payloads[i].asterisk_format, &codecs->payloads[i].format, codecs->payloads[i].rtp_code); } } } @@ -512,12 +515,13 @@ void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_cod int i; for (i = 0; i < AST_RTP_MAX_PT; i++) { - if (src->payloads[i].code) { + if (src->payloads[i].rtp_code || src->payloads[i].asterisk_format) { ast_debug(2, "Copying payload %d from %p to %p\n", i, src, dest); dest->payloads[i].asterisk_format = src->payloads[i].asterisk_format; - dest->payloads[i].code = src->payloads[i].code; + dest->payloads[i].rtp_code = src->payloads[i].rtp_code; + ast_format_copy(&dest->payloads[i].format, &src->payloads[i].format); if (instance && instance->engine && instance->engine->payload_set) { - instance->engine->payload_set(instance, i, dest->payloads[i].asterisk_format, dest->payloads[i].code); + instance->engine->payload_set(instance, i, dest->payloads[i].asterisk_format, &dest->payloads[i].format, dest->payloads[i].rtp_code); } } } @@ -525,17 +529,18 @@ void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_cod void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload) { - if (payload < 0 || payload >= AST_RTP_MAX_PT || !static_RTP_PT[payload].code) { + if (payload < 0 || payload >= AST_RTP_MAX_PT || (!static_RTP_PT[payload].rtp_code && !static_RTP_PT[payload].asterisk_format)) { return; } codecs->payloads[payload].asterisk_format = static_RTP_PT[payload].asterisk_format; - codecs->payloads[payload].code = static_RTP_PT[payload].code; + codecs->payloads[payload].rtp_code = static_RTP_PT[payload].rtp_code; + ast_format_copy(&codecs->payloads[payload].format, &static_RTP_PT[payload].format); ast_debug(1, "Setting payload %d based on m type on %p\n", payload, codecs); if (instance && instance->engine && instance->engine->payload_set) { - instance->engine->payload_set(instance, payload, codecs->payloads[payload].asterisk_format, codecs->payloads[payload].code); + instance->engine->payload_set(instance, payload, codecs->payloads[payload].asterisk_format, &codecs->payloads[payload].format, codecs->payloads[payload].rtp_code); } } @@ -572,14 +577,12 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, found = 1; codecs->payloads[pt] = t->payload_type; - if ((t->payload_type.code == AST_FORMAT_G726) && - t->payload_type.asterisk_format && - (options & AST_RTP_OPT_G726_NONSTANDARD)) { - codecs->payloads[pt].code = AST_FORMAT_G726_AAL2; + if ((t->payload_type.format.id == AST_FORMAT_G726) && t->payload_type.asterisk_format && (options & AST_RTP_OPT_G726_NONSTANDARD)) { + ast_format_set(&codecs->payloads[pt].format, AST_FORMAT_G726_AAL2, 0); } if (instance && instance->engine && instance->engine->payload_set) { - instance->engine->payload_set(instance, pt, codecs->payloads[i].asterisk_format, codecs->payloads[i].code); + instance->engine->payload_set(instance, pt, codecs->payloads[i].asterisk_format, &codecs->payloads[i].format, codecs->payloads[i].rtp_code); } break; @@ -602,10 +605,11 @@ void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp ast_debug(2, "Unsetting payload %d on %p\n", payload, codecs); codecs->payloads[payload].asterisk_format = 0; - codecs->payloads[payload].code = 0; + codecs->payloads[payload].rtp_code = 0; + ast_format_clear(&codecs->payloads[payload].format); if (instance && instance->engine && instance->engine->payload_set) { - instance->engine->payload_set(instance, payload, 0, 0); + instance->engine->payload_set(instance, payload, 0, NULL, 0); } } @@ -618,45 +622,55 @@ struct ast_rtp_payload_type ast_rtp_codecs_payload_lookup(struct ast_rtp_codecs } result.asterisk_format = codecs->payloads[payload].asterisk_format; - result.code = codecs->payloads[payload].code; + result.rtp_code = codecs->payloads[payload].rtp_code; + ast_format_copy(&result.format, &codecs->payloads[payload].format); - if (!result.code) { + if (!result.rtp_code && !result.asterisk_format) { result = static_RTP_PT[payload]; } return result; } -void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, format_t *astformats, int *nonastformats) +void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_format_cap *astformats, int *nonastformats) { int i; - *astformats = *nonastformats = 0; + ast_format_cap_remove_all(astformats); + *nonastformats = 0; for (i = 0; i < AST_RTP_MAX_PT; i++) { - if (codecs->payloads[i].code) { + if (codecs->payloads[i].rtp_code || codecs->payloads[i].asterisk_format) { ast_debug(1, "Incorporating payload %d on %p\n", i, codecs); } if (codecs->payloads[i].asterisk_format) { - *astformats |= codecs->payloads[i].code; + ast_format_cap_add(astformats, &codecs->payloads[i].format); } else { - *nonastformats |= codecs->payloads[i].code; + *nonastformats |= codecs->payloads[i].rtp_code; } } } -int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, const int asterisk_format, const format_t code) +int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code) { int i; for (i = 0; i < AST_RTP_MAX_PT; i++) { - if (codecs->payloads[i].asterisk_format == asterisk_format && codecs->payloads[i].code == code) { + if (codecs->payloads[i].asterisk_format && asterisk_format && format && + (ast_format_cmp(format, &codecs->payloads[i].format) != AST_FORMAT_CMP_NOT_EQUAL)) { + return i; + } else if (!codecs->payloads[i].asterisk_format && !asterisk_format && + (codecs->payloads[i].rtp_code == code)) { return i; } } for (i = 0; i < AST_RTP_MAX_PT; i++) { - if (static_RTP_PT[i].asterisk_format == asterisk_format && static_RTP_PT[i].code == code) { + if (static_RTP_PT[i].asterisk_format && asterisk_format && format && + (ast_format_cmp(format, &static_RTP_PT[i].format) != AST_FORMAT_CMP_NOT_EQUAL)) { + return i; + } else if (!static_RTP_PT[i].asterisk_format && !asterisk_format && + (static_RTP_PT[i].rtp_code == code)) { return i; } } @@ -664,29 +678,38 @@ int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, const int asteris return -1; } -const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, const format_t code, enum ast_rtp_options options) +const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, struct ast_format *format, int code, enum ast_rtp_options options) { int i; for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); i++) { - if (ast_rtp_mime_types[i].payload_type.code == code && ast_rtp_mime_types[i].payload_type.asterisk_format == asterisk_format) { - if (asterisk_format && (code == AST_FORMAT_G726_AAL2) && (options & AST_RTP_OPT_G726_NONSTANDARD)) { + if (ast_rtp_mime_types[i].payload_type.asterisk_format && asterisk_format && format && + (ast_format_cmp(format, &ast_rtp_mime_types[i].payload_type.format) != AST_FORMAT_CMP_NOT_EQUAL)) { + if ((format->id == AST_FORMAT_G726_AAL2) && (options & AST_RTP_OPT_G726_NONSTANDARD)) { return "G726-32"; } else { return ast_rtp_mime_types[i].subtype; } + } else if (!ast_rtp_mime_types[i].payload_type.asterisk_format && !asterisk_format && + ast_rtp_mime_types[i].payload_type.rtp_code == code) { + + return ast_rtp_mime_types[i].subtype; } } return ""; } -unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, format_t code) +unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, struct ast_format *format, int code) { unsigned int i; for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); ++i) { - if ((ast_rtp_mime_types[i].payload_type.code == code) && (ast_rtp_mime_types[i].payload_type.asterisk_format == asterisk_format)) { + if (ast_rtp_mime_types[i].payload_type.asterisk_format && asterisk_format && format && + (ast_format_cmp(format, &ast_rtp_mime_types[i].payload_type.format) != AST_FORMAT_CMP_NOT_EQUAL)) { + return ast_rtp_mime_types[i].sample_rate; + } else if (!ast_rtp_mime_types[i].payload_type.asterisk_format && !asterisk_format && + ast_rtp_mime_types[i].payload_type.rtp_code == code) { return ast_rtp_mime_types[i].sample_rate; } } @@ -694,23 +717,35 @@ unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, format_t code) return 0; } -char *ast_rtp_lookup_mime_multiple2(struct ast_str *buf, const format_t capability, const int asterisk_format, enum ast_rtp_options options) +char *ast_rtp_lookup_mime_multiple2(struct ast_str *buf, struct ast_format_cap *ast_format_capability, int rtp_capability, const int asterisk_format, enum ast_rtp_options options) { - format_t format; int found = 0; - + const char *name; if (!buf) { return NULL; } - ast_str_append(&buf, 0, "0x%llx (", (unsigned long long) capability); - for (format = 1; format < AST_RTP_MAX; format <<= 1) { - if (capability & format) { - const char *name = ast_rtp_lookup_mime_subtype2(asterisk_format, format, options); + if (asterisk_format) { + struct ast_format tmp_fmt; + ast_format_cap_iter_start(ast_format_capability); + while (!ast_format_cap_iter_next(ast_format_capability, &tmp_fmt)) { + name = ast_rtp_lookup_mime_subtype2(asterisk_format, &tmp_fmt, 0, options); ast_str_append(&buf, 0, "%s|", name); found = 1; } + ast_format_cap_iter_end(ast_format_capability); + + } else { + int x; + ast_str_append(&buf, 0, "0x%x (", (unsigned int) rtp_capability); + for (x = 1; x < AST_RTP_MAX; x <<= 1) { + if (rtp_capability & x) { + name = ast_rtp_lookup_mime_subtype2(asterisk_format, NULL, x, options); + ast_str_append(&buf, 0, "%s|", name); + found = 1; + } + } } ast_str_append(&buf, 0, "%s", found ? ")" : "nothing)"); @@ -842,7 +877,8 @@ static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct a cs[2] = NULL; for (;;) { /* If the underlying formats have changed force this bridge to break */ - if ((c0->rawreadformat != c1->rawwriteformat) || (c1->rawreadformat != c0->rawwriteformat)) { + if ((ast_format_cmp(&c0->rawreadformat, &c1->rawwriteformat) == AST_FORMAT_CMP_NOT_EQUAL) || + (ast_format_cmp(&c1->rawreadformat, &c0->rawwriteformat) == AST_FORMAT_CMP_NOT_EQUAL)) { ast_debug(1, "rtp-engine-local-bridge: Oooh, formats changed, backing out\n"); res = AST_BRIDGE_FAILED_NOWARN; break; @@ -969,20 +1005,40 @@ static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct a return res; } -static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp_instance *instance0, struct ast_rtp_instance *instance1, - struct ast_rtp_instance *vinstance0, struct ast_rtp_instance *vinstance1, struct ast_rtp_instance *tinstance0, - struct ast_rtp_instance *tinstance1, struct ast_rtp_glue *glue0, struct ast_rtp_glue *glue1, format_t codec0, format_t codec1, int timeoutms, - int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1) +static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, + struct ast_channel *c1, + struct ast_rtp_instance *instance0, + struct ast_rtp_instance *instance1, + struct ast_rtp_instance *vinstance0, + struct ast_rtp_instance *vinstance1, + struct ast_rtp_instance *tinstance0, + struct ast_rtp_instance *tinstance1, + struct ast_rtp_glue *glue0, + struct ast_rtp_glue *glue1, + struct ast_format_cap *cap0, + struct ast_format_cap *cap1, + int timeoutms, + int flags, + struct ast_frame **fo, + struct ast_channel **rc, + void *pvt0, + void *pvt1) { enum ast_bridge_result res = AST_BRIDGE_FAILED; struct ast_channel *who = NULL, *other = NULL, *cs[3] = { NULL, }; - format_t oldcodec0 = codec0, oldcodec1 = codec1; + struct ast_format_cap *oldcap0 = ast_format_cap_dup(cap0); + struct ast_format_cap *oldcap1 = ast_format_cap_dup(cap1); struct ast_sockaddr ac1 = {{0,}}, vac1 = {{0,}}, tac1 = {{0,}}, ac0 = {{0,}}, vac0 = {{0,}}, tac0 = {{0,}}; struct ast_sockaddr t1 = {{0,}}, vt1 = {{0,}}, tt1 = {{0,}}, t0 = {{0,}}, vt0 = {{0,}}, tt0 = {{0,}}; struct ast_frame *fr = NULL; + if (!oldcap0 || !oldcap1) { + ast_channel_unlock(c0); + ast_channel_unlock(c1); + goto remote_bridge_cleanup; + } /* Test the first channel */ - if (!(glue0->update_peer(c0, instance1, vinstance1, tinstance1, codec1, 0))) { + if (!(glue0->update_peer(c0, instance1, vinstance1, tinstance1, cap1, 0))) { ast_rtp_instance_get_remote_address(instance1, &ac1); if (vinstance1) { ast_rtp_instance_get_remote_address(vinstance1, &vac1); @@ -995,7 +1051,7 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct } /* Test the second channel */ - if (!(glue1->update_peer(c1, instance0, vinstance0, tinstance0, codec0, 0))) { + if (!(glue1->update_peer(c1, instance0, vinstance0, tinstance0, cap0, 0))) { ast_rtp_instance_get_remote_address(instance0, &ac0); if (vinstance0) { ast_rtp_instance_get_remote_address(instance0, &vac0); @@ -1039,7 +1095,8 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct ast_rtp_instance_get_remote_address(tinstance1, &tt1); } if (glue1->get_codec) { - codec1 = glue1->get_codec(c1); + ast_format_cap_remove_all(cap1); + glue1->get_codec(c1, cap1); } ast_rtp_instance_get_remote_address(instance0, &t0); @@ -1050,63 +1107,66 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct ast_rtp_instance_get_remote_address(tinstance0, &tt0); } if (glue0->get_codec) { - codec0 = glue0->get_codec(c0); + ast_format_cap_remove_all(cap0); + glue0->get_codec(c0, cap0); } if ((ast_sockaddr_cmp(&t1, &ac1)) || (vinstance1 && ast_sockaddr_cmp(&vt1, &vac1)) || (tinstance1 && ast_sockaddr_cmp(&tt1, &tac1)) || - (codec1 != oldcodec1)) { + (!ast_format_cap_identical(cap1, oldcap1))) { + char tmp_buf[512] = { 0, }; ast_debug(1, "Oooh, '%s' changed end address to %s (format %s)\n", c1->name, ast_sockaddr_stringify(&t1), - ast_getformatname(codec1)); + ast_getformatname_multiple(tmp_buf, sizeof(tmp_buf), cap1)); ast_debug(1, "Oooh, '%s' changed end vaddress to %s (format %s)\n", c1->name, ast_sockaddr_stringify(&vt1), - ast_getformatname(codec1)); + ast_getformatname_multiple(tmp_buf, sizeof(tmp_buf), cap1)); ast_debug(1, "Oooh, '%s' changed end taddress to %s (format %s)\n", c1->name, ast_sockaddr_stringify(&tt1), - ast_getformatname(codec1)); + ast_getformatname_multiple(tmp_buf, sizeof(tmp_buf), cap1)); ast_debug(1, "Oooh, '%s' was %s/(format %s)\n", c1->name, ast_sockaddr_stringify(&ac1), - ast_getformatname(oldcodec1)); + ast_getformatname_multiple(tmp_buf, sizeof(tmp_buf), oldcap1)); ast_debug(1, "Oooh, '%s' was %s/(format %s)\n", c1->name, ast_sockaddr_stringify(&vac1), - ast_getformatname(oldcodec1)); + ast_getformatname_multiple(tmp_buf, sizeof(tmp_buf), oldcap1)); ast_debug(1, "Oooh, '%s' was %s/(format %s)\n", c1->name, ast_sockaddr_stringify(&tac1), - ast_getformatname(oldcodec1)); + ast_getformatname_multiple(tmp_buf, sizeof(tmp_buf), oldcap1)); if (glue0->update_peer(c0, ast_sockaddr_isnull(&t1) ? NULL : instance1, ast_sockaddr_isnull(&vt1) ? NULL : vinstance1, ast_sockaddr_isnull(&tt1) ? NULL : tinstance1, - codec1, 0)) { + cap1, 0)) { ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name); } ast_sockaddr_copy(&ac1, &t1); ast_sockaddr_copy(&vac1, &vt1); ast_sockaddr_copy(&tac1, &tt1); - oldcodec1 = codec1; + ast_format_cap_copy(oldcap1, cap1); } if ((ast_sockaddr_cmp(&t0, &ac0)) || (vinstance0 && ast_sockaddr_cmp(&vt0, &vac0)) || (tinstance0 && ast_sockaddr_cmp(&tt0, &tac0)) || - (codec0 != oldcodec0)) { + (!ast_format_cap_identical(cap0, oldcap0))) { + char tmp_buf[512] = { 0, }; ast_debug(1, "Oooh, '%s' changed end address to %s (format %s)\n", c0->name, ast_sockaddr_stringify(&t0), - ast_getformatname(codec0)); + ast_getformatname_multiple(tmp_buf, sizeof(tmp_buf), cap0)); ast_debug(1, "Oooh, '%s' was %s/(format %s)\n", c0->name, ast_sockaddr_stringify(&ac0), - ast_getformatname(oldcodec0)); + ast_getformatname_multiple(tmp_buf, sizeof(tmp_buf), oldcap0)); if (glue1->update_peer(c1, t0.len ? instance0 : NULL, vt0.len ? vinstance0 : NULL, tt0.len ? tinstance0 : NULL, - codec0, 0)) { + cap0, 0)) { ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c1->name, c0->name); } ast_sockaddr_copy(&ac0, &t0); ast_sockaddr_copy(&vac0, &vt0); ast_sockaddr_copy(&tac0, &tt0); - oldcodec0 = codec0; + ast_format_cap_copy(oldcap0, cap0); } /* Wait for frame to come in on the channels */ @@ -1148,9 +1208,9 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct } else if (fr->subclass.integer == AST_CONTROL_UNHOLD) { /* If they went off hold they should go back to being direct */ if (who == c0) { - glue1->update_peer(c1, instance0, vinstance0, tinstance0, codec0, 0); + glue1->update_peer(c1, instance0, vinstance0, tinstance0, cap0, 0); } else { - glue0->update_peer(c0, instance1, vinstance1, tinstance1, codec1, 0); + glue0->update_peer(c0, instance1, vinstance1, tinstance1, cap1, 0); } } /* Update local address information */ @@ -1160,10 +1220,17 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct ast_sockaddr_copy(&ac1, &t1); /* Update codec information */ if (glue0->get_codec && c0->tech_pvt) { - oldcodec0 = codec0 = glue0->get_codec(c0); + ast_format_cap_remove_all(cap0); + ast_format_cap_remove_all(oldcap0); + glue0->get_codec(c0, cap0); + ast_format_cap_append(oldcap0, cap0); + } if (glue1->get_codec && c1->tech_pvt) { - oldcodec1 = codec1 = glue1->get_codec(c1); + ast_format_cap_remove_all(cap1); + ast_format_cap_remove_all(oldcap1); + glue0->get_codec(c1, cap1); + ast_format_cap_append(oldcap1, cap1); } ast_indicate_data(other, fr->subclass.integer, fr->data.ptr, fr->datalen); ast_frfree(fr); @@ -1181,7 +1248,8 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct *fo = fr; *rc = who; ast_debug(1, "Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass.integer, who->name); - return AST_BRIDGE_COMPLETE; + res = AST_BRIDGE_COMPLETE; + goto remote_bridge_cleanup; } } else { if ((fr->frametype == AST_FRAME_DTMF_BEGIN) || @@ -1214,6 +1282,10 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct ast_poll_channel_del(c0, c1); +remote_bridge_cleanup: + ast_format_cap_destroy(oldcap0); + ast_format_cap_destroy(oldcap1); + return res; } @@ -1238,9 +1310,15 @@ enum ast_bridge_result ast_rtp_instance_bridge(struct ast_channel *c0, struct as enum ast_rtp_glue_result audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID, video_glue0_res = AST_RTP_GLUE_RESULT_FORBID, text_glue0_res = AST_RTP_GLUE_RESULT_FORBID; enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID, video_glue1_res = AST_RTP_GLUE_RESULT_FORBID, text_glue1_res = AST_RTP_GLUE_RESULT_FORBID; enum ast_bridge_result res = AST_BRIDGE_FAILED; - format_t codec0 = 0, codec1 = 0; + struct ast_format_cap *cap0 = ast_format_cap_alloc_nolock(); + struct ast_format_cap *cap1 = ast_format_cap_alloc_nolock(); int unlock_chans = 1; + if (!cap0 || !cap1) { + unlock_chans = 0; + goto done; + } + /* Lock both channels so we can look for the glue that binds them together */ ast_channel_lock(c0); while (ast_channel_trylock(c1)) { @@ -1311,10 +1389,18 @@ enum ast_bridge_result ast_rtp_instance_bridge(struct ast_channel *c0, struct as } /* Make sure that codecs match */ - codec0 = glue0->get_codec ? glue0->get_codec(c0) : 0; - codec1 = glue1->get_codec ? glue1->get_codec(c1) : 0; - if (codec0 && codec1 && !(codec0 & codec1)) { - ast_debug(1, "Channel codec0 = %s is not codec1 = %s, cannot native bridge in RTP.\n", ast_getformatname(codec0), ast_getformatname(codec1)); + if (glue0->get_codec){ + glue0->get_codec(c0, cap0); + } + if (glue1->get_codec) { + glue1->get_codec(c1, cap1); + } + if (!ast_format_cap_is_empty(cap0) && !ast_format_cap_is_empty(cap1) && !ast_format_cap_has_joint(cap0, cap1)) { + char tmp0[256] = { 0, }; + char tmp1[256] = { 0, }; + ast_debug(1, "Channel codec0 = %s is not codec1 = %s, cannot native bridge in RTP.\n", + ast_getformatname_multiple(tmp0, sizeof(tmp0), cap0), + ast_getformatname_multiple(tmp1, sizeof(tmp1), cap1)); res = AST_BRIDGE_FAILED_NOWARN; goto done; } @@ -1331,7 +1417,7 @@ enum ast_bridge_result ast_rtp_instance_bridge(struct ast_channel *c0, struct as } else { ast_verbose(VERBOSE_PREFIX_3 "Remotely bridging %s and %s\n", c0->name, c1->name); res = remote_bridge_loop(c0, c1, instance0, instance1, vinstance0, vinstance1, - tinstance0, tinstance1, glue0, glue1, codec0, codec1, timeoutms, flags, + tinstance0, tinstance1, glue0, glue1, cap0, cap1, timeoutms, flags, fo, rc, c0->tech_pvt, c1->tech_pvt); } @@ -1347,6 +1433,8 @@ done: ast_channel_unlock(c0); ast_channel_unlock(c1); } + ast_format_cap_destroy(cap1); + ast_format_cap_destroy(cap0); unref_instance_cond(&instance0); unref_instance_cond(&instance1); @@ -1371,7 +1459,8 @@ void ast_rtp_instance_early_bridge_make_compatible(struct ast_channel *c0, struc struct ast_rtp_glue *glue0, *glue1; enum ast_rtp_glue_result audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID, video_glue0_res = AST_RTP_GLUE_RESULT_FORBID, text_glue0_res = AST_RTP_GLUE_RESULT_FORBID; enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID, video_glue1_res = AST_RTP_GLUE_RESULT_FORBID, text_glue1_res = AST_RTP_GLUE_RESULT_FORBID; - format_t codec0 = 0, codec1 = 0; + struct ast_format_cap *cap0 = ast_format_cap_alloc_nolock(); + struct ast_format_cap *cap1 = ast_format_cap_alloc_nolock(); int res = 0; /* Lock both channels so we can look for the glue that binds them together */ @@ -1382,6 +1471,10 @@ void ast_rtp_instance_early_bridge_make_compatible(struct ast_channel *c0, struc ast_channel_lock(c0); } + if (!cap1 || !cap0) { + goto done; + } + /* Grab glue that binds each channel to something using the RTP engine */ if (!(glue0 = ast_rtp_instance_get_glue(c0->tech->type)) || !(glue1 = ast_rtp_instance_get_glue(c1->tech->type))) { ast_debug(1, "Can't find native functions for channel '%s'\n", glue0 ? c1->name : c0->name); @@ -1404,10 +1497,10 @@ void ast_rtp_instance_early_bridge_make_compatible(struct ast_channel *c0, struc audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID; } if (audio_glue0_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue0_res == AST_RTP_GLUE_RESULT_FORBID || video_glue0_res == AST_RTP_GLUE_RESULT_REMOTE) && glue0->get_codec) { - codec0 = glue0->get_codec(c0); + glue0->get_codec(c0, cap0); } if (audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue1_res == AST_RTP_GLUE_RESULT_FORBID || video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) && glue1->get_codec) { - codec1 = glue1->get_codec(c1); + glue1->get_codec(c1, cap1); } /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */ @@ -1416,7 +1509,7 @@ void ast_rtp_instance_early_bridge_make_compatible(struct ast_channel *c0, struc } /* Make sure we have matching codecs */ - if (!(codec0 & codec1)) { + if (!ast_format_cap_has_joint(cap0, cap1)) { goto done; } @@ -1435,6 +1528,9 @@ done: ast_channel_unlock(c0); ast_channel_unlock(c1); + ast_format_cap_destroy(cap0); + ast_format_cap_destroy(cap1); + unref_instance_cond(&instance0); unref_instance_cond(&instance1); unref_instance_cond(&vinstance0); @@ -1455,11 +1551,14 @@ int ast_rtp_instance_early_bridge(struct ast_channel *c0, struct ast_channel *c1 struct ast_rtp_glue *glue0, *glue1; enum ast_rtp_glue_result audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID, video_glue0_res = AST_RTP_GLUE_RESULT_FORBID, text_glue0_res = AST_RTP_GLUE_RESULT_FORBID; enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID, video_glue1_res = AST_RTP_GLUE_RESULT_FORBID, text_glue1_res = AST_RTP_GLUE_RESULT_FORBID; - format_t codec0 = 0, codec1 = 0; + struct ast_format_cap *cap0 = ast_format_cap_alloc_nolock(); + struct ast_format_cap *cap1 = ast_format_cap_alloc_nolock(); int res = 0; /* If there is no second channel just immediately bail out, we are of no use in that scenario */ if (!c1) { + ast_format_cap_destroy(cap0); + ast_format_cap_destroy(cap1); return -1; } @@ -1471,6 +1570,10 @@ int ast_rtp_instance_early_bridge(struct ast_channel *c0, struct ast_channel *c1 ast_channel_lock(c0); } + if (!cap1 || !cap0) { + goto done; + } + /* Grab glue that binds each channel to something using the RTP engine */ if (!(glue0 = ast_rtp_instance_get_glue(c0->tech->type)) || !(glue1 = ast_rtp_instance_get_glue(c1->tech->type))) { ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", glue0 ? c1->name : c0->name); @@ -1492,11 +1595,11 @@ int ast_rtp_instance_early_bridge(struct ast_channel *c0, struct ast_channel *c1 if (video_glue1_res != AST_RTP_GLUE_RESULT_FORBID && (audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE)) { audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID; } - if (audio_glue0_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue0_res == AST_RTP_GLUE_RESULT_FORBID || video_glue0_res == AST_RTP_GLUE_RESULT_REMOTE) && glue0->get_codec(c0)) { - codec0 = glue0->get_codec(c0); + if (audio_glue0_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue0_res == AST_RTP_GLUE_RESULT_FORBID || video_glue0_res == AST_RTP_GLUE_RESULT_REMOTE) && glue0->get_codec) { + glue0->get_codec(c0, cap0); } - if (audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue1_res == AST_RTP_GLUE_RESULT_FORBID || video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) && glue1->get_codec(c1)) { - codec1 = glue1->get_codec(c1); + if (audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE && (video_glue1_res == AST_RTP_GLUE_RESULT_FORBID || video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) && glue1->get_codec) { + glue1->get_codec(c1, cap1); } /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */ @@ -1505,12 +1608,12 @@ int ast_rtp_instance_early_bridge(struct ast_channel *c0, struct ast_channel *c1 } /* Make sure we have matching codecs */ - if (!(codec0 & codec1)) { + if (!ast_format_cap_has_joint(cap0, cap1)) { goto done; } /* Bridge media early */ - if (glue0->update_peer(c0, instance1, vinstance1, tinstance1, codec1, 0)) { + if (glue0->update_peer(c0, instance1, vinstance1, tinstance1, cap1, 0)) { ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", c0->name, c1 ? c1->name : "<unspecified>"); } @@ -1520,6 +1623,9 @@ done: ast_channel_unlock(c0); ast_channel_unlock(c1); + ast_format_cap_destroy(cap0); + ast_format_cap_destroy(cap1); + unref_instance_cond(&instance0); unref_instance_cond(&instance1); unref_instance_cond(&vinstance0); @@ -1623,12 +1729,12 @@ void ast_rtp_instance_set_stats_vars(struct ast_channel *chan, struct ast_rtp_in } } -int ast_rtp_instance_set_read_format(struct ast_rtp_instance *instance, format_t format) +int ast_rtp_instance_set_read_format(struct ast_rtp_instance *instance, struct ast_format *format) { return instance->engine->set_read_format ? instance->engine->set_read_format(instance, format) : -1; } -int ast_rtp_instance_set_write_format(struct ast_rtp_instance *instance, format_t format) +int ast_rtp_instance_set_write_format(struct ast_rtp_instance *instance, struct ast_format *format) { return instance->engine->set_write_format ? instance->engine->set_write_format(instance, format) : -1; } @@ -1669,15 +1775,16 @@ int ast_rtp_instance_make_compatible(struct ast_channel *chan, struct ast_rtp_in return res; } -format_t ast_rtp_instance_available_formats(struct ast_rtp_instance *instance, format_t to_endpoint, format_t to_asterisk) +void ast_rtp_instance_available_formats(struct ast_rtp_instance *instance, struct ast_format_cap *to_endpoint, struct ast_format_cap *to_asterisk, struct ast_format_cap *result) { - format_t formats; - - if (instance->engine->available_formats && (formats = instance->engine->available_formats(instance, to_endpoint, to_asterisk))) { - return formats; + if (instance->engine->available_formats) { + instance->engine->available_formats(instance, to_endpoint, to_asterisk, result); + if (!ast_format_cap_is_empty(result)) { + return; + } } - return ast_translate_available_formats(to_endpoint, to_asterisk); + ast_translate_available_formats(to_endpoint, to_asterisk, result); } int ast_rtp_instance_activate(struct ast_rtp_instance *instance) diff --git a/main/slinfactory.c b/main/slinfactory.c index 4da443af5..f7363ab4b 100644 --- a/main/slinfactory.c +++ b/main/slinfactory.c @@ -36,7 +36,7 @@ void ast_slinfactory_init(struct ast_slinfactory *sf) { memset(sf, 0, sizeof(*sf)); sf->offset = sf->hold; - sf->output_format = AST_FORMAT_SLINEAR; + ast_format_set(&sf->output_format, AST_FORMAT_SLINEAR, 0); } int ast_slinfactory_init_rate(struct ast_slinfactory *sf, unsigned int sample_rate) @@ -45,10 +45,10 @@ int ast_slinfactory_init_rate(struct ast_slinfactory *sf, unsigned int sample_ra sf->offset = sf->hold; switch (sample_rate) { case 8000: - sf->output_format = AST_FORMAT_SLINEAR; + ast_format_set(&sf->output_format, AST_FORMAT_SLINEAR, 0); break; case 16000: - sf->output_format = AST_FORMAT_SLINEAR16; + ast_format_set(&sf->output_format, AST_FORMAT_SLINEAR16, 0); break; default: return -1; @@ -85,19 +85,19 @@ int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f) return 0; } - if (f->subclass.codec != sf->output_format) { - if (sf->trans && f->subclass.codec != sf->format) { + if (ast_format_cmp(&f->subclass.format, &sf->output_format) == AST_FORMAT_CMP_NOT_EQUAL) { + if (sf->trans && (ast_format_cmp(&f->subclass.format, &sf->format) == AST_FORMAT_CMP_NOT_EQUAL)) { ast_translator_free_path(sf->trans); sf->trans = NULL; } if (!sf->trans) { - if (!(sf->trans = ast_translator_build_path(sf->output_format, f->subclass.codec))) { - ast_log(LOG_WARNING, "Cannot build a path from %s to %s\n", ast_getformatname(f->subclass.codec), - ast_getformatname(sf->output_format)); + if (!(sf->trans = ast_translator_build_path(&sf->output_format, &f->subclass.format))) { + ast_log(LOG_WARNING, "Cannot build a path from %s to %s\n", ast_getformatname(&f->subclass.format), + ast_getformatname(&sf->output_format)); return 0; } - sf->format = f->subclass.codec; + ast_format_copy(&sf->format, &f->subclass.format); } if (!(begin_frame = ast_translate(sf->trans, f, 0))) { diff --git a/main/translate.c b/main/translate.c index 61b4a4686..553e70cde 100644 --- a/main/translate.c +++ b/main/translate.c @@ -40,31 +40,26 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cli.h" #include "asterisk/term.h" -#define MAX_RECALC 1000 /* max sample recalc */ +/*! \todo + * TODO: sample frames for each supported input format. + * We build this on the fly, by taking an SLIN frame and using + * the existing converter to play with it. + */ + +/*! max sample recalc */ +#define MAX_RECALC 1000 /*! \brief the list of translators */ static AST_RWLIST_HEAD_STATIC(translators, ast_translator); - -/*! \brief these values indicate how a translation path will affect the sample rate - * - * \note These must stay in this order. They are ordered by most optimal selection first. - */ -enum path_samp_change { - RATE_CHANGE_NONE = 0, /*!< path uses the same sample rate consistently */ - RATE_CHANGE_UPSAMP = 1, /*!< path will up the sample rate during a translation */ - RATE_CHANGE_DOWNSAMP = 2, /*!< path will have to down the sample rate during a translation. */ - RATE_CHANGE_UPSAMP_DOWNSAMP = 3, /*!< path will both up and down the sample rate during translation */ -}; - struct translator_path { - struct ast_translator *step; /*!< Next step translator */ - unsigned int cost; /*!< Complete cost to destination */ - unsigned int multistep; /*!< Multiple conversions required for this translation */ - enum path_samp_change rate_change; /*!< does this path require a sample rate change, if so what kind. */ + struct ast_translator *step; /*!< Next step translator */ + uint32_t table_cost; /*!< Complete table cost to destination */ + uint8_t multistep; /*!< Multiple conversions required for this translation */ }; -/*! \brief a matrix that, for any pair of supported formats, +/*! + * \brief a matrix that, for any pair of supported formats, * indicates the total cost of translation and the first step. * The full path can be reconstricted iterating on the matrix * until step->dstfmt == desired_format. @@ -74,27 +69,202 @@ struct translator_path { * Note: the lock in the 'translators' list is also used to protect * this structure. */ -static struct translator_path tr_matrix[MAX_FORMAT][MAX_FORMAT]; +static struct translator_path **__matrix; -/*! \todo - * TODO: sample frames for each supported input format. - * We build this on the fly, by taking an SLIN frame and using - * the existing converter to play with it. +/*! + * \brief table for converting index to format id values. + * + * \note this table is protected by the table_lock. */ +static int *__indextable; + +/*! protects the __indextable for resizing */ +static ast_rwlock_t tablelock; + +/* index size starts at this*/ +#define INIT_INDEX 32 +/* index size grows by this as necessary */ +#define GROW_INDEX 16 + +/*! the current largest index used by the __matrix and __indextable arrays*/ +static int cur_max_index; +/*! the largest index that can be used in either the __indextable or __matrix before resize must occur */ +static int index_size; + +static void matrix_rebuild(int samples); -/*! \brief returns the index of the lowest bit set */ -static force_inline int powerof(format_t d) +/*! + * \internal + * \brief converts format id to index value. + */ +static int format2index(enum ast_format_id id) { - int x = ffsll(d); + int x; + + ast_rwlock_rdlock(&tablelock); + for (x = 0; x < cur_max_index; x++) { + if (__indextable[x] == id) { + /* format already exists in index2format table */ + ast_rwlock_unlock(&tablelock); + return x; + } + } + ast_rwlock_unlock(&tablelock); + return -1; /* not found */ +} - if (x) - return x - 1; +/*! + * \internal + * \brief add a new format to the matrix and index table structures. + * + * \note it is perfectly safe to call this on formats already indexed. + * + * \retval 0, success + * \retval -1, matrix and index table need to be resized + */ +static int add_format2index(enum ast_format_id id) +{ + if (format2index(id) != -1) { + /* format is already already indexed */ + return 0; + } + + ast_rwlock_wrlock(&tablelock); + if (cur_max_index == (index_size)) { + ast_rwlock_unlock(&tablelock); + return -1; /* hit max length */ + } + __indextable[cur_max_index] = id; + cur_max_index++; + ast_rwlock_unlock(&tablelock); + + return 0; +} - ast_log(LOG_WARNING, "No bits set? %llu\n", (unsigned long long) d); +/*! + * \internal + * \brief converts index value back to format id + */ +static enum ast_format_id index2format(int index) +{ + enum ast_format_id format_id; + + if (index >= cur_max_index) { + return 0; + } + ast_rwlock_rdlock(&tablelock); + format_id = __indextable[index]; + ast_rwlock_unlock(&tablelock); + + return format_id; +} + +/*! + * \internal + * \brief resize both the matrix and index table so they can represent + * more translators + * + * \note _NO_ locks can be held prior to calling this function + * + * \retval 0, success + * \retval -1, failure. Old matrix and index table can still be used though + */ +static int matrix_resize(int init) +{ + struct translator_path **tmp_matrix = NULL; + int *tmp_table = NULL; + int old_index; + int x; + + AST_RWLIST_WRLOCK(&translators); + ast_rwlock_wrlock(&tablelock); + + old_index = index_size; + if (init) { + index_size += INIT_INDEX; + } else { + index_size += GROW_INDEX; + } + + /* make new 2d array of translator_path structures */ + if (!(tmp_matrix = ast_calloc(1, sizeof(struct translator_path *) * (index_size)))) { + goto resize_cleanup; + } + + for (x = 0; x < index_size; x++) { + if (!(tmp_matrix[x] = ast_calloc(1, sizeof(struct translator_path) * (index_size)))) { + goto resize_cleanup; + } + } + + /* make new index table */ + if (!(tmp_table = ast_calloc(1, sizeof(int) * index_size))) { + goto resize_cleanup; + } + + /* if everything went well this far, free the old and use the new */ + if (!init) { + for (x = 0; x < old_index; x++) { + ast_free(__matrix[x]); + } + ast_free(__matrix); + + memcpy(tmp_table, __indextable, sizeof(int) * old_index); + ast_free(__indextable); + } + + /* now copy them over */ + __matrix = tmp_matrix; + __indextable = tmp_table; + + matrix_rebuild(0); + ast_rwlock_unlock(&tablelock); + AST_RWLIST_UNLOCK(&translators); + + return 0; + +resize_cleanup: + ast_rwlock_unlock(&tablelock); + AST_RWLIST_UNLOCK(&translators); + if (tmp_matrix) { + for (x = 0; x < index_size; x++) { + ast_free(tmp_matrix[x]); + } + ast_free(tmp_matrix); + } + ast_free(tmp_table); return -1; } +/*! + * \internal + * \brief reinitialize the __matrix during matrix rebuild + * + * \note must be protected by the translators list lock + */ +static void matrix_clear(void) +{ + int x; + for (x = 0; x < index_size; x++) { + memset(__matrix[x], '\0', sizeof(struct translator_path) * (index_size)); + } +} + +/*! + * \internal + * \brief get a matrix entry + * + * \note This function must be protected by the translators list lock + */ +static struct translator_path *matrix_get(unsigned int x, unsigned int y) +{ + if (!(x >= 0 && y >= 0)) { + return NULL; + } + return __matrix[x] + y; +} + /* * wrappers around the translator routines. */ @@ -151,7 +321,7 @@ static int framein(struct ast_trans_pvt *pvt, struct ast_frame *f) { int ret; int samples = pvt->samples; /* initial value */ - + /* Copy the last in jb timing info to the pvt */ ast_copy_flags(&pvt->f, f, AST_FRFLAG_HAS_TIMING_INFO); pvt->f.ts = f->ts; @@ -193,23 +363,23 @@ struct ast_frame *ast_trans_frameout(struct ast_trans_pvt *pvt, { struct ast_frame *f = &pvt->f; - if (samples) + if (samples) { f->samples = samples; - else { + } else { if (pvt->samples == 0) return NULL; f->samples = pvt->samples; pvt->samples = 0; } - if (datalen) + if (datalen) { f->datalen = datalen; - else { + } else { f->datalen = pvt->datalen; pvt->datalen = 0; } f->frametype = AST_FRAME_VOICE; - f->subclass.codec = 1LL << (pvt->t->dstfmt); + ast_format_copy(&f->subclass.format, &pvt->t->dst_format); f->mallocd = 0; f->offset = AST_FRIENDLY_OFFSET; f->src = pvt->t->name; @@ -235,45 +405,56 @@ void ast_translator_free_path(struct ast_trans_pvt *p) } /*! \brief Build a chain of translators based upon the given source and dest formats */ -struct ast_trans_pvt *ast_translator_build_path(format_t dest, format_t source) +struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct ast_format *src) { struct ast_trans_pvt *head = NULL, *tail = NULL; - - source = powerof(source); - dest = powerof(dest); + 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); - if (source == -1 || dest == -1) { - ast_log(LOG_WARNING, "No translator path: (%s codec is not valid)\n", source == -1 ? "starting" : "ending"); + if (src_index == -1 || dst_index == -1) { + ast_log(LOG_WARNING, "No translator path: (%s codec is not valid)\n", src_index == -1 ? "starting" : "ending"); return NULL; } AST_RWLIST_RDLOCK(&translators); - while (source != dest) { + while (src_index != dst_index) { struct ast_trans_pvt *cur; - struct ast_translator *t = tr_matrix[source][dest].step; + struct ast_translator *t = matrix_get(src_index, dst_index)->step; if (!t) { - ast_log(LOG_WARNING, "No translator path from %s to %s\n", - ast_getformatname(source), ast_getformatname(dest)); + 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_RWLIST_UNLOCK(&translators); return NULL; } if (!(cur = newpvt(t))) { + int src_id = index2format(src_index); + int dst_id = index2format(dst_index); ast_log(LOG_WARNING, "Failed to build translator step from %s to %s\n", - ast_getformatname(source), ast_getformatname(dest)); - if (head) - ast_translator_free_path(head); + ast_getformatname(ast_format_set(&tmp_fmt1, src_id, 0)), + ast_getformatname(ast_format_set(&tmp_fmt2, dst_id, 0))); + if (head) { + ast_translator_free_path(head); + } AST_RWLIST_UNLOCK(&translators); return NULL; } - if (!head) + if (!head) { head = cur; - else + } else { tail->next = cur; + } tail = cur; cur->nextin = cur->nextout = ast_tv(0, 0); /* Keep going if this isn't the final destination */ - source = cur->t->dstfmt; + src_index = cur->t->dst_fmt_index; } AST_RWLIST_UNLOCK(&translators); @@ -284,7 +465,7 @@ struct ast_trans_pvt *ast_translator_build_path(format_t dest, format_t source) struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, int consume) { struct ast_trans_pvt *p = path; - struct ast_frame *out = f; + struct ast_frame *out; struct timeval delivery; int has_timing_info; long ts; @@ -296,7 +477,6 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, len = f->len; seqno = f->seqno; - /* XXX hmmm... check this below */ if (!ast_tvzero(f->delivery)) { if (!ast_tvzero(path->nextin)) { /* Make sure this is in line with what we were expecting */ @@ -316,31 +496,35 @@ 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.codec))); + path->nextin = ast_tvadd(path->nextin, ast_samp2tv(f->samples, ast_format_rate(&f->subclass.format))); } delivery = f->delivery; - for ( ; out && p ; p = p->next) { + for (out = f; out && p ; p = p->next) { framein(p, out); - if (out != f) + if (out != f) { ast_frfree(out); + } out = p->t->frameout(p); } - if (consume) + if (consume) { ast_frfree(f); - if (out == NULL) + } + if (out == NULL) { return NULL; + } /* we have a frame, play with times */ if (!ast_tvzero(delivery)) { /* Regenerate prediction after a discontinuity */ - if (ast_tvzero(path->nextout)) + if (ast_tvzero(path->nextout)) { path->nextout = ast_tvnow(); + } /* Use next predicted outgoing timestamp */ out->delivery = path->nextout; - + /* Predict next outgoing timestamp from samples in this frame. */ - path->nextout = ast_tvadd(path->nextout, ast_samp2tv(out->samples, ast_format_rate(out->subclass.codec))); + path->nextout = ast_tvadd(path->nextout, ast_samp2tv(out->samples, ast_format_rate(&out->subclass.format))); } else { out->delivery = ast_tv(0, 0); ast_set2_flag(out, has_timing_info, AST_FRFLAG_HAS_TIMING_INFO); @@ -351,35 +535,45 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, } } /* Invalidate prediction if we're entering a silence period */ - if (out->frametype == AST_FRAME_CNG) + if (out->frametype == AST_FRAME_CNG) { path->nextout = ast_tv(0, 0); + } return out; } -/*! \brief compute the cost of a single translation step */ -static void calc_cost(struct ast_translator *t, int seconds) +/*! + * \internal + * \brief Compute the computational cost of a single translation step. + * + * \note This function is only used to decide which translation path to + * use between two translators with identical src and dst formats. Computational + * cost acts only as a tie breaker. This is done so hardware translators + * can naturally have precedence over software translators. + */ +static void generate_computational_cost(struct ast_translator *t, int seconds) { int num_samples = 0; struct ast_trans_pvt *pvt; struct rusage start; struct rusage end; int cost; - int out_rate = ast_format_rate(t->dstfmt); + int out_rate = ast_format_rate(&t->dst_format); - if (!seconds) + if (!seconds) { seconds = 1; - + } + /* If they don't make samples, give them a terrible score */ if (!t->sample) { ast_log(LOG_WARNING, "Translator '%s' does not produce sample frames.\n", t->name); - t->cost = 999999; + t->comp_cost = 999999; return; } pvt = newpvt(t); if (!pvt) { ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name); - t->cost = 999999; + t->comp_cost = 999999; return; } @@ -391,7 +585,7 @@ static void calc_cost(struct ast_translator *t, int seconds) if (!f) { ast_log(LOG_WARNING, "Translator '%s' failed to produce a sample frame.\n", t->name); destroy(pvt); - t->cost = 999999; + t->comp_cost = 999999; return; } framein(pvt, f); @@ -409,72 +603,127 @@ static void calc_cost(struct ast_translator *t, int seconds) destroy(pvt); - t->cost = cost / seconds; + t->comp_cost = cost / seconds; - if (!t->cost) - t->cost = 1; + if (!t->comp_cost) { + t->comp_cost = 1; + } } -static enum path_samp_change get_rate_change_result(format_t src, format_t dst) +/*! + * \internal + * + * \brief If no table cost value was pre set by the translator. An attempt is made to + * automatically generate that cost value from the cost table based on our src and + * dst formats. + * + * \note This function allows older translators built before the translation cost + * changed away from using onely computational time to continue to be registered + * correctly. It is expected that translators built after the introduction of this + * function will manually assign their own table cost value. + * + * \note This function is safe to use on any audio formats that used to be defined in the + * first 64 bits of the old bit field codec representation. + * + * \retval Table Cost value greater than 0. + * \retval 0 on error. + */ +static int generate_table_cost(struct ast_format *src, struct ast_format *dst) { int src_rate = ast_format_rate(src); + int src_ll = 0; int dst_rate = ast_format_rate(dst); + int dst_ll = 0; - /* if src rate is less than dst rate, a sample upgrade is required */ - if (src_rate < dst_rate) { - return RATE_CHANGE_UPSAMP; + if ((AST_FORMAT_GET_TYPE(src->id) != AST_FORMAT_TYPE_AUDIO) || (AST_FORMAT_GET_TYPE(dst->id) != AST_FORMAT_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; } - - /* if src rate is larger than dst rate, a downgrade is required */ - if (src_rate > dst_rate) { - return RATE_CHANGE_DOWNSAMP; + if ((src->id == AST_FORMAT_SLINEAR) || (src->id == AST_FORMAT_SLINEAR16)) { + src_ll = 1; + } + if ((dst->id == AST_FORMAT_SLINEAR) || (dst->id == AST_FORMAT_SLINEAR16)) { + dst_ll = 1; } - return RATE_CHANGE_NONE; + if (src_ll) { + if (dst_ll && (src_rate == dst_rate)) { + return AST_TRANS_COST_LL_LL_ORIGSAMP; + } else if (!dst_ll && (src_rate == dst_rate)) { + return AST_TRANS_COST_LL_LY_ORIGSAMP; + } else if (dst_ll && (src_rate < dst_rate)) { + return AST_TRANS_COST_LL_LL_UPSAMP; + } else if (!dst_ll && (src_rate < dst_rate)) { + return AST_TRANS_COST_LL_LY_UPSAMP; + } else if (dst_ll && (src_rate > dst_rate)) { + return AST_TRANS_COST_LL_LL_DOWNSAMP; + } else if (!dst_ll && (src_rate > dst_rate)) { + return AST_TRANS_COST_LL_LY_DOWNSAMP; + } else { + return AST_TRANS_COST_LL_UNKNOWN; + } + } else { + if (dst_ll && (src_rate == dst_rate)) { + return AST_TRANS_COST_LY_LL_ORIGSAMP; + } else if (!dst_ll && (src_rate == dst_rate)) { + return AST_TRANS_COST_LY_LY_ORIGSAMP; + } else if (dst_ll && (src_rate < dst_rate)) { + return AST_TRANS_COST_LY_LL_UPSAMP; + } else if (!dst_ll && (src_rate < dst_rate)) { + return AST_TRANS_COST_LY_LY_UPSAMP; + } else if (dst_ll && (src_rate > dst_rate)) { + return AST_TRANS_COST_LY_LL_DOWNSAMP; + } else if (!dst_ll && (src_rate > dst_rate)) { + return AST_TRANS_COST_LY_LY_DOWNSAMP; + } else { + return AST_TRANS_COST_LY_UNKNOWN; + } + } } /*! * \brief rebuild a translation matrix. * \note This function expects the list of translators to be locked */ -static void rebuild_matrix(int samples) +static void matrix_rebuild(int samples) { struct ast_translator *t; - int new_rate_change; - int newcost; + int newtablecost; int x; /* source format index */ int y; /* intermediate format index */ int z; /* destination format index */ ast_debug(1, "Resetting translation matrix\n"); - memset(tr_matrix, '\0', sizeof(tr_matrix)); + matrix_clear(); /* first, compute all direct costs */ AST_RWLIST_TRAVERSE(&translators, t, list) { - if (!t->active) + if (!t->active) { continue; + } - x = t->srcfmt; - z = t->dstfmt; - - if (samples) - calc_cost(t, samples); + x = t->src_fmt_index; + z = t->dst_fmt_index; - new_rate_change = get_rate_change_result(1LL << t->srcfmt, 1LL << t->dstfmt); + if (samples) { + generate_computational_cost(t, samples); + } - /* this translator is the best choice if any of the below are true. + /* This new translator is the best choice if any of the below are true. * 1. no translation path is set between x and z yet. - * 2. the new translation costs less and sample rate is no worse than old one. - * 3. the new translation has a better sample rate conversion than the old one. + * 2. the new table cost is less. + * 3. the new computational cost is less. Computational cost is only used + * to break a tie between two identical translation paths. */ - if (!tr_matrix[x][z].step || - ((t->cost < tr_matrix[x][z].cost) && (new_rate_change <= tr_matrix[x][z].rate_change)) || - (new_rate_change < tr_matrix[x][z].rate_change)) { + if (!matrix_get(x, z)->step || + (t->table_cost < matrix_get(x, z)->step->table_cost) || + (t->comp_cost < matrix_get(x, z)->step->comp_cost)) { - tr_matrix[x][z].step = t; - tr_matrix[x][z].cost = t->cost; - tr_matrix[x][z].rate_change = new_rate_change; + matrix_get(x, z)->step = t; + matrix_get(x, z)->table_cost = t->table_cost; } } @@ -486,81 +735,43 @@ static void rebuild_matrix(int samples) */ for (;;) { int changed = 0; - int better_choice = 0; - for (x = 0; x < MAX_FORMAT; x++) { /* source format */ - for (y = 0; y < MAX_FORMAT; y++) { /* intermediate format */ - if (x == y) /* skip ourselves */ + for (x = 0; x < cur_max_index; x++) { /* source format */ + for (y = 0; y < cur_max_index; y++) { /* intermediate format */ + if (x == y) { /* skip ourselves */ continue; - for (z = 0; z < MAX_FORMAT; z++) { /* dst format */ - if (z == x || z == y) /* skip null conversions */ - continue; - if (!tr_matrix[x][y].step) /* no path from x to y */ - continue; - if (!tr_matrix[y][z].step) /* no path from y to z */ - continue; - - /* Does x->y->z result in a less optimal sample rate change? - * Never downgrade the sample rate conversion quality regardless - * of any cost improvements */ - if (tr_matrix[x][z].step && - ((tr_matrix[x][z].rate_change < tr_matrix[x][y].rate_change) || - (tr_matrix[x][z].rate_change < tr_matrix[y][z].rate_change))) { + } + for (z = 0; z < cur_max_index; z++) { /* dst format */ + if ((z == x || z == y) || /* skip null conversions */ + !matrix_get(x, y)->step || /* no path from x to y */ + !matrix_get(y, z)->step) { /* no path from y to z */ continue; } - /* is x->y->z a better sample rate confersion that the current x->z? */ - new_rate_change = tr_matrix[x][y].rate_change + tr_matrix[y][z].rate_change; - - /* calculate cost from x->y->z */ - newcost = tr_matrix[x][y].cost + tr_matrix[y][z].cost; - - /* Is x->y->z a better choice than x->z? - * There are three conditions for x->y->z to be a better choice than x->z - * 1. if there is no step directly between x->z then x->y->z is the best and only current option. - * 2. if x->y->z costs less and the sample rate conversion is no less optimal. - * 3. if x->y->z results in a more optimal sample rate conversion. */ - if (!tr_matrix[x][z].step) { - better_choice = 1; - } else if ((newcost < tr_matrix[x][z].cost) && (new_rate_change <= tr_matrix[x][z].rate_change)) { - better_choice = 1; - } else if (new_rate_change < tr_matrix[x][z].rate_change) { - better_choice = 1; - } else { - better_choice = 0; + /* calculate table cost from x->y->z */ + newtablecost = matrix_get(x, y)->table_cost + matrix_get(y, z)->table_cost; + + /* 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(3, "Discovered %d 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 (!better_choice) { - continue; - } - /* ok, we can get from x to z via y with a cost that - is the sum of the transition from x to y and from y to z */ - tr_matrix[x][z].step = tr_matrix[x][y].step; - tr_matrix[x][z].cost = newcost; - tr_matrix[x][z].multistep = 1; - - /* now calculate what kind of sample rate change is required for this multi-step path - * - * if both paths require a change in rate, and they are not in the same direction - * then this is a up sample down sample conversion scenario. */ - if ((tr_matrix[x][y].rate_change > RATE_CHANGE_NONE) && - (tr_matrix[y][z].rate_change > RATE_CHANGE_NONE) && - (tr_matrix[x][y].rate_change != tr_matrix[y][z].rate_change)) { - - tr_matrix[x][z].rate_change = RATE_CHANGE_UPSAMP_DOWNSAMP; - } else { - /* else just set the rate change to whichever is worse */ - tr_matrix[x][z].rate_change = tr_matrix[x][y].rate_change > tr_matrix[y][z].rate_change - ? tr_matrix[x][y].rate_change : tr_matrix[y][z].rate_change; - } - - ast_debug(3, "Discovered %d cost path from %s to %s, via %s\n", tr_matrix[x][z].cost, - ast_getformatname(1LL << x), ast_getformatname(1LL << z), ast_getformatname(1LL << y)); - changed++; } } } - if (!changed) + if (!changed) { break; + } } } @@ -572,11 +783,11 @@ const char *ast_translate_path_to_str(struct ast_trans_pvt *p, struct ast_str ** return ""; } - ast_str_set(str, 0, "%s", ast_getformatname(1LL << p->t->srcfmt)); + ast_str_set(str, 0, "%s", ast_getformatname(&p->t->src_format)); while ( (p = pn) ) { pn = p->next; - ast_str_append(str, 0, "->%s", ast_getformatname(1LL << p->t->dstfmt)); + ast_str_append(str, 0, "->%s", ast_getformatname(&p->t->dst_format)); } return ast_str_buffer(*str); @@ -592,7 +803,7 @@ static char *complete_trans_path_choice(const char *line, const char *word, int const struct ast_format_list *format_list = ast_get_format_list(&len); for (i = 0; i < len; i++) { - if (!(format_list[i].bits & AST_FORMAT_AUDIO_MASK)) { + if (AST_FORMAT_GET_TYPE(format_list[i].id) != AST_FORMAT_TYPE_AUDIO) { continue; } if (!strncasecmp(word, format_list[i].name, wordlen) && ++which > state) { @@ -603,165 +814,81 @@ static char *complete_trans_path_choice(const char *line, const char *word, int return ret; } -static char *handle_cli_core_show_translation(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +static void handle_cli_recalc(struct ast_cli_args *a) { -#define SHOW_TRANS 64 - static const char * const option1[] = { "recalc", "paths", NULL }; - int x, y, z; - int curlen = 0, longest = 0, magnitude[SHOW_TRANS] = { 0, }; + int time = a->argv[4] ? atoi(a->argv[4]) : 1; - switch (cmd) { - case CLI_INIT: - e->command = "core show translation"; - e->usage = - "Usage: 'core show translation' can be used in two ways.\n" - " 1. 'core show translation [recalc [<recalc seconds>]]\n" - " Displays known codec translators and the cost associated\n" - " 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"; - return NULL; - case CLI_GENERATE: - if (a->pos == 3) { - return ast_cli_complete(a->word, option1, a->n); - } - if (a->pos == 4 && !strcasecmp(a->argv[3], option1[1])) { - return complete_trans_path_choice(a->line, a->word, a->pos, a->n); - } - return NULL; + if (time <= 0) { + ast_cli(a->fd, " Recalc must be greater than 0. Defaulting to 1.\n"); + time = 1; } - if (a->argc > 5) - return CLI_SHOWUSAGE; - - if (a->argv[3] && !strcasecmp(a->argv[3], option1[1]) && a->argc == 5) { - format_t input_src = 0; - format_t src = 0; - size_t len = 0; - int dst; - int i; - const struct ast_format_list *format_list = ast_get_format_list(&len); - struct ast_str *str = ast_str_alloca(256); - struct ast_translator *step; - - for (i = 0; i < len; i++) { - if (!(format_list[i].bits & AST_FORMAT_AUDIO_MASK)) { - continue; - } - if (!strncasecmp(format_list[i].name, a->argv[4], strlen(format_list[i].name))) { - input_src = format_list[i].bits; - } - } - - if (!input_src) { - ast_cli(a->fd, "Source codec \"%s\" is not found.\n", a->argv[4]); - 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)); - for (i = 0; i < len; i++) { - if (!(format_list[i].bits & AST_FORMAT_AUDIO_MASK) || (format_list[i].bits == input_src)) { - continue; - } - dst = powerof(format_list[i].bits); - src = powerof(input_src); - ast_str_reset(str); - if (tr_matrix[src][dst].step) { - ast_str_append(&str, 0, "%s", ast_getformatname(1LL << tr_matrix[src][dst].step->srcfmt)); - while (src != dst) { - step = tr_matrix[src][dst].step; - if (!step) { - ast_str_reset(str); - break; - } - ast_str_append(&str, 0, "->%s", ast_getformatname(1LL << step->dstfmt)); - src = step->dstfmt; - } - } - - 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)); - } - AST_RWLIST_UNLOCK(&translators); - - return CLI_SUCCESS; - } else if (a->argv[3] && !strcasecmp(a->argv[3], "recalc")) { - z = a->argv[4] ? atoi(a->argv[4]) : 1; - - if (z <= 0) { - ast_cli(a->fd, " Recalc must be greater than 0. Defaulting to 1.\n"); - z = 1; - } - - if (z > MAX_RECALC) { - ast_cli(a->fd, " Maximum limit of recalc exceeded by %d, truncating value to %d\n", z - MAX_RECALC, MAX_RECALC); - z = MAX_RECALC; - } - ast_cli(a->fd, " Recalculating Codec Translation (number of sample seconds: %d)\n\n", z); - AST_RWLIST_WRLOCK(&translators); - rebuild_matrix(z); - AST_RWLIST_UNLOCK(&translators); - } else if (a->argc > 3) - return CLI_SHOWUSAGE; + if (time > MAX_RECALC) { + ast_cli(a->fd, " Maximum limit of recalc exceeded by %d, truncating value to %d\n", time - MAX_RECALC, MAX_RECALC); + time = MAX_RECALC; + } + ast_cli(a->fd, " Recalculating Codec Translation (number of sample seconds: %d)\n\n", time); + AST_RWLIST_WRLOCK(&translators); + matrix_rebuild(time); + AST_RWLIST_UNLOCK(&translators); +} +static char *handle_show_translation_table(struct ast_cli_args *a) +{ + int x, y; + int curlen = 0, longest = 0; + struct ast_format tmp_fmt; 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 (x = 0; x < SHOW_TRANS; x++) { + for (x = 0; x < cur_max_index; x++) { /* translation only applies to audio right now. */ - if (!(AST_FORMAT_AUDIO_MASK & (1LL << (x)))) + if (AST_FORMAT_GET_TYPE(index2format(x)) != AST_FORMAT_TYPE_AUDIO) continue; - curlen = strlen(ast_getformatname(1LL << (x))); - if (curlen > longest) + curlen = strlen(ast_getformatname(ast_format_set(&tmp_fmt, index2format(x), 0))); + if (curlen > longest) { longest = curlen; - for (y = 0; y < SHOW_TRANS; y++) { - if (!(AST_FORMAT_AUDIO_MASK & (1LL << (y)))) - continue; - if (tr_matrix[x][y].cost > pow(10, magnitude[x])) { - magnitude[y] = floor(log10(tr_matrix[x][y].cost)); - } } } - for (x = -1; x < SHOW_TRANS; x++) { + + for (x = -1; x < cur_max_index; x++) { struct ast_str *out = ast_str_alloca(256); /* translation only applies to audio right now. */ - if (x >= 0 && !(AST_FORMAT_AUDIO_MASK & (1LL << (x)))) + if (x >= 0 && (AST_FORMAT_GET_TYPE(index2format(x)) != AST_FORMAT_TYPE_AUDIO)) { continue; + } /*Go ahead and move to next iteration if dealing with an unknown codec*/ - if(x >= 0 && !strcmp(ast_getformatname(1LL << (x)), "unknown")) + if (x >= 0 && !strcmp(ast_getformatname(ast_format_set(&tmp_fmt, index2format(x), 0)), "unknown")) { continue; + } ast_str_set(&out, -1, " "); - for (y = -1; y < SHOW_TRANS; y++) { + for (y = -1; y < cur_max_index; y++) { /* translation only applies to audio right now. */ - if (y >= 0 && !(AST_FORMAT_AUDIO_MASK & (1LL << (y)))) + if (y >= 0 && (AST_FORMAT_GET_TYPE(index2format(y)) != AST_FORMAT_TYPE_AUDIO)) { continue; + } /*Go ahead and move to next iteration if dealing with an unknown codec*/ - if (y >= 0 && !strcmp(ast_getformatname(1LL << (y)), "unknown")) + if (y >= 0 && !strcmp(ast_getformatname(ast_format_set(&tmp_fmt, index2format(y), 0)), "unknown")) { continue; - if (y >= 0) - curlen = strlen(ast_getformatname(1LL << (y))); - if (y >= 0 && magnitude[y] + 1 > curlen) { - curlen = magnitude[y] + 1; } - if (curlen < 5) + if (y >= 0) { + curlen = strlen(ast_getformatname(ast_format_set(&tmp_fmt, index2format(y), 0))); + } + if (curlen < 5) { curlen = 5; - if (x >= 0 && y >= 0 && tr_matrix[x][y].step) { + } + + if (x >= 0 && y >= 0 && matrix_get(x, y)->step) { /* Actual codec output */ - ast_str_append(&out, -1, "%*d", curlen + 1, tr_matrix[x][y].cost); + ast_str_append(&out, -1, "%*d", curlen + 1, (matrix_get(x, y)->table_cost/100)); } else if (x == -1 && y >= 0) { /* Top row - use a dynamic size */ - ast_str_append(&out, -1, "%*s", curlen + 1, ast_getformatname(1LL << (y)) ); + ast_str_append(&out, -1, "%*s", curlen + 1, ast_getformatname(ast_format_set(&tmp_fmt, index2format(y), 0))); } else if (y == -1 && x >= 0) { /* Left column - use a static size. */ - ast_str_append(&out, -1, "%*s", longest, ast_getformatname(1LL << (x)) ); + ast_str_append(&out, -1, "%*s", longest, ast_getformatname(ast_format_set(&tmp_fmt, index2format(x), 0))); } else if (x >= 0 && y >= 0) { /* Codec not supported */ ast_str_append(&out, -1, "%*s", curlen + 1, "-"); @@ -777,6 +904,106 @@ static char *handle_cli_core_show_translation(struct ast_cli_entry *e, int cmd, return CLI_SUCCESS; } +static char *handle_show_translation_path(struct ast_cli_args *a) +{ + struct ast_format input_src_format; + size_t len = 0; + int i; + const struct ast_format_list *format_list = ast_get_format_list(&len); + struct ast_str *str = ast_str_alloca(256); + struct ast_translator *step; + + ast_format_clear(&input_src_format); + + for (i = 0; i < len; i++) { + if (AST_FORMAT_GET_TYPE(format_list[i].id) != AST_FORMAT_TYPE_AUDIO) { + continue; + } + if (!strncasecmp(format_list[i].name, a->argv[4], strlen(format_list[i].name))) { + ast_format_set(&input_src_format, format_list[i].id, 0); + } + } + + if (!input_src_format.id) { + ast_cli(a->fd, "Source codec \"%s\" is not found.\n", a->argv[4]); + 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].id) != AST_FORMAT_TYPE_AUDIO) || (format_list[i].id == input_src_format.id)) { + continue; + } + dst = format2index(format_list[i].id); + src = format2index(input_src_format.id); + ast_str_reset(str); + if ((len >= cur_max_index) && (src != -1) && (dst != -1) && matrix_get(src, dst)->step) { + ast_str_append(&str, 0, "%s", ast_getformatname(&matrix_get(src, dst)->step->src_format)); + while (src != dst) { + step = matrix_get(src, dst)->step; + if (!step) { + ast_str_reset(str); + break; + } + ast_str_append(&str, 0, "->%s", ast_getformatname(&step->dst_format)); + 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)); + } + AST_RWLIST_UNLOCK(&translators); + + return CLI_SUCCESS; +} + +static char *handle_cli_core_show_translation(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + static const char * const option[] = { "recalc", "paths", NULL }; + + switch (cmd) { + case CLI_INIT: + e->command = "core show translation"; + e->usage = + "Usage: 'core show translation' can be used in two ways.\n" + " 1. 'core show translation [recalc [<recalc seconds>]]\n" + " Displays known codec translators and the cost associated\n" + " 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"; + return NULL; + case CLI_GENERATE: + if (a->pos == 3) { + return ast_cli_complete(a->word, option, a->n); + } + if (a->pos == 4 && !strcasecmp(a->argv[3], option[1])) { + return complete_trans_path_choice(a->line, a->word, a->pos, a->n); + } + return NULL; + } + + if (a->argc > 5) + return CLI_SHOWUSAGE; + + if (a->argv[3] && !strcasecmp(a->argv[3], option[1]) && a->argc == 5) { /* show paths */ + return handle_show_translation_path(a); + } 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 */ + return CLI_SHOWUSAGE; + } + + return handle_show_translation_table(a); +} + static struct ast_cli_entry cli_translate[] = { AST_CLI_DEFINE(handle_cli_core_show_translation, "Display translation matrix") }; @@ -784,10 +1011,18 @@ static struct ast_cli_entry cli_translate[] = { /*! \brief register codec translator */ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) { - static int added_cli = 0; struct ast_translator *u; char tmp[80]; + if (add_format2index(t->src_format.id) || add_format2index(t->dst_format.id)) { + 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); + } + if (!mod) { ast_log(LOG_WARNING, "Missing module pointer, you need to supply one\n"); return -1; @@ -797,24 +1032,28 @@ 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))) { + 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->srcfmt = powerof(t->srcfmt); - t->dstfmt = powerof(t->dstfmt); + t->src_fmt_index = format2index(t->src_format.id); + t->dst_fmt_index = format2index(t->dst_format.id); t->active = 1; - if (t->srcfmt == -1 || t->dstfmt == -1) { - ast_log(LOG_WARNING, "Invalid translator path: (%s codec is not valid)\n", t->srcfmt == -1 ? "starting" : "ending"); + if (t->src_fmt_index == -1 || t->dst_fmt_index == -1) { + ast_log(LOG_WARNING, "Invalid translator path: (%s codec is not valid)\n", t->src_fmt_index == -1 ? "starting" : "ending"); return -1; } - if (t->srcfmt >= MAX_FORMAT) { - ast_log(LOG_WARNING, "Source format %s is larger than MAX_FORMAT\n", ast_getformatname(t->srcfmt)); + 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)); return -1; } - if (t->dstfmt >= MAX_FORMAT) { - ast_log(LOG_WARNING, "Destination format %s is larger than MAX_FORMAT\n", ast_getformatname(t->dstfmt)); + 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)); return -1; } @@ -829,28 +1068,24 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) t->buf_size = ((t->buf_size + align - 1) / align) * align; } - if (t->frameout == NULL) + if (t->frameout == NULL) { t->frameout = default_frameout; - - calc_cost(t, 1); + } - ast_verb(2, "Registered translator '%s' from format %s to %s, cost %d\n", - term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)), - ast_getformatname(1LL << t->srcfmt), ast_getformatname(1LL << t->dstfmt), t->cost); + generate_computational_cost(t, 1); - if (!added_cli) { - ast_cli_register_multiple(cli_translate, ARRAY_LEN(cli_translate)); - added_cli++; - } + 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_RWLIST_WRLOCK(&translators); /* find any existing translators that provide this same srcfmt/dstfmt, - and put this one in order based on cost */ + and put this one in order based on computational cost */ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) { - if ((u->srcfmt == t->srcfmt) && - (u->dstfmt == t->dstfmt) && - (u->cost > t->cost)) { + if ((u->src_fmt_index == t->src_fmt_index) && + (u->dst_fmt_index == t->dst_fmt_index) && + (u->comp_cost > t->comp_cost)) { AST_RWLIST_INSERT_BEFORE_CURRENT(t, list); t = NULL; break; @@ -860,10 +1095,11 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) /* if no existing translator was found for this format combination, add it to the beginning of the list */ - if (t) + if (t) { AST_RWLIST_INSERT_HEAD(&translators, t, list); + } - rebuild_matrix(0); + matrix_rebuild(0); AST_RWLIST_UNLOCK(&translators); @@ -881,15 +1117,19 @@ 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", term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)), ast_getformatname(1LL << t->srcfmt), ast_getformatname(1LL << t->dstfmt)); + ast_verb(2, "Unregistered translator '%s' from format %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)); found = 1; break; } } AST_RWLIST_TRAVERSE_SAFE_END; - if (found) - rebuild_matrix(0); + if (found) { + matrix_rebuild(0); + } AST_RWLIST_UNLOCK(&translators); @@ -900,7 +1140,7 @@ void ast_translator_activate(struct ast_translator *t) { AST_RWLIST_WRLOCK(&translators); t->active = 1; - rebuild_matrix(0); + matrix_rebuild(0); AST_RWLIST_UNLOCK(&translators); } @@ -908,93 +1148,93 @@ void ast_translator_deactivate(struct ast_translator *t) { AST_RWLIST_WRLOCK(&translators); t->active = 0; - rebuild_matrix(0); + matrix_rebuild(0); AST_RWLIST_UNLOCK(&translators); } /*! \brief Calculate our best translator source format, given costs, and a desired destination */ -format_t ast_translator_best_choice(format_t *dst, format_t *srcs) +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) { - int x,y; - int better = 0; - int besttime = INT_MAX; - int beststeps = INT_MAX; - unsigned int best_rate_change = INT_MAX; - format_t best = -1; - format_t bestdst = 0; - format_t cur, cursrc; - format_t common = ((*dst) & (*srcs)) & AST_FORMAT_AUDIO_MASK; /* are there common formats ? */ - - if (common) { /* yes, pick one and return */ - for (cur = 1, y = 0; y <= MAX_AUDIO_FORMAT; cur <<= 1, y++) { - if (!(cur & common)) { - continue; - } - + unsigned int besttablecost = INT_MAX; + unsigned int beststeps = INT_MAX; + struct ast_format best; + struct ast_format bestdst; + struct ast_format_cap *joint_cap = ast_format_cap_joint(dst_cap, src_cap); + ast_format_clear(&best); + ast_format_clear(&bestdst); + + if (joint_cap) { /* yes, pick one and return */ + struct ast_format tmp_fmt; + 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 == -1) { - best = cur; + 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(cur)) { - best = cur; + if (ast_format_rate(&best) < ast_format_rate(&tmp_fmt)) { + ast_format_copy(&best, &tmp_fmt); continue; } + } + ast_format_cap_iter_end(joint_cap); + /* We are done, this is a common format to both. */ - *srcs = *dst = best; + ast_format_copy(dst_fmt_out, &best); + ast_format_copy(src_fmt_out, &best); + ast_format_cap_destroy(joint_cap); return 0; } else { /* No, we will need to translate */ + struct ast_format cur_dst; + struct ast_format cur_src; AST_RWLIST_RDLOCK(&translators); - for (cur = 1, y = 0; y <= MAX_AUDIO_FORMAT; cur <<= 1, y++) { - if (! (cur & *dst)) { - continue; - } - for (cursrc = 1, x = 0; x <= MAX_AUDIO_FORMAT; cursrc <<= 1, x++) { - if (!(*srcs & cursrc) || !tr_matrix[x][y].step) { - continue; - } - /* This is a better choice if any of the following are true. - * 1. The sample rate conversion is better than the current pick. - * 2. the sample rate conversion is no worse than the current pick and the cost or multistep is better - */ - better = 0; - if (tr_matrix[x][y].rate_change < best_rate_change) { - better = 1; /* this match has a better rate conversion */ + 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); + if (x < 0 || y < 0) { + continue; } - if ((tr_matrix[x][y].rate_change <= best_rate_change) && - (tr_matrix[x][y].cost < besttime || tr_matrix[x][y].multistep < beststeps)) { - better = 1; /* this match has no worse rate conversion and the conversion cost is less */ + if (!matrix_get(x, y) || !(matrix_get(x, y)->step)) { + continue; } - if (better) { + if (((matrix_get(x, y)->table_cost < besttablecost) || (matrix_get(x, y)->multistep < beststeps))) { /* better than what we have so far */ - best = cursrc; - bestdst = cur; - besttime = tr_matrix[x][y].cost; - beststeps = tr_matrix[x][y].multistep; - best_rate_change = tr_matrix[x][y].rate_change; + ast_format_copy(&best, &cur_src); + ast_format_copy(&bestdst, &cur_dst); + besttablecost = matrix_get(x, y)->table_cost; + beststeps = matrix_get(x, y)->multistep; } } + ast_format_cap_iter_end(src_cap); } + + ast_format_cap_iter_end(dst_cap); AST_RWLIST_UNLOCK(&translators); - if (best > -1) { - *srcs = best; - *dst = bestdst; - best = 0; + if (best.id) { + ast_format_copy(dst_fmt_out, &bestdst); + ast_format_copy(src_fmt_out, &best); + return 0; } - return best; + return -1; } } -unsigned int ast_translate_path_steps(format_t dest, format_t src) +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 = powerof(src); - dest = powerof(dest); + src = format2index(src_format->id); + dest = format2index(dst_format->id); if (src == -1 || dest == -1) { ast_log(LOG_WARNING, "No translator path: (%s codec is not valid)\n", src == -1 ? "starting" : "ending"); @@ -1002,97 +1242,123 @@ unsigned int ast_translate_path_steps(format_t dest, format_t src) } AST_RWLIST_RDLOCK(&translators); - if (tr_matrix[src][dest].step) - res = tr_matrix[src][dest].multistep + 1; + if (matrix_get(src, dest)->step) { + res = matrix_get(src, dest)->multistep + 1; + } AST_RWLIST_UNLOCK(&translators); return res; } -format_t ast_translate_available_formats(format_t dest, format_t src) +void ast_translate_available_formats(struct ast_format_cap *dest, struct ast_format_cap *src, struct ast_format_cap *result) { - format_t res = dest; - format_t x; - format_t src_audio = src & AST_FORMAT_AUDIO_MASK; - format_t src_video = src & AST_FORMAT_VIDEO_MASK; + struct ast_format tmp_fmt; + struct ast_format cur_src; + int src_audio = 0; + int src_video = 0; + int index; + + ast_format_cap_copy(result, dest); /* if we don't have a source format, we just have to try all possible destination formats */ - if (!src) - return dest; + if (!src) { + return; + } - /* If we have a source audio format, get its format index */ - if (src_audio) - src_audio = powerof(src_audio); + 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 (src_video) - src_video = powerof(src_video); + /* 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); + } - AST_RWLIST_RDLOCK(&translators); + 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 (x = 1LL; src_audio && x > 0; x <<= 1) { - if (!(x & AST_FORMAT_AUDIO_MASK)) { - continue; - } + /* 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 this is not a desired format, nothing to do */ - if (!(dest & x)) - continue; + if (AST_FORMAT_GET_TYPE(tmp_fmt.id) != AST_FORMAT_TYPE_AUDIO) { + continue; + } - /* if the source is supplying this format, then - we can leave it in the result */ - if (src & x) - continue; + /* if this is not a desired format, nothing to do */ + if (!ast_format_cap_iscompatible(dest, &tmp_fmt)) { + continue; + } - /* if we don't have a translation path from the src - to this format, remove it from the result */ - if (!tr_matrix[src_audio][powerof(x)].step) { - res &= ~x; - 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; + } - /* now check the opposite direction */ - if (!tr_matrix[powerof(x)][src_audio].step) - res &= ~x; - } + /* 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; + } - /* 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 (x = 1LL; src_video && x > 0; x <<= 1) { - if (!(x & AST_FORMAT_VIDEO_MASK)) { - continue; + /* now check the opposite direction */ + if (!matrix_get(index, src_audio)->step) { + ast_format_cap_remove_byid(result, tmp_fmt.id); + } } - /* if this is not a desired format, nothing to do */ - if (!(dest & x)) - continue; + /* 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 the source is supplying this format, then - we can leave it in the result */ - if (src & x) - continue; + /* if this is not a desired format, nothing to do */ + if (!ast_format_cap_iscompatible(dest, &tmp_fmt)) { + continue; + } - /* if we don't have a translation path from the src - to this format, remove it from the result */ - if (!tr_matrix[src_video][powerof(x)].step) { - res &= ~x; - 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; + } - /* now check the opposite direction */ - if (!tr_matrix[powerof(x)][src_video].step) - res &= ~x; - } + /* 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; + } - AST_RWLIST_UNLOCK(&translators); + /* now check the opposite direction */ + if (!matrix_get(index, src_video)->step) { + ast_format_cap_remove_byid(result, tmp_fmt.id); + } + } + AST_RWLIST_UNLOCK(&translators); + } + ast_format_cap_iter_end(src); +} +int ast_translate_init(void) +{ + int res = 0; + ast_rwlock_init(&tablelock); + res = matrix_resize(1); + res |= ast_cli_register_multiple(cli_translate, ARRAY_LEN(cli_translate)); return res; } diff --git a/main/udptl.c b/main/udptl.c index 1845ca882..f5abbb900 100644 --- a/main/udptl.c +++ b/main/udptl.c @@ -379,7 +379,7 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, unsigned int len) /* Decode the secondary IFP packet */ //fprintf(stderr, "Secondary %d, len %d\n", seq_no - i, lengths[i - 1]); s->f[ifp_no].frametype = AST_FRAME_MODEM; - s->f[ifp_no].subclass.codec = AST_MODEM_T38; + s->f[ifp_no].subclass.integer = AST_MODEM_T38; s->f[ifp_no].mallocd = 0; s->f[ifp_no].seqno = seq_no - i; @@ -481,7 +481,7 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, unsigned int len) if (repaired[l]) { //fprintf(stderr, "Fixed packet %d, len %d\n", j, l); s->f[ifp_no].frametype = AST_FRAME_MODEM; - s->f[ifp_no].subclass.codec = AST_MODEM_T38; + s->f[ifp_no].subclass.integer = AST_MODEM_T38; s->f[ifp_no].mallocd = 0; s->f[ifp_no].seqno = j; @@ -502,7 +502,7 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, unsigned int len) if (seq_no >= s->rx_seq_no) { /* Decode the primary IFP packet */ s->f[ifp_no].frametype = AST_FRAME_MODEM; - s->f[ifp_no].subclass.codec = AST_MODEM_T38; + s->f[ifp_no].subclass.integer = AST_MODEM_T38; s->f[ifp_no].mallocd = 0; s->f[ifp_no].seqno = seq_no; @@ -1057,7 +1057,7 @@ int ast_udptl_write(struct ast_udptl *s, struct ast_frame *f) return 0; if ((f->frametype != AST_FRAME_MODEM) || - (f->subclass.codec != AST_MODEM_T38)) { + (f->subclass.integer != AST_MODEM_T38)) { ast_log(LOG_WARNING, "(%s): UDPTL can only send T.38 data.\n", LOG_TAG(s)); return -1; |