diff options
Diffstat (limited to 'main')
38 files changed, 4390 insertions, 3911 deletions
diff --git a/main/abstract_jb.c b/main/abstract_jb.c index 7841b6a4e..85c188e88 100644 --- a/main/abstract_jb.c +++ b/main/abstract_jb.c @@ -360,7 +360,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_format_get_default_ms(jb->last_format); res = jbimpl->get(jbobj, &f, now, interpolation_len); @@ -371,13 +371,13 @@ static void jb_get_and_deliver(struct ast_channel *chan) case AST_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); - ast_format_copy(&jb->last_format, &f->subclass.format); + ao2_replace(jb->last_format, f->subclass.format); ast_frfree(f); break; case AST_JB_IMPL_INTERP: /* interpolate a frame */ f = &finterp; - ast_format_copy(&f->subclass.format, &jb->last_format); + 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)); @@ -437,7 +437,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. */ - ast_format_copy(&jb->last_format, &frr->subclass.format); + jb->last_format = ao2_bump(frr->subclass.format); /* Create a frame log file */ if (ast_test_flag(jbconf, AST_JB_LOG)) { @@ -502,6 +502,8 @@ void ast_jb_destroy(struct ast_channel *chan) jb->logfile = NULL; } + ao2_cleanup(jb->last_format); + if (ast_test_flag(jb, JB_CREATED)) { /* Remove and free all frames still queued in jb */ while (jbimpl->remove(jbobj, &f) == AST_JB_IMPL_OK) { @@ -820,7 +822,7 @@ struct jb_framedata { const struct ast_jb_impl *jb_impl; struct ast_jb_conf jb_conf; struct timeval start_tv; - struct ast_format last_format; + struct ast_format *last_format; struct ast_timer *timer; int timer_interval; /* ms between deliveries */ int timer_fd; @@ -842,6 +844,7 @@ static void jb_framedata_destroy(struct jb_framedata *framedata) framedata->jb_impl->destroy(framedata->jb_obj); framedata->jb_obj = NULL; } + ao2_cleanup(framedata->last_format); ast_free(framedata); } @@ -909,7 +912,7 @@ static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_fram } jbframe = ast_frisolate(frame); - ast_format_copy(&framedata->last_format, &frame->subclass.format); + ao2_replace(framedata->last_format, frame->subclass.format); if (frame->len && (frame->len != framedata->timer_interval)) { framedata->timer_interval = frame->len; @@ -959,12 +962,12 @@ static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_fram frame = &ast_null_frame; break; case AST_JB_IMPL_INTERP: - if (framedata->last_format.id) { + if (framedata->last_format) { struct ast_frame tmp = { 0, }; tmp.frametype = AST_FRAME_VOICE; - ast_format_copy(&tmp.subclass.format, &framedata->last_format); + tmp.subclass.format = framedata->last_format; /* example: 8000hz / (1000 / 20ms) = 160 samples */ - tmp.samples = ast_format_rate(&framedata->last_format) / (1000 / framedata->timer_interval); + tmp.samples = ast_format_get_sample_rate(framedata->last_format) / (1000 / framedata->timer_interval); tmp.delivery = ast_tvadd(framedata->start_tv, ast_samp2tv(next, 1000)); tmp.offset = AST_FRIENDLY_OFFSET; tmp.src = "func_jitterbuffer interpolation"; diff --git a/main/app.c b/main/app.c index 822fe6c45..fa7c3ece1 100644 --- a/main/app.c +++ b/main/app.c @@ -70,6 +70,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/stasis.h" #include "asterisk/stasis_channels.h" #include "asterisk/json.h" +#include "asterisk/format_cache.h" #define MWI_TOPIC_BUCKETS 57 @@ -880,16 +881,18 @@ struct linear_state { int fd; int autoclose; int allowoverride; - struct ast_format origwfmt; + struct ast_format *origwfmt; }; static void linear_release(struct ast_channel *chan, void *params) { struct linear_state *ls = params; - if (ls->origwfmt.id && ast_set_write_format(chan, &ls->origwfmt)) { - ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%u'\n", ast_channel_name(chan), ls->origwfmt.id); + if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt)) { + ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", + ast_channel_name(chan), ast_format_get_name(ls->origwfmt)); } + ao2_cleanup(ls->origwfmt); if (ls->autoclose) { close(ls->fd); @@ -909,7 +912,7 @@ static int linear_generator(struct ast_channel *chan, void *data, int len, int s }; int res; - ast_format_set(&f.subclass.format, AST_FORMAT_SLINEAR, 0); + f.subclass.format = ast_format_slin; len = samples * 2; if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) { @@ -943,10 +946,11 @@ static void *linear_alloc(struct ast_channel *chan, void *params) ast_clear_flag(ast_channel_flags(chan), AST_FLAG_WRITE_INT); } - ast_format_copy(&ls->origwfmt, ast_channel_writeformat(chan)); + ls->origwfmt = ao2_bump(ast_channel_writeformat(chan)); - if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR)) { + if (ast_set_write_format(chan, ast_format_slin)) { ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", ast_channel_name(chan)); + ao2_cleanup(ls->origwfmt); ast_free(ls); ls = params = NULL; } @@ -1358,7 +1362,7 @@ static struct ast_frame *make_silence(const struct ast_frame *orig) return NULL; } - if (orig->subclass.format.id != AST_FORMAT_SLINEAR) { + if (ast_format_cmp(orig->subclass.format, ast_format_slin) == AST_FORMAT_CMP_NOT_EQUAL) { ast_log(LOG_WARNING, "Attempting to silence non-slin frame\n"); return NULL; } @@ -1385,7 +1389,7 @@ static struct ast_frame *make_silence(const struct ast_frame *orig) silence->samples = samples; silence->datalen = datalen; - ast_format_set(&silence->subclass.format, AST_FORMAT_SLINEAR, 0); + silence->subclass.format = ast_format_slin; return silence; } @@ -1400,13 +1404,13 @@ static struct ast_frame *make_silence(const struct ast_frame *orig) * \return 0 on success. * \return -1 on error. */ -static int set_read_to_slin(struct ast_channel *chan, struct ast_format *orig_format) +static int set_read_to_slin(struct ast_channel *chan, struct ast_format **orig_format) { if (!chan || !orig_format) { return -1; } - ast_format_copy(orig_format, ast_channel_readformat(chan)); - return ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR); + *orig_format = ao2_bump(ast_channel_readformat(chan)); + return ast_set_read_format(chan, ast_format_slin); } static int global_silence_threshold = 128; @@ -1448,7 +1452,7 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, int totalsilence = 0; int dspsilence = 0; int olddspsilence = 0; - struct ast_format rfmt; + struct ast_format *rfmt = NULL; struct ast_silence_generator *silgen = NULL; char prependfile[PATH_MAX]; int ioflags; /* IO flags for writing output file */ @@ -1467,7 +1471,6 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, break; } - ast_format_clear(&rfmt); if (silencethreshold < 0) { silencethreshold = global_silence_threshold; } @@ -1542,6 +1545,7 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); ast_dsp_free(sildet); + ao2_cleanup(rfmt); return -1; } } @@ -1692,7 +1696,7 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, * set the mode, if we haven't already * for sildet */ - if (muted && !rfmt.id) { + if (muted && !rfmt) { ast_verb(3, "Setting read format to linear mode\n"); res = set_read_to_slin(chan, &rfmt); if (res < 0) { @@ -1812,9 +1816,10 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, ast_filedelete(prependfile, sfmt[x]); } } - 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), ast_channel_name(chan)); + if (rfmt && ast_set_read_format(chan, rfmt)) { + ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_format_get_name(rfmt), ast_channel_name(chan)); } + ao2_cleanup(rfmt); if ((outmsg == 2) && (!skip_confirmation_sound)) { ast_stream_and_wait(chan, "auth-thankyou", ""); } diff --git a/main/asterisk.c b/main/asterisk.c index 55e06c385..1c6994280 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -248,6 +248,8 @@ int daemon(int, int); /* defined in libresolv of all places */ #include "asterisk/stasis_system.h" #include "asterisk/security_events.h" #include "asterisk/endpoints.h" +#include "asterisk/codec.h" +#include "asterisk/format_cache.h" #include "../defaults.h" @@ -4316,6 +4318,26 @@ int main(int argc, char *argv[]) exit(1); } + if (ast_codec_init()) { + printf("%s", term_quit()); + exit(1); + } + + if (ast_format_init()) { + printf("%s", term_quit()); + exit(1); + } + + if (ast_format_cache_init()) { + printf("%s", term_quit()); + exit(1); + } + + if (ast_codec_builtin_init()) { + printf("%s", term_quit()); + exit(1); + } + #ifdef AST_XML_DOCS /* Load XML documentation. */ ast_xmldoc_load_documentation(); @@ -4370,8 +4392,6 @@ int main(int argc, char *argv[]) threadstorage_init(); - ast_format_attr_init(); - ast_format_list_init(); if (ast_rtp_engine_init()) { printf("%s", term_quit()); exit(1); diff --git a/main/audiohook.c b/main/audiohook.c index 549ad31eb..33dad38f9 100644 --- a/main/audiohook.c +++ b/main/audiohook.c @@ -41,13 +41,14 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/slinfactory.h" #include "asterisk/frame.h" #include "asterisk/translate.h" +#include "asterisk/format_cache.h" #define AST_AUDIOHOOK_SYNC_TOLERANCE 100 /*!< Tolerance in milliseconds for audiohooks synchronization */ #define AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE 100 /*!< When small queue is enabled, this is the maximum amount of audio that can remain queued at a time. */ struct ast_audiohook_translate { struct ast_trans_pvt *trans_pvt; - struct ast_format format; + struct ast_format *format; }; struct ast_audiohook_list { @@ -67,7 +68,7 @@ struct ast_audiohook_list { static int audiohook_set_internal_rate(struct ast_audiohook *audiohook, int rate, int reset) { - struct ast_format slin; + struct ast_format *slin; if (audiohook->hook_internal_samp_rate == rate) { return 0; @@ -75,7 +76,8 @@ static int audiohook_set_internal_rate(struct ast_audiohook *audiohook, int rate audiohook->hook_internal_samp_rate = rate; - ast_format_set(&slin, ast_format_slin_by_rate(rate), 0); + slin = ast_format_cache_get_slin_by_rate(rate); + /* Setup the factories that are needed for this audiohook type */ switch (audiohook->type) { case AST_AUDIOHOOK_TYPE_SPY: @@ -84,12 +86,13 @@ static int audiohook_set_internal_rate(struct ast_audiohook *audiohook, int rate ast_slinfactory_destroy(&audiohook->read_factory); ast_slinfactory_destroy(&audiohook->write_factory); } - ast_slinfactory_init_with_format(&audiohook->read_factory, &slin); - ast_slinfactory_init_with_format(&audiohook->write_factory, &slin); + ast_slinfactory_init_with_format(&audiohook->read_factory, slin); + ast_slinfactory_init_with_format(&audiohook->write_factory, slin); break; default: break; } + return 0; } @@ -143,6 +146,8 @@ int ast_audiohook_destroy(struct ast_audiohook *audiohook) if (audiohook->trans_pvt) ast_translator_free_path(audiohook->trans_pvt); + ao2_cleanup(audiohook->format); + /* Lock and trigger be gone! */ ast_cond_destroy(&audiohook->trigger); ast_mutex_destroy(&audiohook->lock); @@ -220,11 +225,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.format = ast_format_cache_get_slin_by_rate(audiohook->hook_internal_samp_rate), .data.ptr = buf, .datalen = sizeof(buf), .samples = samples, }; - ast_format_set(&frame.subclass.format, ast_format_slin_by_rate(audiohook->hook_internal_samp_rate), 0); /* Ensure the factory is able to give us the samples we want */ if (samples > ast_slinfactory_available(factory)) { @@ -254,7 +259,6 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho .datalen = sizeof(buf1), .samples = samples, }; - ast_format_set(&frame.subclass.format, ast_format_slin_by_rate(audiohook->hook_internal_samp_rate), 0); /* Make sure both factories have the required samples */ usable_read = (ast_slinfactory_available(&audiohook->read_factory) >= samples ? 1 : 0); @@ -346,6 +350,8 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho /* Make the final buffer part of the frame, so it gets duplicated fine */ frame.data.ptr = final_buf; + frame.subclass.format = ast_format_cache_get_slin_by_rate(audiohook->hook_internal_samp_rate); + /* Yahoo, a combined copy of the audio! */ return ast_frdup(&frame); } @@ -353,17 +359,17 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho static struct ast_frame *audiohook_read_frame_helper(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, struct ast_format *format, struct ast_frame **read_reference, struct ast_frame **write_reference) { struct ast_frame *read_frame = NULL, *final_frame = NULL; - struct ast_format tmp_fmt; + struct ast_format *slin; int samples_converted; /* the number of samples requested is based on the format they are requesting. Inorder * to process this correctly samples must be converted to our internal sample rate */ - if (audiohook->hook_internal_samp_rate == ast_format_rate(format)) { + if (audiohook->hook_internal_samp_rate == ast_format_get_sample_rate(format)) { samples_converted = samples; - } else if (audiohook->hook_internal_samp_rate > ast_format_rate(format)) { - samples_converted = samples * (audiohook->hook_internal_samp_rate / (float) ast_format_rate(format)); + } else if (audiohook->hook_internal_samp_rate > ast_format_get_sample_rate(format)) { + samples_converted = samples * (audiohook->hook_internal_samp_rate / (float) ast_format_get_sample_rate(format)); } else { - samples_converted = samples * (ast_format_rate(format) / (float) audiohook->hook_internal_samp_rate); + samples_converted = samples * (ast_format_get_sample_rate(format) / (float) audiohook->hook_internal_samp_rate); } if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ? @@ -372,21 +378,23 @@ static struct ast_frame *audiohook_read_frame_helper(struct ast_audiohook *audio return NULL; } + slin = ast_format_cache_get_slin_by_rate(audiohook->hook_internal_samp_rate); + /* If they don't want signed linear back out, we'll have to send it through the translation path */ - if (format->id != ast_format_slin_by_rate(audiohook->hook_internal_samp_rate)) { + if (ast_format_cmp(format, slin) != AST_FORMAT_CMP_EQUAL) { /* Rebuild translation path if different format then previously */ - if (ast_format_cmp(format, &audiohook->format) == AST_FORMAT_CMP_NOT_EQUAL) { + 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_set(&tmp_fmt, ast_format_slin_by_rate(audiohook->hook_internal_samp_rate), 0)))) { + if (!(audiohook->trans_pvt = ast_translator_build_path(format, slin))) { ast_frfree(read_frame); return NULL; } - ast_format_copy(&audiohook->format, format); + ao2_replace(audiohook->format, format); } /* Convert to requested format, and allow the read in frame to be freed */ final_frame = ast_translate(audiohook->trans_pvt, read_frame, 1); @@ -752,8 +760,7 @@ static struct ast_frame *audiohook_list_translate_to_slin(struct ast_audiohook_l struct ast_audiohook_translate *in_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->in_translate[0] : &audiohook_list->in_translate[1]); struct ast_frame *new_frame = frame; - struct ast_format tmp_fmt; - enum ast_format_id slin_id; + struct ast_format *slin; /* If we are capable of maintaining doing samplerates other that 8khz, update * the internal audiohook_list's rate and higher samplerate audio arrives. By @@ -761,24 +768,24 @@ static struct ast_frame *audiohook_list_translate_to_slin(struct ast_audiohook_l * as the are written and read from. */ if (audiohook_list->native_slin_compatible) { audiohook_list->list_internal_samp_rate = - MAX(ast_format_rate(&frame->subclass.format), audiohook_list->list_internal_samp_rate); + MAX(ast_format_get_sample_rate(frame->subclass.format), audiohook_list->list_internal_samp_rate); } - slin_id = ast_format_slin_by_rate(audiohook_list->list_internal_samp_rate); - - if (frame->subclass.format.id == slin_id) { + slin = ast_format_cache_get_slin_by_rate(audiohook_list->list_internal_samp_rate); + if (ast_format_cmp(frame->subclass.format, slin) == AST_FORMAT_CMP_EQUAL) { return new_frame; } - if (ast_format_cmp(&frame->subclass.format, &in_translate->format) == AST_FORMAT_CMP_NOT_EQUAL) { + 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_set(&tmp_fmt, slin_id, 0), &frame->subclass.format))) { + if (!(in_translate->trans_pvt = ast_translator_build_path(slin, frame->subclass.format))) { return NULL; } - ast_format_copy(&in_translate->format, &frame->subclass.format); + ao2_replace(in_translate->format, frame->subclass.format); } + if (!(new_frame = ast_translate(in_translate->trans_pvt, frame, 0))) { return NULL; } @@ -791,16 +798,16 @@ static struct ast_frame *audiohook_list_translate_to_native(struct ast_audiohook { struct ast_audiohook_translate *out_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->out_translate[0] : &audiohook_list->out_translate[1]); struct ast_frame *outframe = NULL; - if (ast_format_cmp(&slin_frame->subclass.format, outformat) == AST_FORMAT_CMP_NOT_EQUAL) { + if (ast_format_cmp(slin_frame->subclass.format, outformat) == AST_FORMAT_CMP_NOT_EQUAL) { /* rebuild translators if necessary */ - if (ast_format_cmp(&out_translate->format, outformat) == AST_FORMAT_CMP_NOT_EQUAL) { + if (ast_format_cmp(out_translate->format, outformat) == 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(outformat, &slin_frame->subclass.format))) { + if (!(out_translate->trans_pvt = ast_translator_build_path(outformat, slin_frame->subclass.format))) { return NULL; } - ast_format_copy(&out_translate->format, outformat); + ao2_replace(out_translate->format, outformat); } /* translate back to the format the frame came in as. */ if (!(outframe = ast_translate(out_translate->trans_pvt, slin_frame, 0))) { @@ -924,7 +931,7 @@ 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_manipulated) { - if (!(end_frame = audiohook_list_translate_to_native(audiohook_list, direction, middle_frame, &start_frame->subclass.format))) { + if (!(end_frame = audiohook_list_translate_to_native(audiohook_list, direction, middle_frame, start_frame->subclass.format))) { /* translation failed, so just pass back the input frame */ end_frame = start_frame; } diff --git a/main/bridge.c b/main/bridge.c index 41053eb8a..d5fc1699a 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -934,61 +934,65 @@ int ast_bridge_destroy(struct ast_bridge *bridge, int cause) static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) { - struct ast_format read_format; - struct ast_format write_format; - struct ast_format best_format; - char codec_buf[512]; + struct ast_str *codec_buf = ast_str_alloca(64); + struct ast_format *read_format; + struct ast_format *write_format; + struct ast_format *best_format; - ast_format_copy(&read_format, ast_channel_readformat(bridge_channel->chan)); - ast_format_copy(&write_format, ast_channel_writeformat(bridge_channel->chan)); + read_format = ast_channel_readformat(bridge_channel->chan); + write_format = ast_channel_writeformat(bridge_channel->chan); /* Are the formats currently in use something this bridge can handle? */ - if (!ast_format_cap_iscompatible(bridge->technology->format_capabilities, ast_channel_readformat(bridge_channel->chan))) { - ast_best_codec(bridge->technology->format_capabilities, &best_format); + if (ast_format_cap_iscompatible_format(bridge->technology->format_capabilities, read_format) == AST_FORMAT_CMP_NOT_EQUAL) { + best_format = ast_format_cap_get_format(bridge->technology->format_capabilities, 0); /* Read format is a no go... */ 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->format_capabilities), - ast_getformatname(&read_format)); + ast_format_cap_get_names(bridge->technology->format_capabilities, &codec_buf), + ast_format_get_name(read_format)); /* Switch read format to the best one chosen */ - if (ast_set_read_format(bridge_channel->chan, &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", - ast_channel_name(bridge_channel->chan), ast_getformatname(&best_format)); + ast_channel_name(bridge_channel->chan), ast_format_get_name(best_format)); + ao2_cleanup(best_format); return -1; } ast_debug(1, "Bridge %s put channel %s into read format %s\n", bridge->uniqueid, ast_channel_name(bridge_channel->chan), - ast_getformatname(&best_format)); + ast_format_get_name(best_format)); + ao2_cleanup(best_format); } else { ast_debug(1, "Bridge %s is happy that channel %s already has read format %s\n", bridge->uniqueid, ast_channel_name(bridge_channel->chan), - ast_getformatname(&read_format)); + ast_format_get_name(read_format)); } - if (!ast_format_cap_iscompatible(bridge->technology->format_capabilities, &write_format)) { - ast_best_codec(bridge->technology->format_capabilities, &best_format); + if (ast_format_cap_iscompatible_format(bridge->technology->format_capabilities, write_format) == AST_FORMAT_CMP_NOT_EQUAL) { + best_format = ast_format_cap_get_format(bridge->technology->format_capabilities, 0); /* Write format is a no go... */ 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->format_capabilities), - ast_getformatname(&write_format)); + ast_format_cap_get_names(bridge->technology->format_capabilities, &codec_buf), + ast_format_get_name(write_format)); /* Switch write format to the best one chosen */ - if (ast_set_write_format(bridge_channel->chan, &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", - ast_channel_name(bridge_channel->chan), ast_getformatname(&best_format)); + ast_channel_name(bridge_channel->chan), ast_format_get_name(best_format)); + ao2_cleanup(best_format); return -1; } ast_debug(1, "Bridge %s put channel %s into write format %s\n", bridge->uniqueid, ast_channel_name(bridge_channel->chan), - ast_getformatname(&best_format)); + ast_format_get_name(best_format)); + ao2_cleanup(best_format); } else { ast_debug(1, "Bridge %s is happy that channel %s already has write format %s\n", bridge->uniqueid, ast_channel_name(bridge_channel->chan), - ast_getformatname(&write_format)); + ast_format_get_name(write_format)); } return 0; @@ -3525,7 +3529,7 @@ void ast_bridge_update_talker_src_video_mode(struct ast_bridge *bridge, struct a struct ast_bridge_video_talker_src_data *data; /* If the channel doesn't support video, we don't care about it */ - if (!ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_FORMAT_TYPE_VIDEO)) { + if (!ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_MEDIA_TYPE_VIDEO)) { return; } diff --git a/main/bridge_basic.c b/main/bridge_basic.c index 60ce37a98..cb8c2f992 100644 --- a/main/bridge_basic.c +++ b/main/bridge_basic.c @@ -46,6 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/dial.h" #include "asterisk/stasis_bridges.h" #include "asterisk/features.h" +#include "asterisk/format_cache.h" #define NORMAL_FLAGS (AST_BRIDGE_FLAG_DISSOLVE_HANGUP | AST_BRIDGE_FLAG_DISSOLVE_EMPTY \ | AST_BRIDGE_FLAG_SMART) @@ -2267,14 +2268,13 @@ static void recall_callback(struct ast_dial *dial) static int recalling_enter(struct attended_transfer_properties *props) { - RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK), ast_format_cap_destroy); - struct ast_format fmt; + RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup); if (!cap) { return -1; } - ast_format_cap_add(cap, ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0)); + ast_format_cap_append(cap, ast_format_slin, 0); /* When we dial the transfer target, since we are communicating * with a local channel, we can place the local channel in a bridge @@ -2395,8 +2395,7 @@ static int attach_framehook(struct attended_transfer_properties *props, struct a static int retransfer_enter(struct attended_transfer_properties *props) { - RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK), ast_format_cap_destroy); - struct ast_format fmt; + RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup); char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2]; int cause; @@ -2406,7 +2405,7 @@ static int retransfer_enter(struct attended_transfer_properties *props) snprintf(destination, sizeof(destination), "%s@%s", props->exten, props->context); - ast_format_cap_add(cap, ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0)); + ast_format_cap_append(cap, ast_format_slin, 0); /* Get a channel that is the destination we wish to call */ props->recall_target = ast_request("Local", cap, NULL, NULL, destination, &cause); diff --git a/main/bridge_channel.c b/main/bridge_channel.c index 93cbed22d..ea9b3f868 100644 --- a/main/bridge_channel.c +++ b/main/bridge_channel.c @@ -320,25 +320,28 @@ struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *br void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel) { + ast_assert(bridge_channel->read_format != NULL); + ast_assert(bridge_channel->write_format != NULL); + /* Restore original formats of the channel as they came in */ - if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) { + if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) { ast_debug(1, "Bridge is returning %p(%s) to read format %s\n", bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->read_format)); - if (ast_set_read_format(bridge_channel->chan, &bridge_channel->read_format)) { + ast_format_get_name(bridge_channel->read_format)); + if (ast_set_read_format(bridge_channel->chan, bridge_channel->read_format)) { ast_debug(1, "Bridge failed to return %p(%s) to read format %s\n", bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->read_format)); + ast_format_get_name(bridge_channel->read_format)); } } - if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) { + if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) { ast_debug(1, "Bridge is returning %p(%s) to write format %s\n", bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->write_format)); - if (ast_set_write_format(bridge_channel->chan, &bridge_channel->write_format)) { + ast_format_get_name(bridge_channel->write_format)); + if (ast_set_write_format(bridge_channel->chan, bridge_channel->write_format)) { ast_debug(1, "Bridge failed to return %p(%s) to write format %s\n", bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->write_format)); + ast_format_get_name(bridge_channel->write_format)); } } } @@ -2342,8 +2345,8 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel) int res = 0; struct ast_bridge_features *channel_features; - ast_format_copy(&bridge_channel->read_format, ast_channel_readformat(bridge_channel->chan)); - ast_format_copy(&bridge_channel->write_format, ast_channel_writeformat(bridge_channel->chan)); + bridge_channel->read_format = ao2_bump(ast_channel_readformat(bridge_channel->chan)); + bridge_channel->write_format = ao2_bump(ast_channel_writeformat(bridge_channel->chan)); ast_debug(1, "Bridge %s: %p(%s) is joining\n", bridge_channel->bridge->uniqueid, @@ -2590,6 +2593,9 @@ static void bridge_channel_destroy(void *obj) pipe_close(bridge_channel->alert_pipe); ast_cond_destroy(&bridge_channel->cond); + + ao2_cleanup(bridge_channel->write_format); + ao2_cleanup(bridge_channel->read_format); } struct ast_bridge_channel *bridge_channel_internal_alloc(struct ast_bridge *bridge) diff --git a/main/callerid.c b/main/callerid.c index 5f53cb2ae..e3b2b139e 100644 --- a/main/callerid.c +++ b/main/callerid.c @@ -42,6 +42,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/callerid.h" #include "asterisk/fskmodem.h" #include "asterisk/utils.h" +#include "asterisk/format_cache.h" struct callerid_state { fsk_data fskd; diff --git a/main/ccss.c b/main/ccss.c index 09fa2d114..9fcabfefd 100644 --- a/main/ccss.c +++ b/main/ccss.c @@ -52,6 +52,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/manager.h" #include "asterisk/causes.h" #include "asterisk/stasis_system.h" +#include "asterisk/format_cache.h" /*** DOCUMENTATION <application name="CallCompletionRequest" language="en_US"> @@ -2814,8 +2815,7 @@ static void *generic_recall(void *data) const char *callback_macro = ast_get_cc_callback_macro(agent->cc_params); const char *callback_sub = ast_get_cc_callback_sub(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(AST_FORMAT_CAP_FLAG_NOLOCK); + struct ast_format_cap *tmp_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); if (!tmp_cap) { return NULL; @@ -2826,17 +2826,17 @@ static void *generic_recall(void *data) *target++ = '\0'; } - ast_format_cap_add(tmp_cap, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0)); + ast_format_cap_append(tmp_cap, ast_format_slin, 0); if (!(chan = ast_request_and_dial(tech, tmp_cap, NULL, 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 %u: 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); + ao2_ref(tmp_cap, -1); return NULL; } - ast_format_cap_destroy(tmp_cap); + ao2_ref(tmp_cap, -1); /* We have a channel. It's time now to set up the datastore of recalled CC interfaces. * This will be a common task for all recall functions. If it were possible, I'd have diff --git a/main/channel.c b/main/channel.c index 15f2cbce0..b17dddeef 100644 --- a/main/channel.c +++ b/main/channel.c @@ -342,7 +342,7 @@ static char *complete_channeltypes(struct ast_cli_args *a) static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct chanlist *cl = NULL; - char buf[512]; + struct ast_str *codec_buf = ast_str_alloca(64); switch (cmd) { case CLI_INIT: @@ -387,7 +387,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), + ast_format_cap_get_names(cl->tech->capabilities, &codec_buf), (cl->tech->send_digit_begin) ? "yes" : "no", (cl->tech->send_digit_end) ? "yes" : "no", (cl->tech->send_html) ? "yes" : "no", @@ -764,78 +764,6 @@ char *ast_transfercapability2str(int transfercapability) } } -/*! \brief Pick the best audio codec */ -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 */ - static const enum ast_format_id prefs[] = - { - /*! Okay, ulaw is used by all telephony equipment, so start with it */ - AST_FORMAT_ULAW, - /*! Unless of course, you're a silly European, so then prefer ALAW */ - AST_FORMAT_ALAW, - AST_FORMAT_G719, - AST_FORMAT_SIREN14, - AST_FORMAT_SIREN7, - AST_FORMAT_TESTLAW, - /*! G.722 is better then all below, but not as common as the above... so give ulaw and alaw priority */ - AST_FORMAT_G722, - /*! Okay, well, signed linear is easy to translate into other stuff */ - AST_FORMAT_SLINEAR192, - AST_FORMAT_SLINEAR96, - AST_FORMAT_SLINEAR48, - AST_FORMAT_SLINEAR44, - AST_FORMAT_SLINEAR32, - AST_FORMAT_SLINEAR24, - AST_FORMAT_SLINEAR16, - AST_FORMAT_SLINEAR12, - AST_FORMAT_SLINEAR, - /*! G.726 is standard ADPCM, in RFC3551 packing order */ - AST_FORMAT_G726, - /*! G.726 is standard ADPCM, in AAL2 packing order */ - AST_FORMAT_G726_AAL2, - /*! ADPCM has great sound quality and is still pretty easy to translate */ - AST_FORMAT_ADPCM, - /*! Okay, we're down to vocoders now, so pick GSM because it's small and easier to - translate and sounds pretty good */ - AST_FORMAT_GSM, - /*! iLBC is not too bad */ - AST_FORMAT_ILBC, - /*! Speex is free, but computationally more expensive than GSM */ - AST_FORMAT_SPEEX32, - AST_FORMAT_SPEEX16, - AST_FORMAT_SPEEX, - /*! Opus */ - AST_FORMAT_OPUS, - /*! SILK is pretty awesome. */ - AST_FORMAT_SILK, - /*! CELT supports crazy high sample rates */ - AST_FORMAT_CELT, - /*! Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough - to use it */ - AST_FORMAT_LPC10, - /*! G.729a is faster than 723 and slightly less expensive */ - AST_FORMAT_G729A, - /*! Down to G.723.1 which is proprietary but at least designed for voice */ - AST_FORMAT_G723_1, - }; - char buf[512]; - int x; - - /* Find the first preferred codec in the format given */ - for (x = 0; x < ARRAY_LEN(prefs); x++) { - if (ast_format_cap_best_byid(cap, prefs[x], result)) { - return result; - } - } - - ast_format_clear(result); - ast_log(LOG_WARNING, "Don't know any of %s formats\n", ast_getformatname_multiple(buf, sizeof(buf), cap)); - - return NULL; -} - /*! \brief Channel technology used to extract a channel from a running application. The * channel created with this technology will be immediately hung up - most external * applications won't ever want to see this. @@ -883,11 +811,18 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char ast_channel_stage_snapshot(tmp); - if (!(nativeformats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_CACHE_STRINGS))) { + if (!(nativeformats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { /* format capabilities structure allocation failure */ return ast_channel_unref(tmp); } + ast_format_cap_append(nativeformats, ast_format_none, 0); ast_channel_nativeformats_set(tmp, nativeformats); + ao2_ref(nativeformats, -1); + + ast_channel_set_rawwriteformat(tmp, ast_format_none); + ast_channel_set_rawreadformat(tmp, ast_format_none); + ast_channel_set_writeformat(tmp, ast_format_none); + ast_channel_set_readformat(tmp, ast_format_none); /* * Init file descriptors to unopened state so @@ -2317,6 +2252,13 @@ static void ast_channel_destructor(void *obj) if (ast_channel_pbx(chan)) ast_log_callid(LOG_WARNING, callid, "PBX may not have been terminated properly on '%s'\n", ast_channel_name(chan)); + /* Free formats */ + ast_channel_set_oldwriteformat(chan, NULL); + ast_channel_set_rawreadformat(chan, NULL); + ast_channel_set_rawwriteformat(chan, NULL); + ast_channel_set_readformat(chan, NULL); + ast_channel_set_writeformat(chan, NULL); + ast_party_dialed_free(ast_channel_dialed(chan)); ast_party_caller_free(ast_channel_caller(chan)); ast_party_connected_line_free(ast_channel_connected(chan)); @@ -2373,7 +2315,7 @@ static void ast_channel_destructor(void *obj) ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_DEVSTATE_CACHE) ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), device_name); } - ast_channel_nativeformats_set(chan, ast_format_cap_destroy(ast_channel_nativeformats(chan))); + ast_channel_nativeformats_set(chan, NULL); if (callid) { ast_callid_unref(callid); } @@ -2623,21 +2565,14 @@ int ast_softhangup(struct ast_channel *chan, int cause) static void free_translation(struct ast_channel *clonechan) { - if (ast_channel_writetrans(clonechan)) + if (ast_channel_writetrans(clonechan)) { ast_translator_free_path(ast_channel_writetrans(clonechan)); - if (ast_channel_readtrans(clonechan)) + } + if (ast_channel_readtrans(clonechan)) { ast_translator_free_path(ast_channel_readtrans(clonechan)); + } ast_channel_writetrans_set(clonechan, NULL); ast_channel_readtrans_set(clonechan, NULL); - if (ast_format_cap_is_empty(ast_channel_nativeformats(clonechan))) { - ast_format_clear(ast_channel_rawwriteformat(clonechan)); - ast_format_clear(ast_channel_rawreadformat(clonechan)); - } else { - struct ast_format tmpfmt; - ast_best_codec(ast_channel_nativeformats(clonechan), &tmpfmt); - ast_format_copy(ast_channel_rawwriteformat(clonechan), &tmpfmt); - ast_format_copy(ast_channel_rawreadformat(clonechan), &tmpfmt); - } } void ast_set_hangupsource(struct ast_channel *chan, const char *source, int force) @@ -2998,10 +2933,11 @@ static int generator_force(const void *data) generate = ast_channel_generator(chan)->generate; ast_channel_unlock(chan); - if (!tmp || !generate) + if (!tmp || !generate) { return 0; + } - res = generate(chan, tmp, 0, ast_format_rate(ast_channel_writeformat(chan)) / 50); + res = generate(chan, tmp, 0, ast_format_get_sample_rate(ast_channel_writeformat(chan)) / 50); ast_channel_lock(chan); if (ast_channel_generator(chan) && generate == ast_channel_generator(chan)->generate) { @@ -3677,11 +3613,9 @@ static void ast_read_generator_actions(struct ast_channel *chan, struct ast_fram * We must generate frames in phase locked mode since * we have no internal timer available. */ - - if (ast_format_cmp(&f->subclass.format, ast_channel_writeformat(chan)) == AST_FORMAT_CMP_NOT_EQUAL) { + if (ast_format_cmp(f->subclass.format, ast_channel_writeformat(chan)) == AST_FORMAT_CMP_NOT_EQUAL) { float factor; - - factor = ((float) ast_format_rate(ast_channel_writeformat(chan))) / ((float) ast_format_rate(&f->subclass.format)); + factor = ((float) ast_format_get_sample_rate(ast_channel_writeformat(chan))) / ((float) ast_format_get_sample_rate(f->subclass.format)); samples = (int) (((float) f->samples) * factor); } else { samples = f->samples; @@ -4186,11 +4120,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) && !ast_format_cap_iscompatible(ast_channel_nativeformats(chan), &f->subclass.format)) { + } else if ((f->frametype == AST_FRAME_VOICE) && ast_format_cap_iscompatible_format(ast_channel_nativeformats(chan), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) { /* This frame is not one of the current native formats -- drop it on the floor */ - char to[200]; + struct ast_str *codec_buf = ast_str_alloca(64); ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n", - ast_channel_name(chan), ast_getformatname(&f->subclass.format), ast_getformatname_multiple(to, sizeof(to), ast_channel_nativeformats(chan))); + ast_channel_name(chan), ast_format_get_name(f->subclass.format), ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf)); ast_frfree(f); f = &ast_null_frame; } else if ((f->frametype == AST_FRAME_VOICE)) { @@ -4206,7 +4140,9 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) #ifndef MONITOR_CONSTANT_DELAY int jump = ast_channel_outsmpl(chan) - ast_channel_insmpl(chan) - 4 * f->samples; if (jump >= 0) { - jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)), ast_format_rate(&f->subclass.format), ast_format_rate(&ast_channel_monitor(chan)->read_stream->fmt->format)); + jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)), + ast_format_get_sample_rate(f->subclass.format), + ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format)); if (ast_seekstream(ast_channel_monitor(chan)->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"); } @@ -4215,7 +4151,9 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + f->samples); } #else - int jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)), ast_format_rate(f->subclass.codec), ast_format_rate(ast_channel_monitor(chan)->read_stream->fmt->format)); + int jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)), + ast_format_get_sample_rate(f->subclass.codec), + ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format)); if (jump - MONITOR_DELAY >= 0) { if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump - f->samples, SEEK_FORCECUR) == -1) ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n"); @@ -4748,7 +4686,7 @@ int ast_sendtext(struct ast_channel *chan, const char *text) } CHECK_BLOCKING(chan); - if (ast_channel_tech(chan)->write_text && (ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_FORMAT_TYPE_TEXT))) { + if (ast_channel_tech(chan)->write_text && (ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_MEDIA_TYPE_TEXT))) { struct ast_frame f; f.frametype = AST_FRAME_TEXT; @@ -4759,7 +4697,7 @@ int ast_sendtext(struct ast_channel *chan, const char *text) f.offset = 0; f.seqno = 0; - ast_format_set(&f.subclass.format, AST_FORMAT_T140, 0); + f.subclass.format = ast_format_t140; res = ast_channel_tech(chan)->write_text(chan, &f); } else if (ast_channel_tech(chan)->send_text) { res = ast_channel_tech(chan)->send_text(chan, text); @@ -4856,7 +4794,7 @@ int ast_prod(struct ast_channel *chan) /* Send an empty audio frame to get things moving */ if (ast_channel_state(chan) != AST_STATE_UP) { ast_debug(1, "Prodding channel '%s'\n", ast_channel_name(chan)); - ast_format_copy(&a.subclass.format, ast_channel_rawwriteformat(chan)); + a.subclass.format = ast_channel_rawwriteformat(chan); a.data.ptr = nothing + AST_FRIENDLY_OFFSET; a.src = "ast_prod"; /* this better match check in ast_write */ if (ast_write(chan, &a)) @@ -5068,7 +5006,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) CHECK_BLOCKING(chan); break; case AST_FRAME_TEXT: - if (fr->subclass.integer == AST_FORMAT_T140) { + if (ast_format_cmp(fr->subclass.format, ast_format_t140) == AST_FORMAT_CMP_EQUAL) { res = (ast_channel_tech(chan)->write_text == NULL) ? 0 : ast_channel_tech(chan)->write_text(chan, fr); } else { @@ -5093,17 +5031,17 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) if (ast_channel_tech(chan)->write == NULL) break; /*! \todo XXX should return 0 maybe ? */ - if (ast_opt_generic_plc && fr->subclass.format.id == AST_FORMAT_SLINEAR) { + if (ast_opt_generic_plc && ast_format_cmp(fr->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) { 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 (ast_format_cmp(&fr->subclass.format, ast_channel_rawwriteformat(chan)) != AST_FORMAT_CMP_NOT_EQUAL) { + if (ast_format_cmp(fr->subclass.format, ast_channel_rawwriteformat(chan)) != AST_FORMAT_CMP_NOT_EQUAL) { f = fr; } else { - if ((!ast_format_cap_iscompatible(ast_channel_nativeformats(chan), &fr->subclass.format)) && - (ast_format_cmp(ast_channel_writeformat(chan), &fr->subclass.format) != AST_FORMAT_CMP_EQUAL)) { - char nf[512]; + if ((ast_format_cap_iscompatible_format(ast_channel_nativeformats(chan), fr->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) && + (ast_format_cmp(ast_channel_writeformat(chan), fr->subclass.format) != AST_FORMAT_CMP_EQUAL)) { + struct ast_str *codec_buf = ast_str_alloca(64); /* * XXX Something is not right. We are not compatible with this @@ -5115,9 +5053,9 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) * areas. */ ast_log(LOG_WARNING, "Codec mismatch on channel %s setting write format to %s from %s native formats %s\n", - ast_channel_name(chan), ast_getformatname(&fr->subclass.format), ast_getformatname(ast_channel_writeformat(chan)), - ast_getformatname_multiple(nf, sizeof(nf), ast_channel_nativeformats(chan))); - ast_set_write_format_by_id(chan, fr->subclass.format.id); + ast_channel_name(chan), ast_format_get_name(fr->subclass.format), ast_format_get_name(ast_channel_writeformat(chan)), + ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf)); + ast_set_write_format(chan, fr->subclass.format); } f = (ast_channel_writetrans(chan)) ? ast_translate(ast_channel_writetrans(chan), fr, 0) : fr; @@ -5185,7 +5123,9 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) #ifndef MONITOR_CONSTANT_DELAY int jump = ast_channel_insmpl(chan) - ast_channel_outsmpl(chan) - 4 * cur->samples; if (jump >= 0) { - jump = calc_monitor_jump((ast_channel_insmpl(chan) - ast_channel_outsmpl(chan)), ast_format_rate(&f->subclass.format), ast_format_rate(&ast_channel_monitor(chan)->read_stream->fmt->format)); + jump = calc_monitor_jump((ast_channel_insmpl(chan) - ast_channel_outsmpl(chan)), + ast_format_get_sample_rate(f->subclass.format), + ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format)); if (ast_seekstream(ast_channel_monitor(chan)->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"); } @@ -5194,7 +5134,9 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) ast_channel_outsmpl_set(chan, ast_channel_outsmpl(chan) + cur->samples); } #else - int jump = calc_monitor_jump((ast_channel_insmpl(chan) - ast_channel_outsmpl(chan)), ast_format_rate(f->subclass.codec), ast_format_rate(ast_channel_monitor(chan)->read_stream->fmt->format)); + int jump = calc_monitor_jump((ast_channel_insmpl(chan) - ast_channel_outsmpl(chan)), + ast_format_get_sample_rate(f->subclass.codec), + ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format)); if (jump - MONITOR_DELAY >= 0) { if (ast_seekstream(ast_channel_monitor(chan)->write_stream, jump - cur->samples, SEEK_FORCECUR) == -1) { ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n"); @@ -5300,21 +5242,23 @@ static int set_format(struct ast_channel *chan, { struct ast_trans_pvt *trans_pvt; struct ast_format_cap *cap_native; - struct ast_format best_set_fmt; - struct ast_format best_native_fmt; + RAII_VAR(struct ast_format *, best_set_fmt, ast_format_cap_get_format(cap_set, 0), ao2_cleanup); + RAII_VAR(struct ast_format *, best_native_fmt, NULL, ao2_cleanup); int res; - ast_best_codec(cap_set, &best_set_fmt); + ast_assert(format != NULL); + ast_assert(rawformat != NULL); /* 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, &best_set_fmt, sizeof(best_set_fmt), 0)) { ast_debug(1, "Channel driver natively set channel %s to %s format %s\n", ast_channel_name(chan), - direction ? "write" : "read", ast_getformatname(&best_set_fmt)); + direction ? "write" : "read", ast_format_get_name(best_set_fmt)); ast_channel_lock(chan); - ast_format_copy(format, &best_set_fmt); - ast_format_copy(rawformat, &best_set_fmt); - ast_format_cap_set(ast_channel_nativeformats(chan), &best_set_fmt); + cap_native = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + ast_format_cap_append(cap_native, best_set_fmt, 0); + ast_channel_nativeformats_set(chan, cap_native); + ao2_cleanup(cap_native); ast_channel_unlock(chan); trans_pvt = trans->get(chan); @@ -5344,31 +5288,27 @@ static int set_format(struct ast_channel *chan, res = ast_translator_best_choice(cap_native, cap_set, &best_native_fmt, &best_set_fmt); } if (res < 0) { - char from[200]; - char to[200]; + struct ast_str *codec_from = ast_str_alloca(64); + struct ast_str *codec_to = ast_str_alloca(64); - ast_getformatname_multiple(from, sizeof(from), cap_native); + ast_format_cap_get_names(cap_native, &codec_from); ast_channel_unlock(chan); - ast_getformatname_multiple(to, sizeof(to), cap_set); + ast_format_cap_get_names(cap_set, &codec_to); ast_log(LOG_WARNING, "Unable to find a codec translation path from %s to %s\n", - from, to); + ast_str_buffer(codec_from), ast_str_buffer(codec_to)); return -1; } /* Now we have a good choice for both. */ - 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) && + 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->get(chan))) { /* the channel is already in these formats, so nothing to do */ ast_channel_unlock(chan); return 0; } - ast_format_copy(rawformat, &best_native_fmt); - /* User perspective is fmt */ - ast_format_copy(format, &best_set_fmt); - /* Free any translation we have right now */ trans_pvt = trans->get(chan); if (trans_pvt) { @@ -5377,7 +5317,7 @@ static int set_format(struct ast_channel *chan, } /* Build a translation path from the raw format to the desired format */ - if (ast_format_cmp(format, rawformat) != AST_FORMAT_CMP_NOT_EQUAL) { + if (ast_format_cmp(best_set_fmt, best_native_fmt) != 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 @@ -5387,20 +5327,33 @@ static int set_format(struct ast_channel *chan, } else { if (!direction) { /* reading */ - trans_pvt = ast_translator_build_path(format, rawformat); + trans_pvt = ast_translator_build_path(best_set_fmt, best_native_fmt); } else { /* writing */ - trans_pvt = ast_translator_build_path(rawformat, format); + trans_pvt = ast_translator_build_path(best_native_fmt, best_set_fmt); } trans->set(chan, trans_pvt); res = trans_pvt ? 0 : -1; } - ast_channel_unlock(chan); - ast_debug(1, "Set channel %s to %s format %s\n", - ast_channel_name(chan), - direction ? "write" : "read", - ast_getformatname(&best_set_fmt)); + if (!res) { + if (!direction) { + /* reading */ + ast_channel_set_readformat(chan, best_set_fmt); + ast_channel_set_rawreadformat(chan, best_native_fmt); + } else { + /* writing */ + ast_channel_set_writeformat(chan, best_set_fmt); + ast_channel_set_rawwriteformat(chan, best_native_fmt); + } + + ast_debug(1, "Set channel %s to %s format %s\n", + ast_channel_name(chan), + direction ? "write" : "read", + ast_format_get_name(best_set_fmt)); + } + + ast_channel_unlock(chan); /* If there is a generator on the channel, it needs to know about this * change if it is the write format. */ @@ -5413,35 +5366,15 @@ static int set_format(struct ast_channel *chan, int ast_set_read_format(struct ast_channel *chan, struct ast_format *format) { - struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK); + struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); int res; - if (!cap) { - return -1; - } - ast_format_cap_add(cap, format); - - res = set_format(chan, - cap, - ast_channel_rawreadformat(chan), - ast_channel_readformat(chan), - &set_format_readtrans, - 0); - - ast_format_cap_destroy(cap); - return res; -} - -int ast_set_read_format_by_id(struct ast_channel *chan, enum ast_format_id id) -{ - struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK); - struct ast_format tmp_format; - int res; + ast_assert(format != NULL); if (!cap) { return -1; } - ast_format_cap_add(cap, ast_format_set(&tmp_format, id, 0)); + ast_format_cap_append(cap, format, 0); res = set_format(chan, cap, @@ -5450,7 +5383,7 @@ int ast_set_read_format_by_id(struct ast_channel *chan, enum ast_format_id id) &set_format_readtrans, 0); - ast_format_cap_destroy(cap); + ao2_cleanup(cap); return res; } @@ -5466,35 +5399,15 @@ int ast_set_read_format_from_cap(struct ast_channel *chan, struct ast_format_cap int ast_set_write_format(struct ast_channel *chan, struct ast_format *format) { - struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK); + struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); int res; - if (!cap) { - return -1; - } - ast_format_cap_add(cap, format); - - res = set_format(chan, - cap, - ast_channel_rawwriteformat(chan), - ast_channel_writeformat(chan), - &set_format_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(AST_FORMAT_CAP_FLAG_NOLOCK); - struct ast_format tmp_format; - int res; + ast_assert(format != NULL); if (!cap) { return -1; } - ast_format_cap_add(cap, ast_format_set(&tmp_format, id, 0)); + ast_format_cap_append(cap, format, 0); res = set_format(chan, cap, @@ -5503,7 +5416,7 @@ int ast_set_write_format_by_id(struct ast_channel *chan, enum ast_format_id id) &set_format_writetrans, 1); - ast_format_cap_destroy(cap); + ao2_cleanup(cap); return res; } @@ -5912,26 +5825,29 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request AST_RWLIST_TRAVERSE(&backends, chan, list) { struct ast_format_cap *tmp_cap; - struct ast_format tmp_fmt; - struct ast_format best_audio_fmt; + RAII_VAR(struct ast_format *, tmp_fmt, NULL, ao2_cleanup); + RAII_VAR(struct ast_format *, best_audio_fmt, NULL, ao2_cleanup); struct ast_format_cap *joint_cap; if (strcasecmp(type, chan->tech->type)) continue; - 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))) { + tmp_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (tmp_cap) { + ast_format_cap_append_from_cap(tmp_cap, request_cap, AST_MEDIA_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(tmp_cap, chan->tech->capabilities, &tmp_fmt, &best_audio_fmt); - ast_format_cap_destroy(tmp_cap); + ao2_ref(tmp_cap, -1); if (res < 0) { - char tmp1[256], tmp2[256]; + struct ast_str *tech_codecs = ast_str_alloca(64); + struct ast_str *request_codecs = ast_str_alloca(64); + 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), request_cap)); + ast_format_cap_get_names(chan->tech->capabilities, &tech_codecs), + ast_format_cap_get_names(request_cap, &request_codecs)); *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; AST_RWLIST_UNLOCK(&backends); return NULL; @@ -5945,14 +5861,16 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request * 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))) { + joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!joint_cap) { return NULL; } - ast_format_cap_remove_bytype(joint_cap, AST_FORMAT_TYPE_AUDIO); - ast_format_cap_add(joint_cap, &best_audio_fmt); + ast_format_cap_append_from_cap(joint_cap, request_cap, AST_MEDIA_TYPE_UNKNOWN); + ast_format_cap_remove_by_type(joint_cap, AST_MEDIA_TYPE_AUDIO); + ast_format_cap_append(joint_cap, best_audio_fmt, 0); if (!(c = chan->tech->requester(type, joint_cap, assignedids, requestor, addr, cause))) { - ast_format_cap_destroy(joint_cap); + ao2_ref(joint_cap, -1); return NULL; } @@ -5967,7 +5885,7 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request } } - joint_cap = ast_format_cap_destroy(joint_cap); + ao2_ref(joint_cap, -1); if (set_security_requirements(requestor, c)) { ast_log(LOG_WARNING, "Setting security requirements failed\n"); @@ -6164,8 +6082,8 @@ static int ast_channel_make_compatible_helper(struct ast_channel *from, struct a { struct ast_format_cap *src_cap; struct ast_format_cap *dst_cap; - struct ast_format best_src_fmt; - struct ast_format best_dst_fmt; + RAII_VAR(struct ast_format *, best_src_fmt, NULL, ao2_cleanup); + RAII_VAR(struct ast_format *, best_dst_fmt, NULL, ao2_cleanup); int no_path; ast_channel_lock_both(from, to); @@ -6182,8 +6100,8 @@ static int ast_channel_make_compatible_helper(struct ast_channel *from, struct a dst_cap = ast_channel_nativeformats(to); /* shallow copy, do not destroy */ /* If there's no audio in this call, don't bother with trying to find a translation path */ - if (!ast_format_cap_has_type(src_cap, AST_FORMAT_TYPE_AUDIO) - || !ast_format_cap_has_type(dst_cap, AST_FORMAT_TYPE_AUDIO)) { + if (!ast_format_cap_has_type(src_cap, AST_MEDIA_TYPE_AUDIO) + || !ast_format_cap_has_type(dst_cap, AST_MEDIA_TYPE_AUDIO)) { ast_channel_unlock(to); ast_channel_unlock(from); return 0; @@ -6206,28 +6124,29 @@ 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 */ - if (ast_format_cmp(&best_dst_fmt, &best_src_fmt) == AST_FORMAT_CMP_NOT_EQUAL + if (ast_format_cmp(best_dst_fmt, best_src_fmt) == AST_FORMAT_CMP_NOT_EQUAL && (ast_opt_generic_plc || ast_opt_transcode_via_slin)) { - int use_slin = (ast_format_is_slinear(&best_src_fmt) - || ast_format_is_slinear(&best_dst_fmt)) ? 1 : 0; + int use_slin = (ast_format_cache_is_slinear(best_src_fmt) + || ast_format_cache_is_slinear(best_dst_fmt)) ? 1 : 0; - if (use_slin || ast_translate_path_steps(&best_dst_fmt, &best_src_fmt) != 1) { - int best_sample_rate = (ast_format_rate(&best_src_fmt) > ast_format_rate(&best_dst_fmt)) ? - ast_format_rate(&best_src_fmt) : ast_format_rate(&best_dst_fmt); + if (use_slin || ast_translate_path_steps(best_dst_fmt, best_src_fmt) != 1) { + int best_sample_rate = (ast_format_get_sample_rate(best_src_fmt) > ast_format_get_sample_rate(best_dst_fmt)) ? + ast_format_get_sample_rate(best_src_fmt) : ast_format_get_sample_rate(best_dst_fmt); /* pick the best signed linear format based upon what preserves the sample rate the best. */ - ast_format_set(&best_src_fmt, ast_format_slin_by_rate(best_sample_rate), 0); + ao2_ref(best_src_fmt, -1); + best_src_fmt = ao2_bump(ast_format_cache_get_slin_by_rate(best_sample_rate)); } } - if (ast_set_read_format(from, &best_src_fmt)) { + if (ast_set_read_format(from, best_src_fmt)) { ast_log(LOG_WARNING, "Unable to set read format on channel %s to %s\n", - ast_channel_name(from), ast_getformatname(&best_src_fmt)); + ast_channel_name(from), ast_format_get_name(best_src_fmt)); return -1; } - if (ast_set_write_format(to, &best_src_fmt)) { + if (ast_set_write_format(to, best_src_fmt)) { ast_log(LOG_WARNING, "Unable to set write format on channel %s to %s\n", - ast_channel_name(to), ast_getformatname(&best_src_fmt)); + ast_channel_name(to), ast_format_get_name(best_src_fmt)); return -1; } return 0; @@ -6387,9 +6306,10 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann struct ast_party_redirecting redirecting; } exchange; struct ast_channel *bridged; - struct ast_format rformat; - struct ast_format wformat; - struct ast_format tmp_format; + struct ast_format *rformat; + struct ast_format *wformat; + struct ast_format *tmp_format; + struct ast_format_cap *tmp_cap; char tmp_name[AST_CHANNEL_NAME]; char clone_sending_dtmf_digit; struct timeval clone_sending_dtmf_tv; @@ -6446,8 +6366,8 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann * Remember the original read/write formats. We turn off any * translation on either one */ - ast_format_copy(&rformat, ast_channel_readformat(original)); - ast_format_copy(&wformat, ast_channel_writeformat(original)); + rformat = ao2_bump(ast_channel_readformat(original)); + wformat = ao2_bump(ast_channel_writeformat(original)); free_translation(clonechan); free_translation(original); @@ -6508,13 +6428,15 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann } /* Swap the raw formats */ - ast_format_copy(&tmp_format, ast_channel_rawreadformat(original)); - ast_format_copy(ast_channel_rawreadformat(original), ast_channel_rawreadformat(clonechan)); - ast_format_copy(ast_channel_rawreadformat(clonechan), &tmp_format); + tmp_format = ao2_bump(ast_channel_rawreadformat(original)); + ast_channel_set_rawreadformat(original, ast_channel_rawreadformat(clonechan)); + ast_channel_set_rawreadformat(clonechan, tmp_format); + ao2_cleanup(tmp_format); - ast_format_copy(&tmp_format, ast_channel_rawwriteformat(original)); - ast_format_copy(ast_channel_rawwriteformat(original), ast_channel_rawwriteformat(clonechan)); - ast_format_copy(ast_channel_rawwriteformat(clonechan), &tmp_format); + tmp_format = ao2_bump(ast_channel_rawwriteformat(original)); + ast_channel_set_rawwriteformat(original, ast_channel_rawwriteformat(clonechan)); + ast_channel_set_rawwriteformat(clonechan, tmp_format); + ao2_cleanup(tmp_format); ast_channel_softhangup_internal_flag_set(clonechan, AST_SOFTHANGUP_DEV); @@ -6642,16 +6564,21 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann ast_channel_set_fd(original, AST_TIMING_FD, ast_channel_timingfd(original)); /* Our native formats are different now */ - ast_format_cap_copy(ast_channel_nativeformats(original), ast_channel_nativeformats(clonechan)); + tmp_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (tmp_cap) { + ast_format_cap_append_from_cap(tmp_cap, ast_channel_nativeformats(clonechan), AST_MEDIA_TYPE_UNKNOWN); + ast_channel_nativeformats_set(original, tmp_cap); + ao2_ref(tmp_cap, -1); + } /* 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_channel_musicclass_set(original, ast_channel_musicclass(clonechan)); @@ -6660,7 +6587,7 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann ast_channel_accountcode_set(original, S_OR(ast_channel_accountcode(clonechan), "")); ast_debug(1, "Putting channel %s in %s/%s formats\n", ast_channel_name(original), - ast_getformatname(&wformat), ast_getformatname(&rformat)); + ast_format_get_name(wformat), ast_format_get_name(rformat)); /* Fixup the original clonechan's physical side */ if (ast_channel_tech(original)->fixup && ast_channel_tech(original)->fixup(clonechan, original)) { @@ -6773,6 +6700,9 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann /* Release our held safety references. */ ast_channel_unref(original); ast_channel_unref(clonechan); + + ao2_cleanup(rformat); + ao2_cleanup(wformat); } void ast_set_callerid(struct ast_channel *chan, const char *cid_num, const char *cid_name, const char *cid_ani) @@ -6917,7 +6847,7 @@ struct tonepair_state { int v1_2; int v2_2; int v3_2; - struct ast_format origwfmt; + struct ast_format *origwfmt; int pos; int duration; int modulate; @@ -6930,8 +6860,10 @@ static void tonepair_release(struct ast_channel *chan, void *params) { struct tonepair_state *ts = params; - if (chan) - ast_set_write_format(chan, &ts->origwfmt); + if (chan) { + ast_set_write_format(chan, ts->origwfmt); + } + ao2_cleanup(ts->origwfmt); ast_free(ts); } @@ -6940,10 +6872,12 @@ static void *tonepair_alloc(struct ast_channel *chan, void *params) struct tonepair_state *ts; struct tonepair_def *td = params; - if (!(ts = ast_calloc(1, sizeof(*ts)))) + if (!(ts = ast_calloc(1, sizeof(*ts)))) { return NULL; - ast_format_copy(&ts->origwfmt, ast_channel_writeformat(chan)); - if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR)) { + } + + ts->origwfmt = ao2_bump(ast_channel_writeformat(chan)); + if (ast_set_write_format(chan, ast_format_slin)) { ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", ast_channel_name(chan)); tonepair_release(NULL, ts); ts = NULL; @@ -6997,7 +6931,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; - ast_format_set(&ts->f.subclass.format, AST_FORMAT_SLINEAR, 0); + ts->f.subclass.format = ast_format_slin; ts->f.datalen = len; ts->f.samples = samples; ts->f.offset = AST_FRIENDLY_OFFSET; @@ -7675,7 +7609,7 @@ static int silence_generator_generate(struct ast_channel *chan, void *data, int .samples = samples, .datalen = sizeof(buf), }; - ast_format_set(&frame.subclass.format, AST_FORMAT_SLINEAR, 0); + frame.subclass.format = ast_format_slin; memset(buf, 0, sizeof(buf)); @@ -7692,7 +7626,7 @@ static struct ast_generator silence_generator = { }; struct ast_silence_generator { - struct ast_format old_write_format; + struct ast_format *old_write_format; }; struct ast_silence_generator *ast_channel_start_silence_generator(struct ast_channel *chan) @@ -7703,9 +7637,9 @@ struct ast_silence_generator *ast_channel_start_silence_generator(struct ast_cha return NULL; } - ast_format_copy(&state->old_write_format, ast_channel_writeformat(chan)); + state->old_write_format = ao2_bump(ast_channel_writeformat(chan)); - if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) { + if (ast_set_write_format(chan, ast_format_slin) < 0) { ast_log(LOG_ERROR, "Could not set write format to SLINEAR\n"); ast_free(state); return NULL; @@ -7749,9 +7683,11 @@ void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_sil if (deactivate_silence_generator(chan)) { ast_debug(1, "Stopped silence generator on '%s'\n", ast_channel_name(chan)); - 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"); + } } + ao2_cleanup(state->old_write_format); ast_free(state); } @@ -10199,8 +10135,8 @@ struct ast_channel *ast_channel_yank(struct ast_channel *yankee) char *context; char *name; int amaflags; - struct ast_format readformat; - struct ast_format writeformat; + struct ast_format *readformat; + struct ast_format *writeformat; } my_vars = { 0, }; ast_channel_lock(yankee); @@ -10209,8 +10145,8 @@ struct ast_channel *ast_channel_yank(struct ast_channel *yankee) my_vars.context = ast_strdupa(ast_channel_context(yankee)); my_vars.name = ast_strdupa(ast_channel_name(yankee)); my_vars.amaflags = ast_channel_amaflags(yankee); - ast_format_copy(&my_vars.writeformat, ast_channel_writeformat(yankee)); - ast_format_copy(&my_vars.readformat, ast_channel_readformat(yankee)); + my_vars.writeformat = ao2_bump(ast_channel_writeformat(yankee)); + my_vars.readformat = ao2_bump(ast_channel_readformat(yankee)); ast_channel_unlock(yankee); /* Do not hold any channel locks while calling channel_alloc() since the function @@ -10218,12 +10154,16 @@ struct ast_channel *ast_channel_yank(struct ast_channel *yankee) if (!(yanked_chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, my_vars.accountcode, my_vars.exten, my_vars.context, NULL, yankee, my_vars.amaflags, "Surrogate/%s", my_vars.name))) { + ao2_cleanup(my_vars.writeformat); + ao2_cleanup(my_vars.readformat); return NULL; } /* Make formats okay */ - ast_format_copy(ast_channel_readformat(yanked_chan), &my_vars.readformat); - ast_format_copy(ast_channel_writeformat(yanked_chan), &my_vars.writeformat); + ast_channel_set_readformat(yanked_chan, my_vars.readformat); + ast_channel_set_writeformat(yanked_chan, my_vars.writeformat); + ao2_cleanup(my_vars.readformat); + ao2_cleanup(my_vars.writeformat); ast_channel_unlock(yanked_chan); diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c index c2dc2d737..4ad0ef3a0 100644 --- a/main/channel_internal_api.c +++ b/main/channel_internal_api.c @@ -176,7 +176,7 @@ struct ast_channel { int fdno; /*!< Which fd had an event detected on */ int streamid; /*!< For streaming playback, the schedule ID */ int vstreamid; /*!< For streaming video playback, the schedule ID */ - struct ast_format oldwriteformat; /*!< Original writer format */ + struct ast_format *oldwriteformat; /*!< Original writer format */ int timingfd; /*!< Timing fd */ enum ast_channel_state state; /*!< State of line -- Don't write directly, use ast_setstate() */ int rings; /*!< Number of rings so far */ @@ -193,10 +193,10 @@ struct ast_channel { struct ast_flags flags; /*!< channel flags of AST_FLAG_ type */ int alertpipe[2]; struct ast_format_cap *nativeformats; /*!< Kinds of data this channel can natively handle */ - struct ast_format readformat; /*!< Requested read format (after translation) */ - struct ast_format writeformat; /*!< Requested write format (after translation) */ - struct ast_format rawreadformat; /*!< Raw read format (before translation) */ - struct ast_format rawwriteformat; /*!< Raw write format (before translation) */ + struct ast_format *readformat; /*!< Requested read format (after translation) */ + struct ast_format *writeformat; /*!< Requested write format (after translation) */ + struct ast_format *rawreadformat; /*!< Raw read format (before translation) */ + struct ast_format *rawwriteformat; /*!< Raw write format (before translation) */ unsigned int emulate_dtmf_duration; /*!< Number of ms left to emulate DTMF for */ #ifdef HAVE_EPOLL int epfd; @@ -826,7 +826,8 @@ struct ast_format_cap *ast_channel_nativeformats(const struct ast_channel *chan) } void ast_channel_nativeformats_set(struct ast_channel *chan, struct ast_format_cap *value) { - chan->nativeformats = value; + ao2_cleanup(chan->nativeformats); + chan->nativeformats = ao2_bump(value); } struct ast_framehook_list *ast_channel_framehooks(const struct ast_channel *chan) { @@ -949,25 +950,50 @@ void ast_channel_state_set(struct ast_channel *chan, enum ast_channel_state valu { chan->state = value; } +void ast_channel_set_oldwriteformat(struct ast_channel *chan, struct ast_format *format) +{ + ao2_cleanup(chan->oldwriteformat); + chan->oldwriteformat = ao2_bump(format); +} +void ast_channel_set_rawreadformat(struct ast_channel *chan, struct ast_format *format) +{ + ao2_cleanup(chan->rawreadformat); + chan->rawreadformat = ao2_bump(format); +} +void ast_channel_set_rawwriteformat(struct ast_channel *chan, struct ast_format *format) +{ + ao2_cleanup(chan->rawwriteformat); + chan->rawwriteformat = ao2_bump(format); +} +void ast_channel_set_readformat(struct ast_channel *chan, struct ast_format *format) +{ + ao2_cleanup(chan->readformat); + chan->readformat = ao2_bump(format); +} +void ast_channel_set_writeformat(struct ast_channel *chan, struct ast_format *format) +{ + ao2_cleanup(chan->writeformat); + chan->writeformat = ao2_bump(format); +} struct ast_format *ast_channel_oldwriteformat(struct ast_channel *chan) { - return &chan->oldwriteformat; + return chan->oldwriteformat; } struct ast_format *ast_channel_rawreadformat(struct ast_channel *chan) { - return &chan->rawreadformat; + return chan->rawreadformat; } struct ast_format *ast_channel_rawwriteformat(struct ast_channel *chan) { - return &chan->rawwriteformat; + return chan->rawwriteformat; } struct ast_format *ast_channel_readformat(struct ast_channel *chan) { - return &chan->readformat; + return chan->readformat; } struct ast_format *ast_channel_writeformat(struct ast_channel *chan) { - return &chan->writeformat; + return chan->writeformat; } struct ast_hangup_handler_list *ast_channel_hangup_handlers(struct ast_channel *chan) { diff --git a/main/cli.c b/main/cli.c index bff58695d..bb8e33e44 100644 --- a/main/cli.c +++ b/main/cli.c @@ -1521,9 +1521,9 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar long elapsed_seconds=0; int hour=0, min=0, sec=0; struct ast_var_t *var; - char nativeformats[256]; struct ast_str *write_transpath = ast_str_alloca(256); struct ast_str *read_transpath = ast_str_alloca(256); + struct ast_str *codec_buf = ast_str_alloca(64); struct ast_bridge *bridge; struct ast_callid *callid; char callid_buf[32]; @@ -1634,9 +1634,9 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar ast_channel_language(chan), ast_state2str(ast_channel_state(chan)), ast_channel_state(chan), - ast_getformatname_multiple(nativeformats, sizeof(nativeformats), ast_channel_nativeformats(chan)), - ast_getformatname(ast_channel_writeformat(chan)), - ast_getformatname(ast_channel_readformat(chan)), + ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf), + ast_format_get_name(ast_channel_writeformat(chan)), + ast_format_get_name(ast_channel_readformat(chan)), ast_str_strlen(write_transpath) ? "Yes" : "No", ast_str_buffer(write_transpath), ast_str_strlen(read_transpath) ? "Yes" : "No", diff --git a/main/codec.c b/main/codec.c new file mode 100644 index 000000000..e060efe3e --- /dev/null +++ b/main/codec.c @@ -0,0 +1,381 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2014, Digium, Inc. + * + * Joshua Colp <jcolp@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 Codecs API + * + * \author Joshua Colp <jcolp@digium.com> + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/logger.h" +#include "asterisk/codec.h" +#include "asterisk/format.h" +#include "asterisk/frame.h" +#include "asterisk/astobj2.h" +#include "asterisk/strings.h" +#include "asterisk/module.h" +#include "asterisk/cli.h" + +/*! \brief Number of buckets to use for codecs (should be prime for performance reasons) */ +#define CODEC_BUCKETS 53 + +/*! \brief Current identifier value for newly registered codec */ +static int codec_id = 1; + +/*! \brief Registered codecs */ +static struct ao2_container *codecs; + +static int codec_hash(const void *obj, int flags) +{ + const struct ast_codec *codec; + const char *key; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_KEY: + key = obj; + return ast_str_hash(key); + case OBJ_SEARCH_OBJECT: + codec = obj; + return ast_str_hash(codec->name); + default: + /* Hash can only work on something with a full key. */ + ast_assert(0); + return 0; + } +} + +static int codec_cmp(void *obj, void *arg, int flags) +{ + const struct ast_codec *left = obj; + const struct ast_codec *right = arg; + const char *right_key = arg; + int cmp; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: + right_key = right->name; + cmp = strcmp(left->name, right_key); + + if (right->type != AST_MEDIA_TYPE_UNKNOWN) { + cmp |= (right->type != left->type); + } + + /* BUGBUG: this will allow a match on a codec by name only. + * This is particularly useful when executed by the CLI; if + * that is not needed in translate.c, this can be removed. + */ + if (right->sample_rate) { + cmp |= (right->sample_rate != left->sample_rate); + } + break; + case OBJ_SEARCH_KEY: + cmp = strcmp(left->name, right_key); + break; + case OBJ_SEARCH_PARTIAL_KEY: + cmp = strncmp(left->name, right_key, strlen(right_key)); + break; + default: + ast_assert(0); + cmp = 0; + break; + } + if (cmp) { + return 0; + } + + return CMP_MATCH; +} + +static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + struct ao2_iterator i; + struct ast_codec *codec; + + switch (cmd) { + case CLI_INIT: + e->command = "core show codecs [audio|video|image|text]"; + e->usage = + "Usage: core show codecs [audio|video|image|text]\n" + " Displays codec mapping\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if ((a->argc < 3) || (a->argc > 4)) { + return CLI_SHOWUSAGE; + } + + if (!ast_opt_dont_warn) { + 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, "%8s %5s %8s %s\n","ID","TYPE","NAME","DESCRIPTION"); + ast_cli(a->fd, "-----------------------------------------------------------------------------------\n"); + + ao2_rdlock(codecs); + i = ao2_iterator_init(codecs, AO2_ITERATOR_DONTLOCK); + + for (; (codec = ao2_iterator_next(&i)); ao2_ref(codec, -1)) { + if (a->argc == 4) { + if (!strcasecmp(a->argv[3], "audio")) { + if (codec->type != AST_MEDIA_TYPE_AUDIO) { + continue; + } + } else if (!strcasecmp(a->argv[3], "video")) { + if (codec->type != AST_MEDIA_TYPE_VIDEO) { + continue; + } + } else if (!strcasecmp(a->argv[3], "image")) { + if (codec->type != AST_MEDIA_TYPE_IMAGE) { + continue; + } + } else if (!strcasecmp(a->argv[3], "text")) { + if (codec->type != AST_MEDIA_TYPE_TEXT) { + continue; + } + } else { + continue; + } + } + + ast_cli(a->fd, "%8u %5s %8s (%s)\n", + codec->id, + ast_codec_media_type2str(codec->type), + codec->name, + codec->description); + } + + ao2_iterator_destroy(&i); + ao2_unlock(codecs); + + return CLI_SUCCESS; +} + +/*! \brief Callback function for getting a codec based on unique identifier */ +static int codec_id_cmp(void *obj, void *arg, int flags) +{ + struct ast_codec *codec = obj; + int *id = arg; + + return (codec->id == *id) ? CMP_MATCH | CMP_STOP : 0; +} + +static char *show_codec(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int type_punned_codec; + struct ast_codec *codec; + + switch (cmd) { + case CLI_INIT: + e->command = "core show codec"; + e->usage = + "Usage: core show codec <number>\n" + " Displays codec mapping\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 4) { + return CLI_SHOWUSAGE; + } + + if (sscanf(a->argv[3], "%30d", &type_punned_codec) != 1) { + return CLI_SHOWUSAGE; + } + + codec = ao2_callback(codecs, 0, codec_id_cmp, &type_punned_codec); + if (!codec) { + ast_cli(a->fd, "Codec %d not found\n", type_punned_codec); + return CLI_SUCCESS; + } + + ast_cli(a->fd, "%11u %s\n", (unsigned int) codec->id, codec->description); + + ao2_ref(codec, -1); + + return CLI_SUCCESS; +} + +/* Builtin Asterisk CLI-commands for debugging */ +static struct ast_cli_entry codec_cli[] = { + AST_CLI_DEFINE(show_codecs, "Displays a list of registered codecs"), + AST_CLI_DEFINE(show_codec, "Shows a specific codec"), +}; + +/*! \brief Function called when the process is shutting down */ +static void codec_shutdown(void) +{ + ast_cli_unregister_multiple(codec_cli, ARRAY_LEN(codec_cli)); + ao2_cleanup(codecs); + codecs = NULL; +} + +int ast_codec_init(void) +{ + codecs = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, CODEC_BUCKETS, codec_hash, codec_cmp); + if (!codecs) { + return -1; + } + + ast_cli_register_multiple(codec_cli, ARRAY_LEN(codec_cli)); + ast_register_atexit(codec_shutdown); + + return 0; +} + +static void codec_dtor(void *obj) +{ + struct ast_codec *codec; + + codec = obj; + + ast_module_unref(codec->mod); +} + +int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod) +{ + SCOPED_AO2WRLOCK(lock, codecs); + struct ast_codec *codec_new; + + /* Some types have specific requirements */ + if (codec->type == AST_MEDIA_TYPE_UNKNOWN) { + ast_log(LOG_ERROR, "A media type must be specified for codec '%s'\n", codec->name); + return -1; + } else if (codec->type == AST_MEDIA_TYPE_AUDIO) { + if (!codec->sample_rate) { + ast_log(LOG_ERROR, "A sample rate must be specified for codec '%s' of type '%s'\n", + codec->name, ast_codec_media_type2str(codec->type)); + return -1; + } + } + + codec_new = ao2_find(codecs, codec, OBJ_SEARCH_OBJECT | OBJ_NOLOCK); + if (codec_new) { + ast_log(LOG_ERROR, "A codec with name '%s' of type '%s' and sample rate '%u' is already registered\n", + codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate); + ao2_ref(codec_new, -1); + return -1; + } + + codec_new = ao2_t_alloc_options(sizeof(*codec_new), codec_dtor, + AO2_ALLOC_OPT_LOCK_NOLOCK, S_OR(codec->description, "")); + if (!codec_new) { + ast_log(LOG_ERROR, "Could not allocate a codec with name '%s' of type '%s' and sample rate '%u'\n", + codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate); + return -1; + } + *codec_new = *codec; + codec_new->id = codec_id++; + + ao2_link_flags(codecs, codec_new, OBJ_NOLOCK); + + /* Once registered a codec can not be unregistered, and the module must persist */ + ast_module_ref(mod); + + ast_verb(2, "Registered '%s' codec '%s' at sample rate '%u' with id '%u'\n", + ast_codec_media_type2str(codec->type), codec->name, codec->sample_rate, codec_new->id); + + ao2_ref(codec_new, -1); + + return 0; +} + +struct ast_codec *ast_codec_get(const char *name, enum ast_media_type type, unsigned int sample_rate) +{ + struct ast_codec codec = { + .name = name, + .type = type, + .sample_rate = sample_rate, + }; + + return ao2_find(codecs, &codec, OBJ_SEARCH_OBJECT); +} + +struct ast_codec *ast_codec_get_by_id(int id) +{ + return ao2_callback(codecs, 0, codec_id_cmp, &id); +} + +int ast_codec_get_max(void) +{ + return codec_id; +} + +const char *ast_codec_media_type2str(enum ast_media_type type) +{ + switch (type) { + case AST_MEDIA_TYPE_AUDIO: + return "audio"; + case AST_MEDIA_TYPE_VIDEO: + return "video"; + case AST_MEDIA_TYPE_IMAGE: + return "image"; + case AST_MEDIA_TYPE_TEXT: + return "text"; + default: + return "<unknown>"; + } +} + +unsigned int ast_codec_samples_count(struct ast_frame *frame) +{ + struct ast_codec *codec; + unsigned int samples = 0; + + if ((frame->frametype != AST_FRAME_VOICE) && + (frame->frametype != AST_FRAME_VIDEO) && + (frame->frametype != AST_FRAME_IMAGE)) { + return 0; + } + + /* BUGBUG - why not just get the codec pointer off the format? + This is a bit roundabout + */ + codec = ast_codec_get_by_id(ast_format_get_codec_id(frame->subclass.format)); + + if (codec->samples_count) { + samples = codec->samples_count(frame); + } else { + ast_log(LOG_WARNING, "Unable to calculate samples for codec %s\n", + ast_format_get_name(frame->subclass.format)); + } + + ao2_ref(codec, -1); + return samples; +} + +unsigned int ast_codec_determine_length(const struct ast_codec *codec, unsigned int samples) +{ + if (!codec->get_length) { + return 0; + } + + return codec->get_length(samples); +} diff --git a/main/codec_builtin.c b/main/codec_builtin.c new file mode 100644 index 000000000..594074aa4 --- /dev/null +++ b/main/codec_builtin.c @@ -0,0 +1,845 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2014, Digium, Inc. + * + * Joshua Colp <jcolp@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 Built-in supported codecs + * + * \author Joshua Colp <jcolp@digium.com> + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/logger.h" +#include "asterisk/astobj2.h" +#include "asterisk/codec.h" +#include "asterisk/format.h" +#include "asterisk/format_cache.h" +#include "asterisk/frame.h" + +enum frame_type { + TYPE_HIGH, /* 0x0 */ + TYPE_LOW, /* 0x1 */ + TYPE_SILENCE, /* 0x2 */ + TYPE_DONTSEND /* 0x3 */ +}; + +#define TYPE_MASK 0x3 + +static int g723_len(unsigned char buf) +{ + enum frame_type type = buf & TYPE_MASK; + + switch(type) { + case TYPE_DONTSEND: + return 0; + break; + case TYPE_SILENCE: + return 4; + break; + case TYPE_HIGH: + return 24; + break; + case TYPE_LOW: + return 20; + break; + default: + ast_log(LOG_WARNING, "Badly encoded frame (%u)\n", type); + } + return -1; +} + +static int g723_samples(struct ast_frame *frame) +{ + unsigned char *buf = frame->data.ptr; + int pos = 0, samples = 0, res; + + while(pos < frame->datalen) { + res = g723_len(buf[pos]); + if (res <= 0) + break; + samples += 240; + pos += res; + } + + return samples; +} + +static int g723_length(unsigned int samples) +{ + return (samples / 240) * 20; +} + +static struct ast_codec g723 = { + .name = "g723", + .description = "G.723.1", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 30, + .maximum_ms = 300, + .default_ms = 30, + .minimum_bytes = 20, + .samples_count = g723_samples, + .get_length = g723_length, +}; + +static int none_samples(struct ast_frame *frame) +{ + return frame->datalen; +} + +static int none_length(unsigned int samples) { + return samples; +} + +static struct ast_codec none = { + .name = "none", + .description = "<Null> codec", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, /* This must have some sample rate to prevent divide by 0 */ + .minimum_ms = 10, + .maximum_ms = 150, + .default_ms = 20, + .minimum_bytes = 20, + .samples_count = none_samples, + .get_length = none_length, +}; + +static int ulaw_samples(struct ast_frame *frame) +{ + return frame->datalen; +} + +static int ulaw_length(unsigned int samples) +{ + return samples; +} + +static struct ast_codec ulaw = { + .name = "ulaw", + .description = "G.711 u-law", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 10, + .maximum_ms = 150, + .default_ms = 20, + .minimum_bytes = 80, + .samples_count = ulaw_samples, + .get_length = ulaw_length, + .smooth = 1, +}; + +static struct ast_codec alaw = { + .name = "alaw", + .description = "G.711 a-law", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 10, + .maximum_ms = 150, + .default_ms = 20, + .minimum_bytes = 80, + .samples_count = ulaw_samples, + .get_length = ulaw_length, + .smooth = 1, +}; + +static int gsm_samples(struct ast_frame *frame) +{ + return 160 * (frame->datalen / 33); +} + +static int gsm_length(unsigned int samples) +{ + return (samples / 160) * 33; +} + +static struct ast_codec gsm = { + .name = "gsm", + .description = "GSM", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 20, + .maximum_ms = 300, + .default_ms = 20, + .minimum_bytes = 33, + .samples_count = gsm_samples, + .get_length = gsm_length, + .smooth = 1, +}; + +static int g726_samples(struct ast_frame *frame) +{ + return frame->datalen * 2; +} + +static int g726_length(unsigned int samples) +{ + return samples / 2; +} + +static struct ast_codec g726rfc3551 = { + .name = "g726", + .description = "G.726 RFC3551", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 10, + .maximum_ms = 300, + .default_ms = 20, + .minimum_bytes = 40, + .samples_count = g726_samples, + .get_length = g726_length, + .smooth = 1, +}; + +static struct ast_codec g726aal2 = { + .name = "g726aal2", + .description = "G.726 AAL2", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 10, + .maximum_ms = 300, + .default_ms = 20, + .minimum_bytes = 40, + .samples_count = g726_samples, + .get_length = g726_length, + .smooth = 1, +}; + +static struct ast_codec adpcm = { + .name = "adpcm", + .description = "Dialogic ADPCM", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 10, + .maximum_ms = 300, + .default_ms = 20, + .minimum_bytes = 40, + .samples_count = g726_samples, + .get_length = g726_length, + .smooth = 1, +}; + +static int slin_samples(struct ast_frame *frame) +{ + return frame->datalen / 2; +} + +static int slin_length(unsigned int samples) +{ + return samples * 2; +} + +static struct ast_codec slin8 = { + .name = "slin", + .description = "16 bit Signed Linear PCM", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 10, + .maximum_ms = 70, + .default_ms = 20, + .minimum_bytes = 160, + .samples_count = slin_samples, + .get_length = slin_length, + .smooth = 1, +}; + +static struct ast_codec slin12 = { + .name = "slin", + .description = "16 bit Signed Linear PCM (12kHz)", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 12000, + .minimum_ms = 10, + .maximum_ms = 70, + .default_ms = 20, + .minimum_bytes = 240, + .samples_count = slin_samples, + .get_length = slin_length, + .smooth = 1, +}; + +static struct ast_codec slin16 = { + .name = "slin", + .description = "16 bit Signed Linear PCM (16kHz)", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 16000, + .minimum_ms = 10, + .maximum_ms = 70, + .default_ms = 20, + .minimum_bytes = 320, + .samples_count = slin_samples, + .get_length = slin_length, + .smooth = 1, +}; + +static struct ast_codec slin24 = { + .name = "slin", + .description = "16 bit Signed Linear PCM (24kHz)", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 24000, + .minimum_ms = 10, + .maximum_ms = 70, + .default_ms = 20, + .minimum_bytes = 480, + .samples_count = slin_samples, + .get_length = slin_length, + .smooth = 1, +}; + +static struct ast_codec slin32 = { + .name = "slin", + .description = "16 bit Signed Linear PCM (32kHz)", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 32000, + .minimum_ms = 10, + .maximum_ms = 70, + .default_ms = 20, + .minimum_bytes = 640, + .samples_count = slin_samples, + .get_length = slin_length, + .smooth = 1, +}; + +static struct ast_codec slin44 = { + .name = "slin", + .description = "16 bit Signed Linear PCM (44kHz)", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 44100, + .minimum_ms = 10, + .maximum_ms = 70, + .default_ms = 20, + .minimum_bytes = 882, + .samples_count = slin_samples, + .get_length = slin_length, + .smooth = 1, +}; + +static struct ast_codec slin48 = { + .name = "slin", + .description = "16 bit Signed Linear PCM (48kHz)", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 48000, + .minimum_ms = 10, + .maximum_ms = 70, + .default_ms = 20, + .minimum_bytes = 960, + .samples_count = slin_samples, + .get_length = slin_length, + .smooth = 1, +}; + +static struct ast_codec slin96 = { + .name = "slin", + .description = "16 bit Signed Linear PCM (96kHz)", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 96000, + .minimum_ms = 10, + .maximum_ms = 70, + .default_ms = 20, + .minimum_bytes = 1920, + .samples_count = slin_samples, + .get_length = slin_length, + .smooth = 1, +}; + +static struct ast_codec slin192 = { + .name = "slin", + .description = "16 bit Signed Linear PCM (192kHz)", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 192000, + .minimum_ms = 10, + .maximum_ms = 70, + .default_ms = 20, + .minimum_bytes = 3840, + .samples_count = slin_samples, + .get_length = slin_length, + .smooth = 1, +}; + +static int lpc10_samples(struct ast_frame *frame) +{ + int samples = 22 * 8; + + /* assumes that the RTP packet contains one LPC10 frame */ + samples += (((char *)(frame->data.ptr))[7] & 0x1) * 8; + + return samples; +} + +static struct ast_codec lpc10 = { + .name = "lpc10", + .description = "LPC10", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 20, + .maximum_ms = 20, + .default_ms = 20, + .minimum_bytes = 7, + .samples_count = lpc10_samples, + .smooth = 1, +}; + +static int g729_samples(struct ast_frame *frame) +{ + return frame->datalen * 8; +} + +static int g729_length(unsigned int samples) +{ + return samples / 8; +} + +static struct ast_codec g729a = { + .name = "g729", + .description = "G.729A", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 10, + .maximum_ms = 230, + .default_ms = 20, + .minimum_bytes = 10, + .samples_count = g729_samples, + .get_length = g729_length, + .smooth = 1, +}; + +static unsigned char get_n_bits_at(unsigned char *data, int n, int bit) +{ + int byte = bit / 8; /* byte containing first bit */ + int rem = 8 - (bit % 8); /* remaining bits in first byte */ + unsigned char ret = 0; + + if (n <= 0 || n > 8) + return 0; + + if (rem < n) { + ret = (data[byte] << (n - rem)); + ret |= (data[byte + 1] >> (8 - n + rem)); + } else { + ret = (data[byte] >> (rem - n)); + } + + return (ret & (0xff >> (8 - n))); +} + +static int speex_get_wb_sz_at(unsigned char *data, int len, int bit) +{ + static const int SpeexWBSubModeSz[] = { + 4, 36, 112, 192, + 352, 0, 0, 0 }; + int off = bit; + unsigned char c; + + /* skip up to two wideband frames */ + if (((len * 8 - off) >= 5) && + get_n_bits_at(data, 1, off)) { + c = get_n_bits_at(data, 3, off + 1); + off += SpeexWBSubModeSz[c]; + + if (((len * 8 - off) >= 5) && + get_n_bits_at(data, 1, off)) { + c = get_n_bits_at(data, 3, off + 1); + off += SpeexWBSubModeSz[c]; + + if (((len * 8 - off) >= 5) && + get_n_bits_at(data, 1, off)) { + ast_log(LOG_WARNING, "Encountered corrupt speex frame; too many wideband frames in a row.\n"); + return -1; + } + } + + } + return off - bit; +} + +static int speex_samples(unsigned char *data, int len) +{ + static const int SpeexSubModeSz[] = { + 5, 43, 119, 160, + 220, 300, 364, 492, + 79, 0, 0, 0, + 0, 0, 0, 0 }; + static const int SpeexInBandSz[] = { + 1, 1, 4, 4, + 4, 4, 4, 4, + 8, 8, 16, 16, + 32, 32, 64, 64 }; + int bit = 0; + int cnt = 0; + int off; + unsigned char c; + + while ((len * 8 - bit) >= 5) { + /* skip wideband frames */ + off = speex_get_wb_sz_at(data, len, bit); + if (off < 0) { + ast_log(LOG_WARNING, "Had error while reading wideband frames for speex samples\n"); + break; + } + bit += off; + + if ((len * 8 - bit) < 5) + break; + + /* get control bits */ + c = get_n_bits_at(data, 5, bit); + bit += 5; + + if (c == 15) { + /* terminator */ + break; + } else if (c == 14) { + /* in-band signal; next 4 bits contain signal id */ + c = get_n_bits_at(data, 4, bit); + bit += 4; + bit += SpeexInBandSz[c]; + } else if (c == 13) { + /* user in-band; next 4 bits contain msg len */ + c = get_n_bits_at(data, 4, bit); + bit += 4; + /* after which it's 5-bit signal id + c bytes of data */ + bit += 5 + c * 8; + } else if (c > 8) { + /* unknown */ + ast_log(LOG_WARNING, "Unknown speex control frame %d\n", c); + break; + } else { + /* skip number bits for submode (less the 5 control bits) */ + bit += SpeexSubModeSz[c] - 5; + cnt += 160; /* new frame */ + } + } + return cnt; +} + +static int speex8_samples(struct ast_frame *frame) +{ + return speex_samples(frame->data.ptr, frame->datalen); +} + +static struct ast_codec speex8 = { + .name = "speex", + .description = "SpeeX", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 10, + .maximum_ms = 60, + .default_ms = 20, + .minimum_bytes = 10, + .samples_count = speex8_samples, +}; + +static int speex16_samples(struct ast_frame *frame) +{ + return 2 * speex_samples(frame->data.ptr, frame->datalen); +} + +static struct ast_codec speex16 = { + .name = "speex", + .description = "SpeeX 16khz", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 16000, + .minimum_ms = 10, + .maximum_ms = 60, + .default_ms = 20, + .minimum_bytes = 10, + .samples_count = speex16_samples, +}; + +static int speex32_samples(struct ast_frame *frame) +{ + return 4 * speex_samples(frame->data.ptr, frame->datalen); +} + +static struct ast_codec speex32 = { + .name = "speex", + .description = "SpeeX 32khz", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 32000, + .minimum_ms = 10, + .maximum_ms = 60, + .default_ms = 20, + .minimum_bytes = 10, + .samples_count = speex32_samples, +}; + +static int ilbc_samples(struct ast_frame *frame) +{ + return 240 * (frame->datalen / 50); +} + +static struct ast_codec ilbc = { + .name = "ilbc", + .description = "iLBC", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 30, + .maximum_ms = 30, + .default_ms = 30, + .minimum_bytes = 50, + .samples_count = ilbc_samples, + .smooth = 1, +}; + +static struct ast_codec g722 = { + .name = "g722", + .description = "G722", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 16000, + .minimum_ms = 10, + .maximum_ms = 150, + .default_ms = 20, + .minimum_bytes = 80, + .samples_count = g726_samples, + .get_length = g726_length, + .smooth = 1, +}; + +static int siren7_samples(struct ast_frame *frame) +{ + return frame->datalen * (16000 / 4000); +} + +static int siren7_length(unsigned int samples) +{ + return samples / (16000 / 4000); +} + +static struct ast_codec siren7 = { + .name = "siren7", + .description = "ITU G.722.1 (Siren7, licensed from Polycom)", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 16000, + .minimum_ms = 20, + .maximum_ms = 80, + .default_ms = 20, + .minimum_bytes = 80, + .samples_count = siren7_samples, + .get_length = siren7_length, +}; + +static int siren14_samples(struct ast_frame *frame) +{ + return (int) frame->datalen * ((float) 32000 / 6000); +} + +static int siren14_length(unsigned int samples) +{ + return (int) samples / ((float) 32000 / 6000);; +} + +static struct ast_codec siren14 = { + .name = "siren14", + .description = "ITU G.722.1 Annex C, (Siren14, licensed from Polycom)", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 32000, + .minimum_ms = 20, + .maximum_ms = 80, + .default_ms = 20, + .minimum_bytes = 120, + .samples_count = siren14_samples, + .get_length = siren14_length, +}; + +static struct ast_codec testlaw = { + .name = "testlaw", + .description = "G.711 test-law", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 8000, + .minimum_ms = 10, + .maximum_ms = 150, + .default_ms = 20, + .minimum_bytes = 80, + .samples_count = ulaw_samples, + .get_length = ulaw_length, + .smooth = 1, +}; + +static int g719_samples(struct ast_frame *frame) +{ + return (int) frame->datalen * ((float) 48000 / 8000); +} + +static int g719_length(unsigned int samples) +{ + return (int) samples / ((float) 48000 / 8000); +} + +static struct ast_codec g719 = { + .name = "g719", + .description = "ITU G.719", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 48000, + .minimum_ms = 20, + .maximum_ms = 80, + .default_ms = 20, + .minimum_bytes = 160, + .samples_count = g719_samples, + .get_length = g719_length, +}; + +static struct ast_codec opus = { + .name = "opus", + .description = "Opus Codec", + .type = AST_MEDIA_TYPE_AUDIO, + .sample_rate = 48000, + .minimum_ms = 20, + .maximum_ms = 60, + .default_ms = 20, + .minimum_bytes = 10, +}; + +static struct ast_codec jpeg = { + .name = "jpeg", + .description = "JPEG image", + .type = AST_MEDIA_TYPE_IMAGE, +}; + +static struct ast_codec png = { + .name = "png", + .description = "PNG Image", + .type = AST_MEDIA_TYPE_IMAGE, +}; + +static struct ast_codec h261 = { + .name = "h261", + .description = "H.261 video", + .type = AST_MEDIA_TYPE_VIDEO, +}; + +static struct ast_codec h263 = { + .name = "h263", + .description = "H.263 video", + .type = AST_MEDIA_TYPE_VIDEO, +}; + +static struct ast_codec h263p = { + .name = "h263p", + .description = "H.263+ video", + .type = AST_MEDIA_TYPE_VIDEO, +}; + +static struct ast_codec h264 = { + .name = "h264", + .description = "H.264 video", + .type = AST_MEDIA_TYPE_VIDEO, +}; + +static struct ast_codec mpeg4 = { + .name = "mpeg4", + .description = "MPEG4 video", + .type = AST_MEDIA_TYPE_VIDEO, +}; + +static struct ast_codec vp8 = { + .name = "vp8", + .description = "VP8 video", + .type = AST_MEDIA_TYPE_VIDEO, +}; + +static struct ast_codec t140red = { + .name = "red", + .description = "T.140 Realtime Text with redundancy", + .type = AST_MEDIA_TYPE_TEXT, +}; + +static struct ast_codec t140 = { + .name = "t140", + .description = "Passthrough T.140 Realtime Text", + .type = AST_MEDIA_TYPE_TEXT, +}; + +#define CODEC_REGISTER_AND_CACHE(codec) \ + ({ \ + int __res_ ## __LINE__ = 0; \ + struct ast_format *__fmt_ ## __LINE__; \ + struct ast_codec *__codec_ ## __LINE__; \ + res |= __ast_codec_register(&(codec), NULL); \ + __codec_ ## __LINE__ = ast_codec_get((codec).name, (codec).type, (codec).sample_rate); \ + __fmt_ ## __LINE__ = ast_format_create(__codec_ ## __LINE__); \ + res |= ast_format_cache_set(__fmt_ ## __LINE__); \ + ao2_ref(__fmt_ ## __LINE__, -1); \ + ao2_ref(__codec_ ## __LINE__, -1); \ + __res_ ## __LINE__; \ + }) + +#define CODEC_REGISTER_AND_CACHE_NAMED(format_name, codec) \ + ({ \ + int __res_ ## __LINE__ = 0; \ + struct ast_format *__fmt_ ## __LINE__; \ + struct ast_codec *__codec_ ## __LINE__; \ + res |= __ast_codec_register(&(codec), NULL); \ + __codec_ ## __LINE__ = ast_codec_get((codec).name, (codec).type, (codec).sample_rate); \ + __fmt_ ## __LINE__ = ast_format_create_named((format_name), __codec_ ## __LINE__); \ + res |= ast_format_cache_set(__fmt_ ## __LINE__); \ + ao2_ref(__fmt_ ## __LINE__, -1); \ + ao2_ref(__codec_ ## __LINE__, -1); \ + __res_ ## __LINE__; \ + }) + +int ast_codec_builtin_init(void) +{ + int res = 0; + + res |= CODEC_REGISTER_AND_CACHE(g723); + res |= CODEC_REGISTER_AND_CACHE(ulaw); + res |= CODEC_REGISTER_AND_CACHE(alaw); + res |= CODEC_REGISTER_AND_CACHE(gsm); + res |= CODEC_REGISTER_AND_CACHE(g726rfc3551); + res |= CODEC_REGISTER_AND_CACHE(g726aal2); + res |= CODEC_REGISTER_AND_CACHE(adpcm); + res |= CODEC_REGISTER_AND_CACHE(slin8); + res |= CODEC_REGISTER_AND_CACHE_NAMED("slin12", slin12); + res |= CODEC_REGISTER_AND_CACHE_NAMED("slin16", slin16); + res |= CODEC_REGISTER_AND_CACHE_NAMED("slin24", slin24); + res |= CODEC_REGISTER_AND_CACHE_NAMED("slin32", slin32); + res |= CODEC_REGISTER_AND_CACHE_NAMED("slin44", slin44); + res |= CODEC_REGISTER_AND_CACHE_NAMED("slin48", slin48); + res |= CODEC_REGISTER_AND_CACHE_NAMED("slin96", slin96); + res |= CODEC_REGISTER_AND_CACHE_NAMED("slin192", slin192); + res |= CODEC_REGISTER_AND_CACHE(lpc10); + res |= CODEC_REGISTER_AND_CACHE(g729a); + res |= CODEC_REGISTER_AND_CACHE(speex8); + res |= CODEC_REGISTER_AND_CACHE_NAMED("speex16", speex16); + res |= CODEC_REGISTER_AND_CACHE_NAMED("speex32", speex32); + res |= CODEC_REGISTER_AND_CACHE(ilbc); + res |= CODEC_REGISTER_AND_CACHE(g722); + res |= CODEC_REGISTER_AND_CACHE(siren7); + res |= CODEC_REGISTER_AND_CACHE(siren14); + res |= CODEC_REGISTER_AND_CACHE(testlaw); + res |= CODEC_REGISTER_AND_CACHE(g719); + res |= CODEC_REGISTER_AND_CACHE(opus); + res |= CODEC_REGISTER_AND_CACHE(jpeg); + res |= CODEC_REGISTER_AND_CACHE(png); + res |= CODEC_REGISTER_AND_CACHE(h261); + res |= CODEC_REGISTER_AND_CACHE(h263); + res |= CODEC_REGISTER_AND_CACHE(h263p); + res |= CODEC_REGISTER_AND_CACHE(h264); + res |= CODEC_REGISTER_AND_CACHE(mpeg4); + res |= CODEC_REGISTER_AND_CACHE(vp8); + res |= CODEC_REGISTER_AND_CACHE(t140red); + res |= CODEC_REGISTER_AND_CACHE(t140); + res |= CODEC_REGISTER_AND_CACHE(none); + + return res; +} diff --git a/main/config_options.c b/main/config_options.c index ae40c6289..c86db0247 100644 --- a/main/config_options.c +++ b/main/config_options.c @@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/xmldoc.h" #include "asterisk/cli.h" #include "asterisk/term.h" +#include "asterisk/format_cap.h" #ifdef LOW_MEMORY #define CONFIG_OPT_BUCKETS 5 @@ -1377,9 +1378,8 @@ static int acl_handler_fn(const struct aco_option *opt, struct ast_variable *var * enum aco_option_type in config_options.h */ static int codec_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj) { - struct ast_codec_pref *pref = (struct ast_codec_pref *)(obj + opt->args[0]); - struct ast_format_cap **cap = (struct ast_format_cap **)(obj + opt->args[1]); - return ast_parse_allow_disallow(pref, *cap, var->value, opt->flags); + struct ast_format_cap **cap = (struct ast_format_cap **)(obj + opt->args[0]); + return ast_format_cap_update_by_allow_disallow(*cap, var->value, opt->flags); } /*! \brief Default option handler for stringfields diff --git a/main/core_local.c b/main/core_local.c index e803f29ec..e1b66d0a7 100644 --- a/main/core_local.c +++ b/main/core_local.c @@ -1008,7 +1008,8 @@ static void local_shutdown(void) ao2_ref(locals, -1); locals = NULL; - ast_format_cap_destroy(local_tech.capabilities); + ao2_cleanup(local_tech.capabilities); + local_tech.capabilities = NULL; STASIS_MESSAGE_TYPE_CLEANUP(ast_local_optimization_begin_type); STASIS_MESSAGE_TYPE_CLEANUP(ast_local_optimization_end_type); @@ -1030,14 +1031,15 @@ int ast_local_init(void) return -1; } - if (!(local_tech.capabilities = ast_format_cap_alloc(0))) { + if (!(local_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { return -1; } - ast_format_cap_add_all(local_tech.capabilities); + ast_format_cap_append_by_type(local_tech.capabilities, AST_MEDIA_TYPE_UNKNOWN); locals = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, locals_cmp_cb); if (!locals) { - ast_format_cap_destroy(local_tech.capabilities); + ao2_cleanup(local_tech.capabilities); + local_tech.capabilities = NULL; return -1; } @@ -1045,7 +1047,8 @@ int ast_local_init(void) if (ast_channel_register(&local_tech)) { ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n"); ao2_ref(locals, -1); - ast_format_cap_destroy(local_tech.capabilities); + ao2_cleanup(local_tech.capabilities); + local_tech.capabilities = NULL; return -1; } ast_cli_register_multiple(cli_local, ARRAY_LEN(cli_local)); diff --git a/main/core_unreal.c b/main/core_unreal.c index c9afa5194..a1ae897b9 100644 --- a/main/core_unreal.c +++ b/main/core_unreal.c @@ -859,7 +859,8 @@ void ast_unreal_destructor(void *vdoomed) { struct ast_unreal_pvt *doomed = vdoomed; - doomed->reqcap = ast_format_cap_destroy(doomed->reqcap); + ao2_cleanup(doomed->reqcap); + doomed->reqcap = NULL; } struct ast_unreal_pvt *ast_unreal_alloc(size_t size, ao2_destructor_fn destructor, struct ast_format_cap *cap) @@ -878,11 +879,13 @@ struct ast_unreal_pvt *ast_unreal_alloc(size_t size, ao2_destructor_fn destructo if (!unreal) { return NULL; } - unreal->reqcap = ast_format_cap_dup(cap); + + unreal->reqcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); if (!unreal->reqcap) { ao2_ref(unreal, -1); return NULL; } + ast_format_cap_append_from_cap(unreal->reqcap, cap, AST_MEDIA_TYPE_UNKNOWN); memcpy(&unreal->jb_conf, &jb_conf, sizeof(unreal->jb_conf)); @@ -896,7 +899,7 @@ struct ast_channel *ast_unreal_new_channels(struct ast_unreal_pvt *p, { struct ast_channel *owner; struct ast_channel *chan; - struct ast_format fmt; + RAII_VAR(struct ast_format *, fmt, NULL, ao2_cleanup); struct ast_assigned_ids id1 = {NULL, NULL}; struct ast_assigned_ids id2 = {NULL, NULL}; int generated_seqno = ast_atomic_fetchadd_int((int *) &name_sequence, +1); @@ -940,14 +943,22 @@ struct ast_channel *ast_unreal_new_channels(struct ast_unreal_pvt *p, ao2_ref(p, +1); ast_channel_tech_pvt_set(owner, p); - ast_format_cap_copy(ast_channel_nativeformats(owner), p->reqcap); + ast_channel_nativeformats_set(owner, p->reqcap); /* Determine our read/write format and set it on each channel */ - ast_best_codec(p->reqcap, &fmt); - ast_format_copy(ast_channel_writeformat(owner), &fmt); - ast_format_copy(ast_channel_rawwriteformat(owner), &fmt); - ast_format_copy(ast_channel_readformat(owner), &fmt); - ast_format_copy(ast_channel_rawreadformat(owner), &fmt); + fmt = ast_format_cap_get_format(p->reqcap, 0); + if (!fmt) { + ast_channel_tech_pvt_set(owner, NULL); + ao2_ref(p, -1); + ast_channel_unlock(owner); + ast_channel_release(owner); + return NULL; + } + + ast_channel_set_writeformat(owner, fmt); + ast_channel_set_rawwriteformat(owner, fmt); + ast_channel_set_readformat(owner, fmt); + ast_channel_set_rawreadformat(owner, fmt); ast_set_flag(ast_channel_flags(owner), AST_FLAG_DISABLE_DEVSTATE_CACHE); @@ -955,6 +966,7 @@ struct ast_channel *ast_unreal_new_channels(struct ast_unreal_pvt *p, if (ast_channel_cc_params_init(owner, requestor ? ast_channel_get_cc_config_params((struct ast_channel *) requestor) : NULL)) { + ast_channel_tech_pvt_set(owner, NULL); ao2_ref(p, -1); ast_channel_tech_pvt_set(owner, NULL); ast_channel_unlock(owner); @@ -970,6 +982,7 @@ struct ast_channel *ast_unreal_new_channels(struct ast_unreal_pvt *p, "%s/%s-%08x;2", tech->type, p->name, (unsigned)generated_seqno); if (!chan) { ast_log(LOG_WARNING, "Unable to allocate chan channel structure\n"); + ast_channel_tech_pvt_set(owner, NULL); ao2_ref(p, -1); ast_channel_tech_pvt_set(owner, NULL); ast_channel_release(owner); @@ -984,13 +997,13 @@ struct ast_channel *ast_unreal_new_channels(struct ast_unreal_pvt *p, ao2_ref(p, +1); ast_channel_tech_pvt_set(chan, p); - ast_format_cap_copy(ast_channel_nativeformats(chan), p->reqcap); + ast_channel_nativeformats_set(chan, p->reqcap); /* Format was already determined when setting up owner */ - ast_format_copy(ast_channel_writeformat(chan), &fmt); - ast_format_copy(ast_channel_rawwriteformat(chan), &fmt); - ast_format_copy(ast_channel_readformat(chan), &fmt); - ast_format_copy(ast_channel_rawreadformat(chan), &fmt); + ast_channel_set_writeformat(chan, fmt); + ast_channel_set_rawwriteformat(chan, fmt); + ast_channel_set_readformat(chan, fmt); + ast_channel_set_rawreadformat(chan, fmt); ast_set_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_DEVSTATE_CACHE); diff --git a/main/data.c b/main/data.c index 092d7aad5..746c52dfd 100644 --- a/main/data.c +++ b/main/data.c @@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/manager.h" #include "asterisk/test.h" #include "asterisk/frame.h" +#include "asterisk/codec.h" /*** DOCUMENTATION <manager name="DataGet" language="en_US"> @@ -3097,62 +3098,69 @@ static int manager_data_get(struct mansession *s, const struct message *m) return RESULT_SUCCESS; } +static int data_add_codec(struct ast_data *codecs, struct ast_format *format) { + struct ast_data *codec; + struct ast_codec *tmp; + + tmp = ast_codec_get_by_id(ast_format_get_codec_id(format)); + if (!tmp) { + return -1; + } + + codec = ast_data_add_node(codecs, "codec"); + if (!codec) { + ao2_ref(tmp, -1); + return -1; + } + + ast_data_add_str(codec, "name", tmp->name); + ast_data_add_int(codec, "samplespersecond", tmp->sample_rate); + ast_data_add_str(codec, "description", tmp->description); + ast_data_add_int(codec, "frame_length", tmp->minimum_bytes); + ao2_ref(tmp, -1); + + return 0; +} + 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; - const struct ast_format_list *fmlist; - int x; + struct ast_data *codecs; codecs = ast_data_add_node(root, node_name); if (!codecs) { return -1; } - fmlist = ast_format_list_get(&fmlist_size); - for (x = 0; x < fmlist_size; x++) { - if (ast_format_cmp(&fmlist[x].format, format) == AST_FORMAT_CMP_EQUAL) { - codec = ast_data_add_node(codecs, "codec"); - if (!codec) { - ast_format_list_destroy(fmlist); - 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); - } - } - ast_format_list_destroy(fmlist); - return 0; + return data_add_codec(codecs, format); } 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; - int x; + struct ast_data *codecs; + size_t i; + size_t count; codecs = ast_data_add_node(root, node_name); if (!codecs) { return -1; } - fmlist = ast_format_list_get(&fmlist_size); - for (x = 0; x < fmlist_size; x++) { - if (ast_format_cap_iscompatible(cap, &fmlist[x].format)) { - codec = ast_data_add_node(codecs, "codec"); - if (!codec) { - ast_format_list_destroy(fmlist); - 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); + + count = ast_format_cap_count(cap); + for (i = 1; i <= count; ++i) { + struct ast_format *fmt; + + fmt = ast_format_cap_get_format(cap, i); + if (!fmt) { + return -1; } + + if (data_add_codec(codecs, fmt)) { + ao2_ref(fmt, -1); + return -1; + } + + ao2_ref(fmt, -1); } - ast_format_list_destroy(fmlist); return 0; } diff --git a/main/dial.c b/main/dial.c index f03d43d25..0955aad04 100644 --- a/main/dial.c +++ b/main/dial.c @@ -300,23 +300,23 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe /* Copy device string over */ ast_copy_string(numsubst, channel->device, sizeof(numsubst)); - if (!ast_format_cap_is_empty(cap)) { + if (ast_format_cap_count(cap)) { cap_request = cap; } else if (chan) { cap_request = ast_channel_nativeformats(chan); } else { - cap_all_audio = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK); - ast_format_cap_add_all_by_type(cap_all_audio, AST_FORMAT_TYPE_AUDIO); + cap_all_audio = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + ast_format_cap_append_by_type(cap_all_audio, AST_MEDIA_TYPE_AUDIO); cap_request = cap_all_audio; } /* If we fail to create our owner channel bail out */ if (!(channel->owner = ast_request(channel->tech, cap_request, &assignedids, chan, numsubst, &channel->cause))) { - cap_all_audio = ast_format_cap_destroy(cap_all_audio); + ao2_cleanup(cap_all_audio); return -1; } cap_request = NULL; - cap_all_audio = ast_format_cap_destroy(cap_all_audio); + ao2_cleanup(cap_all_audio); ast_channel_lock(channel->owner); ast_channel_stage_snapshot(channel->owner); diff --git a/main/dsp.c b/main/dsp.c index 747ff28e5..3a5d1221a 100644 --- a/main/dsp.c +++ b/main/dsp.c @@ -60,6 +60,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include <math.h> #include "asterisk/frame.h" +#include "asterisk/format_cache.h" #include "asterisk/channel.h" #include "asterisk/dsp.h" #include "asterisk/ulaw.h" @@ -1183,7 +1184,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 (!ast_format_is_slinear(&inf->subclass.format)) { + if (!ast_format_cache_is_slinear(inf->subclass.format)) { ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n"); return 0; } @@ -1408,30 +1409,29 @@ static int ast_dsp_silence_noise_with_energy(struct ast_dsp *dsp, struct ast_fra ast_log(LOG_WARNING, "Can't calculate silence on a non-voice frame\n"); return 0; } - if (!ast_format_is_slinear(&f->subclass.format)) { + + if (ast_format_cache_is_slinear(f->subclass.format)) { + s = f->data.ptr; + len = f->datalen/2; + } else { odata = f->data.ptr; len = f->datalen; - switch (f->subclass.format.id) { - case AST_FORMAT_ULAW: - s = ast_alloca(len * 2); - for (x = 0; x < len; x++) { - s[x] = AST_MULAW(odata[x]); - } - break; - case AST_FORMAT_ALAW: - s = ast_alloca(len * 2); - for (x = 0; x < len; x++) { - s[x] = AST_ALAW(odata[x]); - } - break; - default: - ast_log(LOG_WARNING, "Can only calculate silence on signed-linear, alaw or ulaw frames :(\n"); + if (ast_format_cmp(f->subclass.format, ast_format_ulaw)) { + s = ast_alloca(len * 2); + for (x = 0; x < len; x++) { + s[x] = AST_MULAW(odata[x]); + } + } else if (ast_format_cmp(f->subclass.format, ast_format_alaw)) { + s = ast_alloca(len * 2); + for (x = 0; x < len; x++) { + s[x] = AST_ALAW(odata[x]); + } + } else { + ast_log(LOG_WARNING, "Can only calculate silence on signed-linear, alaw or ulaw frames :(\n"); return 0; } - } else { - s = f->data.ptr; - len = f->datalen/2; } + if (noise) { return __ast_dsp_silence_noise(dsp, s, len, NULL, total, frames_energy); } else { @@ -1476,31 +1476,26 @@ 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 */ - if (ast_format_is_slinear(&af->subclass.format)) { + if (ast_format_cache_is_slinear(af->subclass.format)) { shortdata = af->data.ptr; len = af->datalen / 2; + } else if (ast_format_cmp(af->subclass.format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) { + shortdata = ast_alloca(af->datalen * 2); + for (x = 0; x < len; x++) { + shortdata[x] = AST_MULAW(odata[x]); + } + } else if (ast_format_cmp(af->subclass.format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) { + shortdata = ast_alloca(af->datalen * 2); + for (x = 0; x < len; x++) { + shortdata[x] = AST_ALAW(odata[x]); + } } else { - switch (af->subclass.format.id) { - case AST_FORMAT_ULAW: - case AST_FORMAT_TESTLAW: - shortdata = ast_alloca(af->datalen * 2); - for (x = 0; x < len; x++) { - shortdata[x] = AST_MULAW(odata[x]); - } - break; - case AST_FORMAT_ALAW: - shortdata = ast_alloca(af->datalen * 2); - for (x = 0; x < len; x++) { - shortdata[x] = AST_ALAW(odata[x]); - } - break; - 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.format)); - dsp->display_inband_dtmf_warning = 0; - return af; + /*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_format_get_name(af->subclass.format)); } + dsp->display_inband_dtmf_warning = 0; + return af; } /* Initially we do not want to mute anything */ @@ -1629,19 +1624,14 @@ 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.format.id) { - case AST_FORMAT_ULAW: + if (ast_format_cmp(af->subclass.format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) { for (x = 0; x < len; x++) { odata[x] = AST_LIN2MU((unsigned short) shortdata[x]); } - break; - case AST_FORMAT_ALAW: + } else if (ast_format_cmp(af->subclass.format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) { for (x = 0; x < len; x++) { odata[x] = AST_LIN2A((unsigned short) shortdata[x]); } - /* fall through */ - default: - break; } if (outf) { diff --git a/main/file.c b/main/file.c index df67fbf43..fa4c63bd9 100644 --- a/main/file.c +++ b/main/file.c @@ -189,8 +189,8 @@ int ast_stopstream(struct ast_channel *tmp) if (ast_channel_stream(tmp)) { ast_closestream(ast_channel_stream(tmp)); ast_channel_stream_set(tmp, NULL); - if (ast_channel_oldwriteformat(tmp)->id && ast_set_write_format(tmp, ast_channel_oldwriteformat(tmp))) - ast_log(LOG_WARNING, "Unable to restore format back to %s\n", ast_getformatname(ast_channel_oldwriteformat(tmp))); + if (ast_channel_oldwriteformat(tmp) && ast_set_write_format(tmp, ast_channel_oldwriteformat(tmp))) + ast_log(LOG_WARNING, "Unable to restore format back to %s\n", ast_format_get_name(ast_channel_oldwriteformat(tmp))); } /* Stop the video stream too */ if (ast_channel_vstream(tmp) != NULL) { @@ -207,10 +207,10 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f) { int res = -1; if (f->frametype == AST_FRAME_VIDEO) { - if (AST_FORMAT_GET_TYPE(fs->fmt->format.id) == AST_FORMAT_TYPE_AUDIO) { + if (ast_format_get_type(fs->fmt->format) == AST_MEDIA_TYPE_AUDIO) { /* This is the audio portion. Call the video one... */ if (!fs->vfs && fs->filename) { - const char *type = ast_getformatname(&f->subclass.format); + const char *type = ast_format_get_name(f->subclass.format); fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode); ast_debug(1, "Opened video output file\n"); } @@ -223,7 +223,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 (ast_format_cmp(&f->subclass.format, &fs->fmt->format) != AST_FORMAT_CMP_NOT_EQUAL) { + 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"); @@ -232,18 +232,19 @@ 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 && (ast_format_cmp(&f->subclass.format, &fs->lastwriteformat) != AST_FORMAT_CMP_EQUAL)) { + 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.format); - if (!fs->trans) + if (!fs->trans) { + 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.format)); - else { + fs->fmt->name, ast_format_get_name(f->subclass.format)); + } else { struct ast_frame *trf; - ast_format_copy(&fs->lastwriteformat, &f->subclass.format); + ao2_replace(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; @@ -349,7 +350,7 @@ static int exts_compare(const char *exts, const char *type) */ static void filestream_close(struct ast_filestream *f) { - enum ast_format_type format_type = AST_FORMAT_GET_TYPE(f->fmt->format.id); + enum ast_media_type format_type = ast_format_get_type(f->fmt->format); if (!f->owner) { return; @@ -358,12 +359,12 @@ static void filestream_close(struct ast_filestream *f) /* Stop a running stream if there is one */ switch (format_type) { - case AST_FORMAT_TYPE_AUDIO: + case AST_MEDIA_TYPE_AUDIO: ast_channel_stream_set(f->owner, NULL); AST_SCHED_DEL_ACCESSOR(ast_channel_sched(f->owner), f->owner, ast_channel_streamid, ast_channel_streamid_set); ast_settimeout(f->owner, 0, NULL, NULL); break; - case AST_FORMAT_TYPE_VIDEO: + case AST_MEDIA_TYPE_VIDEO: ast_channel_vstream_set(f->owner, NULL); AST_SCHED_DEL_ACCESSOR(ast_channel_sched(f->owner), f->owner, ast_channel_vstreamid, ast_channel_vstreamid_set); break; @@ -418,6 +419,8 @@ static void filestream_destructor(void *arg) } if (f->orig_chan_name) free((void *) f->orig_chan_name); + ao2_cleanup(f->lastwriteformat); + ao2_cleanup(f->fr.subclass.format); ast_module_unref(f->fmt->module); } @@ -436,6 +439,15 @@ static struct ast_filestream *get_filestream(struct ast_format_def *fmt, FILE *b if (fmt->buf_size) s->buf = (char *)(s + 1); s->fr.src = fmt->name; + + if (ast_format_get_type(fmt->format) == AST_MEDIA_TYPE_AUDIO) { + s->fr.frametype = AST_FRAME_VOICE; + } else if (ast_format_get_type(fmt->format) == AST_MEDIA_TYPE_VIDEO) { + s->fr.frametype = AST_FRAME_VIDEO; + } + s->fr.mallocd = 0; + s->fr.subclass.format = ao2_bump(fmt->format); + return s; } @@ -529,9 +541,9 @@ static int filehelper(const char *filename, const void *arg2, const char *fmt, c FILE *bfile; struct ast_filestream *s; - if ((ast_format_cmp(ast_channel_writeformat(chan), &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))) { + if ((ast_format_cmp(ast_channel_writeformat(chan), f->format) == AST_FORMAT_CMP_NOT_EQUAL) && + !(((ast_format_get_type(f->format) == AST_MEDIA_TYPE_AUDIO) && fmt) || + ((ast_format_get_type(f->format) == AST_MEDIA_TYPE_VIDEO) && fmt))) { ast_free(fn); continue; /* not a supported format */ } @@ -559,7 +571,7 @@ static int filehelper(const char *filename, const void *arg2, const char *fmt, c s->fmt = f; s->trans = NULL; s->filename = NULL; - if (AST_FORMAT_GET_TYPE(s->fmt->format.id) == AST_FORMAT_TYPE_AUDIO) { + if (ast_format_get_type(s->fmt->format) == AST_MEDIA_TYPE_AUDIO) { if (ast_channel_stream(chan)) ast_closestream(ast_channel_stream(chan)); ast_channel_stream_set(chan, s); @@ -579,7 +591,7 @@ static int filehelper(const char *filename, const void *arg2, const char *fmt, c /* 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); + ast_format_cap_append((struct ast_format_cap *) arg2, f->format, 0); } res = 1; /* file does exist and format it exists in is returned in arg2 */ break; @@ -749,23 +761,23 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char buflen = strlen(preflang) + strlen(filename) + 4; buf = ast_alloca(buflen); - if (!(file_fmt_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK))) { + if (!(file_fmt_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { 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_format_cap_has_type(file_fmt_cap, AST_MEDIA_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); + ao2_ref(file_fmt_cap, -1); return NULL; } /* Set the channel to a format we can work with and save off the previous format. */ - ast_format_copy(ast_channel_oldwriteformat(chan), ast_channel_writeformat(chan)); + ast_channel_set_oldwriteformat(chan, ast_channel_writeformat(chan)); /* 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); + ao2_ref(file_fmt_cap, -1); if (res == -1) { /* No format available that works with this channel */ return NULL; @@ -781,50 +793,50 @@ 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. */ - struct ast_format tmp_fmt; struct ast_format_cap *tmp_cap; char *buf; int buflen; - const char *fmt; - int fd; + int i, fd; - if (preflang == NULL) + if (preflang == NULL) { preflang = ""; + } buflen = strlen(preflang) + strlen(filename) + 4; buf = ast_alloca(buflen); /* is the channel capable of video without translation ?*/ - if (!ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_FORMAT_TYPE_VIDEO)) { + if (!ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_MEDIA_TYPE_VIDEO)) { return NULL; } - if (!(tmp_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK))) { + if (!(tmp_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { 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); + ao2_ref(tmp_cap, -1); return NULL; } /* 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(ast_channel_nativeformats(chan), &tmp_fmt)) { + for (i = 0; i < ast_format_cap_count(tmp_cap); ++i) { + struct ast_format *format = ast_format_cap_get_format(tmp_cap, i); + + if ((ast_format_get_type(format) != AST_MEDIA_TYPE_VIDEO) || + !ast_format_cap_iscompatible(ast_channel_nativeformats(chan), tmp_cap)) { + ao2_ref(format, -1); continue; } - fd = filehelper(buf, chan, fmt, ACTION_OPEN); + fd = filehelper(buf, chan, ast_format_get_name(format), ACTION_OPEN); if (fd >= 0) { - ast_format_cap_iter_end(tmp_cap); - tmp_cap = ast_format_cap_destroy(tmp_cap); + ao2_ref(format, -1); + ao2_ref(tmp_cap, -1); return ast_channel_vstream(chan); } ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename); + ao2_ref(format, -1); } - ast_format_cap_iter_end(tmp_cap); - tmp_cap = ast_format_cap_destroy(tmp_cap); + ao2_ref(tmp_cap, -1); return NULL; } @@ -897,14 +909,14 @@ static enum fsread_res ast_readaudio_callback(struct ast_filestream *s) if (whennext != s->lasttimeout) { if (ast_channel_timingfd(s->owner) > -1) { - float samp_rate = (float) ast_format_rate(&s->fmt->format); + float samp_rate = (float) ast_format_get_sample_rate(s->fmt->format); unsigned int rate; rate = (unsigned int) roundf(samp_rate / ((float) whennext)); ast_settimeout_full(s->owner, rate, ast_fsread_audio, s, 1); } else { - ast_channel_streamid_set(s->owner, ast_sched_add(ast_channel_sched(s->owner), whennext / (ast_format_rate(&s->fmt->format) / 1000), ast_fsread_audio, s)); + ast_channel_streamid_set(s->owner, ast_sched_add(ast_channel_sched(s->owner), whennext / (ast_format_get_sample_rate(s->fmt->format) / 1000), ast_fsread_audio, s)); } s->lasttimeout = whennext; return FSREAD_SUCCESS_NOSCHED; @@ -954,7 +966,7 @@ static enum fsread_res ast_readvideo_callback(struct ast_filestream *s) } if (whennext != s->lasttimeout) { - ast_channel_vstreamid_set(s->owner, ast_sched_add(ast_channel_sched(s->owner), whennext / (ast_format_rate(&s->fmt->format) / 1000), ast_fsread_video, s)); + ast_channel_vstreamid_set(s->owner, ast_sched_add(ast_channel_sched(s->owner), whennext / (ast_format_get_sample_rate(s->fmt->format) / 1000), ast_fsread_video, s)); s->lasttimeout = whennext; return FSREAD_SUCCESS_NOSCHED; } @@ -985,7 +997,7 @@ int ast_playstream(struct ast_filestream *s) { enum fsread_res res; - if (AST_FORMAT_GET_TYPE(s->fmt->format.id) == AST_FORMAT_TYPE_AUDIO) + if (ast_format_get_type(s->fmt->format) == AST_MEDIA_TYPE_AUDIO) res = ast_readaudio_callback(s); else res = ast_readvideo_callback(s); @@ -1067,14 +1079,15 @@ int ast_streamfile(struct ast_channel *chan, const char *filename, const char *p { struct ast_filestream *fs; struct ast_filestream *vfs=NULL; - char fmt[256]; off_t pos; int seekattempt; int res; fs = ast_openstream(chan, filename, preflang); if (!fs) { - ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", filename, ast_getformatname_multiple(fmt, sizeof(fmt), ast_channel_nativeformats(chan)), strerror(errno)); + struct ast_str *codec_buf = ast_str_alloca(64); + ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", + filename, ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf), strerror(errno)); return -1; } @@ -1096,7 +1109,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_format_get_name(vfs->fmt->format)); } if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_MASQ_NOSTREAM)) @@ -1108,7 +1121,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", ast_channel_name(chan), filename, ast_getformatname(ast_channel_writeformat(chan)), preflang ? preflang : "default"); + ast_verb(3, "<%s> Playing '%s.%s' (language '%s')\n", ast_channel_name(chan), filename, ast_format_get_name(ast_channel_writeformat(chan)), preflang ? preflang : "default"); return res; } @@ -1330,7 +1343,7 @@ static void waitstream_control(struct ast_channel *c, } if (cb) { - long ms_len = ast_tellstream(ast_channel_stream(c)) / (ast_format_rate(&ast_channel_stream(c)->fmt->format) / 1000); + long ms_len = ast_tellstream(ast_channel_stream(c)) / (ast_format_get_sample_rate(ast_channel_stream(c)->fmt->format) / 1000); cb(c, ms_len, type); } @@ -1373,7 +1386,7 @@ static int waitstream_core(struct ast_channel *c, orig_chan_name = ast_strdupa(ast_channel_name(c)); if (ast_channel_stream(c) && cb) { - long ms_len = ast_tellstream(ast_channel_stream(c)) / (ast_format_rate(&ast_channel_stream(c)->fmt->format) / 1000); + long ms_len = ast_tellstream(ast_channel_stream(c)) / (ast_format_get_sample_rate(ast_channel_stream(c)->fmt->format) / 1000); cb(c, ms_len, AST_WAITSTREAM_CB_START); } @@ -1722,7 +1735,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_format_get_name(f->format), f->name, f->exts); count_fmt++; } AST_RWLIST_UNLOCK(&formats); @@ -1732,13 +1745,13 @@ static char *handle_cli_core_show_file_formats(struct ast_cli_entry *e, int cmd, #undef FORMAT2 } -const struct ast_format *ast_get_format_for_file_ext(const char *file_ext) +struct ast_format *ast_get_format_for_file_ext(const char *file_ext) { struct ast_format_def *f; SCOPED_RDLOCK(lock, &formats.lock); AST_RWLIST_TRAVERSE(&formats, f, list) { if (exts_compare(f->exts, file_ext)) { - return &f->format; + return f->format; } } diff --git a/main/format.c b/main/format.c index c4ad45b7f..83206f28c 100644 --- a/main/format.c +++ b/main/format.c @@ -1,10 +1,9 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 2010, Digium, Inc. + * Copyright (C) 2014, Digium, Inc. * - * David Vossel <dvossel@digium.com> - * Mark Spencer <markster@digium.com> + * Joshua Colp <jcolp@digium.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact @@ -17,12 +16,11 @@ * at the top of the source tree. */ -/*! - * \file - * \brief Format API +/*! \file * - * \author David Vossel <dvossel@digium.com> - * \author Mark Spencer <markster@digium.com> + * \brief Media Format API + * + * \author Joshua Colp <jcolp@digium.com> */ /*** MODULEINFO @@ -31,1420 +29,351 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") -#include "asterisk/_private.h" +#include "asterisk/logger.h" +#include "asterisk/codec.h" #include "asterisk/format.h" #include "asterisk/astobj2.h" -#include "asterisk/lock.h" -#include "asterisk/frame.h" -#include "asterisk/utils.h" -#include "asterisk/cli.h" -#include "asterisk/rtp_engine.h" -#include "asterisk/config.h" - -#define FORMAT_CONFIG "codecs.conf" - -/*! - * \brief Container for all the format attribute interfaces. - * \note This container uses RWLOCKs instead of MUTEX locks. . - * \note An ao2 container was chosen for fast lookup. - */ -static struct ao2_container *interfaces; - -/*! 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; +#include "asterisk/strings.h" + +/*! \brief Number of buckets to use for format interfaces (should be prime for performance reasons) */ +#define FORMAT_INTERFACE_BUCKETS 53 + +/*! \brief Definition of a media format */ +struct ast_format { + /*! Name of the format */ + const char *name; + /*! \brief Pointer to the codec in use for this format */ + struct ast_codec *codec; + /*! \brief Attribute specific data, implementation specific */ + void *attribute_data; + /*! \brief Pointer to the optional format interface */ + const struct ast_format_interface *interface; }; -/*! \brief Format List container, This container is never directly accessed outside - * of this file, and It only exists for building the format_list_array. */ -static struct ao2_container *format_list; -/*! \brief Format List array is a read only array protected by a read write lock. - * This array may be used outside this file with the use of reference counting to - * guarantee safety for access by multiple threads. */ -static struct ast_format_list *format_list_array; -static size_t format_list_array_len = 0; -/*! \brief Locks the format list array so a reference can be taken safely. */ -static ast_rwlock_t format_list_array_lock; - -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; -} - -void ast_format_copy(struct ast_format *dst, const struct ast_format *src) -{ - *dst = *src; -} - -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 tmp_wrapper = { - .id = format->id, - }; +/*! \brief Structure used when registering a format interface */ +struct format_interface { + /*! \brief Pointer to the format interface itself */ + const struct ast_format_interface *interface; + /*! \brief Name of the codec the interface is for */ + char codec[0]; +}; - return ao2_find(interfaces, &tmp_wrapper, OBJ_POINTER); -} +/*! \brief Container for registered format interfaces */ +static struct ao2_container *interfaces; -static int has_interface(const struct ast_format *format) +static int format_interface_hash(const void *obj, int flags) { - struct interface_ao2_wrapper *wrapper; + const struct format_interface *format_interface; + const char *key; - wrapper = find_interface(format); - if (!wrapper) { + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_KEY: + key = obj; + return ast_str_hash(key); + case OBJ_SEARCH_OBJECT: + format_interface = obj; + return ast_str_hash(format_interface->codec); + default: + /* Hash can only work on something with a full key. */ + ast_assert(0); return 0; } - ao2_ref(wrapper, -1); - return 1; } -int ast_format_sdp_parse(struct ast_format *format, const char *attributes) +static int format_interface_cmp(void *obj, void *arg, int flags) { - struct interface_ao2_wrapper *wrapper; - int res; + const struct format_interface *left = obj; + const struct format_interface *right = arg; + const char *right_key = arg; + int cmp; - if (!(wrapper = find_interface(format))) { - return 0; + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: + cmp = strcmp(left->codec, right->codec); + break; + case OBJ_SEARCH_KEY: + cmp = strcmp(left->codec, right_key); + break; + case OBJ_SEARCH_PARTIAL_KEY: + cmp = strncmp(left->codec, right_key, strlen(right_key)); + break; + default: + ast_assert(0); + cmp = 0; + break; } - - ao2_rdlock(wrapper); - if (!wrapper->interface || !wrapper->interface->format_attr_sdp_parse) { - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); + if (cmp) { return 0; } - res = wrapper->interface->format_attr_sdp_parse(&format->fattr, attributes); - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); - - return res; + return CMP_MATCH; } -void ast_format_sdp_generate(const struct ast_format *format, unsigned int payload, struct ast_str **str) +/*! \brief Function called when the process is shutting down */ +static void format_shutdown(void) { - struct interface_ao2_wrapper *wrapper; - - if (!(wrapper = find_interface(format))) { - return; - } - - ao2_rdlock(wrapper); - if (!wrapper->interface || !wrapper->interface->format_attr_sdp_generate) { - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); - return; - } - - wrapper->interface->format_attr_sdp_generate(&format->fattr, payload, str); - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); + ao2_cleanup(interfaces); + interfaces = NULL; } -/*! \internal - * \brief set format attributes using an interface - */ -static int format_set_helper(struct ast_format *format, va_list ap) +int ast_format_init(void) { - struct interface_ao2_wrapper *wrapper; - - if (!(wrapper = find_interface(format))) { - ast_log(LOG_WARNING, "Could not find format interface to set.\n"); - return -1; - } - - ao2_rdlock(wrapper); - if (!wrapper->interface || !wrapper->interface->format_attr_set) { - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); + interfaces = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, FORMAT_INTERFACE_BUCKETS, format_interface_hash, + format_interface_cmp); + if (!interfaces) { return -1; } - wrapper->interface->format_attr_set(&format->fattr, ap); - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); + ast_register_atexit(format_shutdown); 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, ... ) +int __ast_format_interface_register(const char *codec, const struct ast_format_interface *interface, struct ast_module *mod) { - /* initialize the structure before setting it. */ - ast_format_clear(format); - - format->id = id; + SCOPED_AO2WRLOCK(lock, interfaces); + struct format_interface *format_interface; - if (set_attributes) { - va_list ap; - va_start(ap, set_attributes); - format_set_helper(format, ap); - va_end(ap); + if (!interface->format_clone || !interface->format_destroy) { + ast_log(LOG_ERROR, "Format interface for codec '%s' does not implement required callbacks\n", codec); + return -1; } - 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(const 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))) { + format_interface = ao2_find(interfaces, codec, OBJ_SEARCH_KEY | OBJ_NOLOCK); + if (format_interface) { + ast_log(LOG_ERROR, "A format interface is already present for codec '%s'\n", codec); + ao2_ref(format_interface, -1); return -1; } - ao2_rdlock(wrapper); - if (!wrapper->interface || - !wrapper->interface->format_attr_set || - !wrapper->interface->format_attr_cmp) { - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); + format_interface = ao2_alloc_options(sizeof(*format_interface) + strlen(codec) + 1, + NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!format_interface) { return -1; } + format_interface->interface = interface; + strcpy(format_interface->codec, codec); /* Safe */ - /* if isset is present, use that function, else just build a new - * format and use the cmp function */ - if (wrapper->interface->format_attr_isset) { - res = wrapper->interface->format_attr_isset(&format->fattr, ap); - } else { - 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); - res = (res == AST_FORMAT_CMP_NOT_EQUAL) ? -1 : 0; - } + ao2_link_flags(interfaces, format_interface, OBJ_NOLOCK); + ao2_ref(format_interface, -1); - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); + ast_verb(2, "Registered format interface for codec '%s'\n", codec); - return res; + return 0; } -int ast_format_isset(const struct ast_format *format, ... ) +void *ast_format_get_attribute_data(const struct ast_format *format) { - va_list ap; - int res; - - va_start(ap, format); - res = format_isset_helper(format, ap); - va_end(ap); - return res; + return format->attribute_data; } -int ast_format_get_value(const struct ast_format *format, int key, void *value) +void ast_format_set_attribute_data(struct ast_format *format, void *attribute_data) { - int res = 0; - struct interface_ao2_wrapper *wrapper; - - if (!(wrapper = find_interface(format))) { - return -1; - } - ao2_rdlock(wrapper); - if (!wrapper->interface || - !wrapper->interface->format_attr_get_val) { - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); - return -1; - } - - res = wrapper->interface->format_attr_get_val(&format->fattr, key, value); - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); - - return res; + format->attribute_data = attribute_data; } -/*! \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) +/*! \brief Destructor for media formats */ +static void format_destroy(void *obj) { - enum ast_format_cmp_res res = AST_FORMAT_CMP_EQUAL; - struct interface_ao2_wrapper *wrapper; + struct ast_format *format = obj; - if (!(wrapper = find_interface(format1))) { - return res; + if (format->interface) { + format->interface->format_destroy(format); } - ao2_rdlock(wrapper); - if (!wrapper->interface || !wrapper->interface->format_attr_cmp) { - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); - return res; - } - - res = wrapper->interface->format_attr_cmp(&format1->fattr, &format2->fattr); - - ao2_unlock(wrapper); - ao2_ref(wrapper, -1); - - return res; + ao2_cleanup(format->codec); } -enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2) +struct ast_format *ast_format_create_named(const char *format_name, struct ast_codec *codec) { - if (format1->id != format2->id) { - return AST_FORMAT_CMP_NOT_EQUAL; - } - - return format_cmp_helper(format1, format2); -} + struct ast_format *format; + struct format_interface *format_interface; -/*! \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; + format = ao2_t_alloc_options(sizeof(*format), format_destroy, + AO2_ALLOC_OPT_LOCK_NOLOCK, S_OR(codec->description, "")); + if (!format) { + return NULL; } + format->name = format_name; + format->codec = ao2_bump(codec); - ao2_rdlock(wrapper); - if (wrapper->interface && wrapper->interface->format_attr_get_joint) { - res = wrapper->interface->format_attr_get_joint(&format1->fattr, &format2->fattr, &result->fattr); + format_interface = ao2_find(interfaces, codec->name, OBJ_SEARCH_KEY); + if (format_interface) { + format->interface = format_interface->interface; + ao2_ref(format_interface, -1); } - ao2_unlock(wrapper); - - ao2_ref(wrapper, -1); - return res; + return format; } -int ast_format_joint(const struct ast_format *format1, const struct ast_format *format2, struct ast_format *result) +struct ast_format *ast_format_clone(const struct ast_format *format) { - if (format1->id != format2->id) { - return -1; - } - result->id = format1->id; - return format_joint_helper(format1, format2, result); -} + struct ast_format *cloned = ast_format_create_named(format->name, format->codec); - -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); - /*! Opus audio (8kHz, 16kHz, 24kHz, 48Khz) */ - case AST_FORMAT_OPUS: - return (1ULL << 34); - /*! 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); - /*! VP8 Video */ - case AST_FORMAT_VP8: - return (1ULL << 23); - - /*! 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); - default: - return 0; /* not supported by old bitfield. */ + if (!cloned) { + return NULL; } - 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); - /*! Opus audio (8kHz, 16kHz, 24kHz, 48Khz) */ - case (1ULL << 34): - return ast_format_set(dst, AST_FORMAT_OPUS, 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); - /*! VP8 Video */ - case (1ULL << 23): - return ast_format_set(dst, AST_FORMAT_VP8, 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); + if (cloned->interface && cloned->interface->format_clone(format, cloned)) { + ao2_ref(cloned, -1); + return NULL; } - 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; + return cloned; } -int ast_format_is_slinear(const struct ast_format *format) +struct ast_format *ast_format_create(struct ast_codec *codec) { - if (format->id == AST_FORMAT_SLINEAR || - format->id == AST_FORMAT_SLINEAR12 || - format->id == AST_FORMAT_SLINEAR16 || - format->id == AST_FORMAT_SLINEAR24 || - format->id == AST_FORMAT_SLINEAR32 || - format->id == AST_FORMAT_SLINEAR44 || - format->id == AST_FORMAT_SLINEAR48 || - format->id == AST_FORMAT_SLINEAR96 || - format->id == AST_FORMAT_SLINEAR192) { - return 1; - } - return 0; + return ast_format_create_named(codec->name, codec); } -enum ast_format_id ast_format_slin_by_rate(unsigned int rate) +enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2) { - if (rate >= 192000) { - return AST_FORMAT_SLINEAR192; - } else if (rate >= 96000) { - return AST_FORMAT_SLINEAR96; - } else if (rate >= 48000) { - return AST_FORMAT_SLINEAR48; - } else if (rate >= 44100) { - return AST_FORMAT_SLINEAR44; - } else if (rate >= 32000) { - return AST_FORMAT_SLINEAR32; - } else if (rate >= 24000) { - return AST_FORMAT_SLINEAR24; - } else if (rate >= 16000) { - return AST_FORMAT_SLINEAR16; - } else if (rate >= 12000) { - return AST_FORMAT_SLINEAR12; - } - return AST_FORMAT_SLINEAR; -} + const struct ast_format_interface *interface; -const char* ast_getformatname(const struct ast_format *format) -{ - int x; - const char *ret = "unknown"; - size_t f_len; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); - for (x = 0; x < f_len; x++) { - if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { - ret = f_list[x].name; - break; - } + if (format1 == NULL || format2 == NULL) { + return AST_FORMAT_CMP_NOT_EQUAL; } - f_list = ast_format_list_destroy(f_list); - return ret; -} - -char *ast_getformatname_multiple_byid(char *buf, size_t size, enum ast_format_id id) -{ - int x; - unsigned len; - char *start, *end = buf; - size_t f_len; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); - - if (!size) { - f_list = ast_format_list_destroy(f_list); - return buf; - } - snprintf(end, size, "("); - len = strlen(end); - end += len; - size -= len; - start = end; - for (x = 0; x < f_len; x++) { - if (f_list[x].format.id == id) { - snprintf(end, size, "%s|", f_list[x].name); - len = strlen(end); - end += len; - size -= len; - } - } - if (start == end) { - ast_copy_string(start, "nothing)", size); - } else if (size > 1) { - *(end - 1) = ')'; + if (format1 == format2) { + return AST_FORMAT_CMP_EQUAL; } - f_list = ast_format_list_destroy(f_list); - return buf; -} - -static struct ast_codec_alias_table { - const char *alias; - const char *realname; -} ast_codec_alias_table[] = { - { "slinear", "slin"}, - { "slinear16", "slin16"}, - { "g723.1", "g723"}, - { "g722.1", "siren7"}, - { "g722.1c", "siren14"}, -}; -static const char *ast_expand_codec_alias(const char *in) -{ - int x; - - for (x = 0; x < ARRAY_LEN(ast_codec_alias_table); x++) { - if (!strcmp(in,ast_codec_alias_table[x].alias)) - return ast_codec_alias_table[x].realname; + if (format1->codec != format2->codec) { + return AST_FORMAT_CMP_NOT_EQUAL; } - return in; -} - -struct ast_format *ast_getformatbyname(const char *name, struct ast_format *result) -{ - int x; - size_t f_len; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); - for (x = 0; x < f_len; x++) { - if (!strcasecmp(f_list[x].name, name) || - !strcasecmp(f_list[x].name, ast_expand_codec_alias(name))) { + interface = format1->interface ? format1->interface : format2->interface; - ast_format_copy(result, &f_list[x].format); - f_list = ast_format_list_destroy(f_list); - return result; - } + if (interface) { + return interface->format_cmp(format1, format2); } - f_list = ast_format_list_destroy(f_list); - - return NULL; -} -const char *ast_codec2str(struct ast_format *format) -{ - int x; - const char *ret = "unknown"; - size_t f_len; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); - - for (x = 0; x < f_len; x++) { - if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { - ret = f_list[x].desc; - break; - } - } - f_list = ast_format_list_destroy(f_list); - return ret; + return AST_FORMAT_CMP_EQUAL; } -int ast_format_rate(const struct ast_format *format) +struct ast_format *ast_format_joint(const struct ast_format *format1, const struct ast_format *format2) { - switch (format->id) { - case AST_FORMAT_SLINEAR12: - return 12000; - case AST_FORMAT_SLINEAR24: - return 24000; - case AST_FORMAT_SLINEAR32: - return 32000; - case AST_FORMAT_SLINEAR44: - return 44100; - case AST_FORMAT_SLINEAR48: - return 48000; - case AST_FORMAT_SLINEAR96: - return 96000; - case AST_FORMAT_SLINEAR192: - return 192000; - case AST_FORMAT_G722: - case AST_FORMAT_SLINEAR16: - case AST_FORMAT_SIREN7: - case AST_FORMAT_SPEEX16: - return 16000; - case AST_FORMAT_SIREN14: - case AST_FORMAT_SPEEX32: - return 32000; - case AST_FORMAT_G719: - return 48000; - case AST_FORMAT_SILK: - if (!(ast_format_isset(format, - SILK_ATTR_KEY_SAMP_RATE, - SILK_ATTR_VAL_SAMP_24KHZ, - AST_FORMAT_ATTR_END))) { - return 24000; - } else if (!(ast_format_isset(format, - SILK_ATTR_KEY_SAMP_RATE, - SILK_ATTR_VAL_SAMP_16KHZ, - AST_FORMAT_ATTR_END))) { - return 16000; - } else if (!(ast_format_isset(format, - SILK_ATTR_KEY_SAMP_RATE, - SILK_ATTR_VAL_SAMP_12KHZ, - AST_FORMAT_ATTR_END))) { - return 12000; - } else { - return 8000; - } - case AST_FORMAT_CELT: - { - int samplerate; - if (!(ast_format_get_value(format, - CELT_ATTR_KEY_SAMP_RATE, - &samplerate))) { - return samplerate; - } - } - /* Opus */ - case AST_FORMAT_OPUS: - return 48000; - default: - return 8000; - } -} + const struct ast_format_interface *interface; -static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - int x, found=0; - size_t f_len; - const struct ast_format_list *f_list; - - switch (cmd) { - case CLI_INIT: - e->command = "core show codecs [audio|video|image|text]"; - e->usage = - "Usage: core show codecs [audio|video|image|text]\n" - " Displays codec mapping\n"; - return NULL; - case CLI_GENERATE: + if (format1->codec != format2->codec) { return NULL; } - if ((a->argc < 3) || (a->argc > 4)) { - return CLI_SHOWUSAGE; + /* If the two formats are the same structure OR if the codec is the same and no attributes + * exist we can immediately return a format with reference count bumped up, since they are + * the same. + */ + if ((ast_format_cmp(format1, format2) == AST_FORMAT_CMP_EQUAL && !format1->attribute_data && !format2->attribute_data)) { + return ao2_bump((struct ast_format*)format1); } - f_list = ast_format_list_get(&f_len); - if (!ast_opt_dont_warn) { - ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n" - "\tIt does not indicate anything about your configuration.\n"); - } + interface = format1->interface ? format1->interface : format2->interface; - ast_cli(a->fd, "%8s %5s %8s %s\n","ID","TYPE","NAME","DESCRIPTION"); - ast_cli(a->fd, "-----------------------------------------------------------------------------------\n"); - - for (x = 0; x < f_len; x++) { - if (a->argc == 4) { - if (!strcasecmp(a->argv[3], "audio")) { - if (AST_FORMAT_GET_TYPE(f_list[x].format.id) != AST_FORMAT_TYPE_AUDIO) { - continue; - } - } else if (!strcasecmp(a->argv[3], "video")) { - if (AST_FORMAT_GET_TYPE(f_list[x].format.id) != AST_FORMAT_TYPE_VIDEO) { - continue; - } - } else if (!strcasecmp(a->argv[3], "image")) { - if (AST_FORMAT_GET_TYPE(f_list[x].format.id) != AST_FORMAT_TYPE_IMAGE) { - continue; - } - } else if (!strcasecmp(a->argv[3], "text")) { - if (AST_FORMAT_GET_TYPE(f_list[x].format.id) != AST_FORMAT_TYPE_TEXT) { - continue; - } - } else { - continue; - } - } - - ast_cli(a->fd, "%8u %5s %8s (%s)\n", - f_list[x].format.id, - (AST_FORMAT_GET_TYPE(f_list[x].format.id) == AST_FORMAT_TYPE_AUDIO) ? "audio" : - (AST_FORMAT_GET_TYPE(f_list[x].format.id) == AST_FORMAT_TYPE_IMAGE) ? "image" : - (AST_FORMAT_GET_TYPE(f_list[x].format.id) == AST_FORMAT_TYPE_VIDEO) ? "video" : - (AST_FORMAT_GET_TYPE(f_list[x].format.id) == AST_FORMAT_TYPE_TEXT) ? "text" : - "(unk)", - f_list[x].name, - f_list[x].desc); - found = 1; - } - - f_list = ast_format_list_destroy(f_list); - if (!found) { - return CLI_SHOWUSAGE; - } else { - return CLI_SUCCESS; - } + /* If there is attribute data on either there has to be an interface */ + return interface->format_get_joint(format1, format2); } -static char *show_codec_n(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +struct ast_format *ast_format_attribute_set(const struct ast_format *format, const char *name, const char *value) { - enum ast_format_id format_id; - int x, found = 0; - int type_punned_codec; - size_t f_len; - const struct ast_format_list *f_list; - - switch (cmd) { - case CLI_INIT: - e->command = "core show codec"; - e->usage = - "Usage: core show codec <number>\n" - " Displays codec mapping\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc != 4) { - return CLI_SHOWUSAGE; - } + const struct ast_format_interface *interface = format->interface; - if (sscanf(a->argv[3], "%30d", &type_punned_codec) != 1) { - return CLI_SHOWUSAGE; - } - format_id = type_punned_codec; - - f_list = ast_format_list_get(&f_len); - for (x = 0; x < f_len; x++) { - if (f_list[x].format.id == format_id) { - found = 1; - ast_cli(a->fd, "%11u %s\n", (unsigned int) format_id, f_list[x].desc); + if (!interface) { + struct format_interface *format_interface = ao2_find(interfaces, format->codec->name, OBJ_SEARCH_KEY); + if (format_interface) { + interface = format_interface->interface; + ao2_ref(format_interface, -1); } } - if (!found) { - ast_cli(a->fd, "Codec %u not found\n", format_id); + if (!interface || !interface->format_attribute_set) { + return ao2_bump((struct ast_format*)format); } - f_list = ast_format_list_destroy(f_list); - return CLI_SUCCESS; + return interface->format_attribute_set(format, name, value); } -/* Builtin Asterisk CLI-commands for debugging */ -static struct ast_cli_entry my_clis[] = { - AST_CLI_DEFINE(show_codecs, "Displays a list of codecs"), - AST_CLI_DEFINE(show_codec_n, "Shows a specific codec"), -}; - -static int format_list_add_custom(struct ast_format_list *new) +struct ast_format *ast_format_parse_sdp_fmtp(const struct ast_format *format, const char *attributes) { - RAII_VAR(struct ast_format_list *, entry, NULL, ao2_cleanup); - if (!(entry = ao2_alloc(sizeof(*entry), NULL))) { - return -1; - } - memcpy(entry, new, sizeof(struct ast_format_list)); - entry->custom_entry = 1; - ao2_link(format_list, entry); - return 0; -} -static int format_list_add_static( - const struct ast_format *format, - const char *name, - int samplespersecond, - const char *description, - int fr_len, - int min_ms, - int max_ms, - int inc_ms, - int def_ms, - unsigned int flags, - int cur_ms) -{ - struct ast_format_list *entry; - if (!(entry = ao2_alloc(sizeof(*entry), NULL))) { - return -1; - } - ast_format_copy(&entry->format, format); - ast_copy_string(entry->name, name, sizeof(entry->name)); - ast_copy_string(entry->desc, description, sizeof(entry->desc)); - entry->samplespersecond = samplespersecond; - entry->fr_len = fr_len; - entry->min_ms = min_ms; - entry->max_ms = max_ms; - entry->inc_ms = inc_ms; - entry->def_ms = def_ms; - entry->flags = flags; - entry->cur_ms = cur_ms; - entry->custom_entry = 0; - - ao2_link(format_list, entry); - ao2_ref(entry, -1); - return 0; -} + const struct ast_format_interface *interface = format->interface; -static int list_all_custom(void *obj, void *arg, int flag) -{ - struct ast_format_list *entry = obj; - return entry->custom_entry ? CMP_MATCH : 0; -} - -static int list_cmp_cb(void *obj, void *arg, int flags) -{ - struct ast_format_list *entry1 = obj; - struct ast_format_list *entry2 = arg; - - return (ast_format_cmp(&entry1->format, &entry2->format) == AST_FORMAT_CMP_EQUAL) ? CMP_MATCH | CMP_STOP : 0; -} - -const struct ast_format_list *ast_format_list_get(size_t *size) -{ - struct ast_format_list *list; - ast_rwlock_rdlock(&format_list_array_lock); - ao2_ref(format_list_array, 1); - list = format_list_array; - *size = format_list_array_len; - ast_rwlock_unlock(&format_list_array_lock); - return list; -} -const struct ast_format_list *ast_format_list_destroy(const struct ast_format_list *list) -{ - ao2_ref((void *) list, -1); - return NULL; -} - -static int build_format_list_array(void) -{ - struct ast_format_list *tmp; - size_t arraysize = sizeof(struct ast_format_list) * ao2_container_count(format_list); - int i = 0; - struct ao2_iterator it; - - ast_rwlock_wrlock(&format_list_array_lock); - tmp = format_list_array; - if (!(format_list_array = ao2_alloc(arraysize, NULL))) { - format_list_array = tmp; - ast_rwlock_unlock(&format_list_array_lock); - return -1; - } - format_list_array_len = ao2_container_count(format_list); - if (tmp) { - ao2_ref(tmp, -1); + if (!interface) { + struct format_interface *format_interface = ao2_find(interfaces, format->codec->name, OBJ_SEARCH_KEY); + if (format_interface) { + interface = format_interface->interface; + ao2_ref(format_interface, -1); + } } - /* walk through the container adding elements to the static array */ - it = ao2_iterator_init(format_list, 0); - while ((tmp = ao2_iterator_next(&it)) && (i < format_list_array_len)) { - memcpy(&format_list_array[i], tmp, sizeof(struct ast_format_list)); - ao2_ref(tmp, -1); - i++; + if (!interface || !interface->format_parse_sdp_fmtp) { + return ao2_bump((struct ast_format*)format); } - ao2_iterator_destroy(&it); - ast_rwlock_unlock(&format_list_array_lock); - return 0; + return interface->format_parse_sdp_fmtp(format, attributes); } -static int format_list_init(void) +void ast_format_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str) { - struct ast_format tmpfmt; - if (!(format_list = ao2_container_alloc(1, NULL, list_cmp_cb))) { - return -1; + if (!format->interface || !format->interface->format_generate_sdp_fmtp) { + return; } - /* initiate static entries XXX DO NOT CHANGE THIS ORDER! */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G723_1, 0), "g723", 8000, "G.723.1", 20, 30, 300, 30, 30, 0, 0); /*!< G723.1 */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_GSM, 0), "gsm", 8000, "GSM", 33, 20, 300, 20, 20, 0, 0); /*!< codec_gsm.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0), "ulaw", 8000, "G.711 u-law", 80, 10, 150, 10, 20, 0, 0); /*!< codec_ulaw.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0), "alaw", 8000, "G.711 A-law", 80, 10, 150, 10, 20, 0, 0); /*!< codec_alaw.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G726, 0), "g726", 8000, "G.726 RFC3551", 40, 10, 300, 10, 20, 0, 0); /*!< codec_g726.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), "adpcm" , 8000, "Dialogic ADPCM", 40, 10, 300, 10, 20, 0, 0); /*!< codec_adpcm.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0), "slin", 8000, "16 bit Signed Linear PCM", 160, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0); /*!< Signed linear */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_LPC10, 0), "lpc10", 8000, "LPC10", 7, 20, 20, 20, 20, 0, 0); /*!< codec_lpc10.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G729A, 0), "g729", 8000, "G.729A", 10, 10, 230, 10, 20, AST_SMOOTHER_FLAG_G729, 0); /*!< Binary commercial distribution */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX, 0), "speex", 8000, "SpeeX", 10, 10, 60, 10, 20, 0, 0); /*!< codec_speex.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX16, 0), "speex16", 16000, "SpeeX 16khz", 10, 10, 60, 10, 20, 0, 0); /*!< codec_speex.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ILBC, 0), "ilbc", 8000, "iLBC", 50, 30, 30, 30, 30, 0, 0); /*!< codec_ilbc.c */ /* inc=30ms - workaround */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G726_AAL2, 0), "g726aal2", 8000, "G.726 AAL2", 40, 10, 300, 10, 20, 0, 0); /*!< codec_g726.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G722, 0), "g722", 16000, "G722", 80, 10, 150, 10, 20, 0, 0); /*!< codec_g722.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR16, 0), "slin16", 16000, "16 bit Signed Linear PCM (16kHz)", 320, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (16kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_JPEG, 0), "jpeg", 0, "JPEG image", 0, 0, 0, 0 ,0 ,0 ,0); /*!< See format_jpeg.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_PNG, 0), "png", 0, "PNG image", 0, 0, 0, 0 ,0 ,0 ,0); /*!< PNG Image format */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H261, 0), "h261", 0, "H.261 Video", 0, 0, 0, 0 ,0 ,0 ,0); /*!< H.261 Video Passthrough */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H263, 0), "h263", 0, "H.263 Video", 0, 0, 0, 0 ,0 ,0 ,0); /*!< H.263 Passthrough support, see format_h263.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), "h263p", 0, "H.263+ Video", 0, 0, 0,0 ,0 ,0, 0); /*!< H.263plus passthrough support See format_h263.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H264, 0), "h264", 0, "H.264 Video", 0, 0, 0, 0 ,0 ,0, 0); /*!< Passthrough support, see format_h263.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_MP4_VIDEO, 0), "mpeg4", 0, "MPEG4 Video", 0, 0, 0, 0, 0 ,0, 0); /*!< Passthrough support for MPEG4 */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_T140RED, 0), "red", 1, "T.140 Realtime Text with redundancy", 0, 0, 0,0 ,0 ,0, 0); /*!< Redundant T.140 Realtime Text */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_T140, 0), "t140", 0, "Passthrough T.140 Realtime Text", 0, 0, 0, 0 ,0 ,0, 0); /*!< Passthrough support for T.140 Realtime Text */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SIREN7, 0), "siren7", 16000, "ITU G.722.1 (Siren7, licensed from Polycom)", 80, 20, 80, 20, 20, 0, 0); /*!< Binary commercial distribution */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SIREN14, 0), "siren14", 32000, "ITU G.722.1 Annex C, (Siren14, licensed from Polycom)", 120, 20, 80, 20, 20, 0, 0); /*!< Binary commercial distribution */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_TESTLAW, 0), "testlaw", 8000, "G.711 test-law", 80, 10, 150, 10, 20, 0, 0); /*!< codec_ulaw.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G719, 0), "g719", 48000, "ITU G.719", 160, 20, 80, 20, 20, 0, 0); - - /* ORDER MAY CHANGE AFTER THIS POINT IN THE LIST */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX32, 0), "speex32", 32000, "SpeeX 32khz", 10, 10, 60, 10, 20, 0, 0); /*!< codec_speex.c */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR12, 0), "slin12", 12000, "16 bit Signed Linear PCM (12kHz)", 240, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (12kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR24, 0), "slin24", 24000, "16 bit Signed Linear PCM (24kHz)", 480, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (24kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR32, 0), "slin32", 32000, "16 bit Signed Linear PCM (32kHz)", 640, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (32kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR44, 0), "slin44", 44100, "16 bit Signed Linear PCM (44kHz)", 882, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (44.1kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR48, 0), "slin48", 48000, "16 bit Signed Linear PCM (48kHz)", 960, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (48kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR96, 0), "slin96", 96000, "16 bit Signed Linear PCM (96kHz)", 1920, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (96kHz) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR192, 0), "slin192", 192000, "16 bit Signed Linear PCM (192kHz)", 3840, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (192kHz) */ - /* Opus (FIXME: real min is 3/5/10, real max is 120...) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_OPUS, 0), "opus", 48000, "Opus Codec", 10, 20, 60, 20, 20, 0, 0); /*!< codec_opus.c */ - /* VP8 (passthrough) */ - format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_VP8, 0), "vp8", 0, "VP8 Video", 0, 0, 0, 0 ,0 ,0, 0); /*!< Passthrough support, see format_h263.c */ - return 0; + format->interface->format_generate_sdp_fmtp(format, payload, str); } -/*! - * \internal - * \brief Clean up resources on Asterisk shutdown - */ -static void format_list_shutdown(void) +unsigned int ast_format_get_codec_id(const struct ast_format *format) { - ast_rwlock_destroy(&format_list_array_lock); - if (format_list) { - ao2_t_ref(format_list, -1, "Unref format_list container in shutdown"); - format_list = NULL; - } - if (format_list_array) { - ao2_t_ref(format_list_array, -1, "Unref format_list_array in shutdown"); - format_list_array = NULL; - } + return format->codec->id; } -int ast_format_list_init(void) +const char *ast_format_get_name(const struct ast_format *format) { - if (ast_rwlock_init(&format_list_array_lock)) { - return -1; - } - if (format_list_init()) { - goto init_list_cleanup; - } - if (build_format_list_array()) { - goto init_list_cleanup; - } - - ast_register_atexit(format_list_shutdown); - return 0; -init_list_cleanup: - - format_list_shutdown(); - return -1; + return format->name; } -/*! - * \internal - * \brief Clean up resources on Asterisk shutdown - */ -static void format_attr_shutdown(void) +const char *ast_format_get_codec_name(const struct ast_format *format) { - ast_cli_unregister_multiple(my_clis, ARRAY_LEN(my_clis)); - if (interfaces) { - ao2_ref(interfaces, -1); - interfaces = NULL; - } + return format->codec->name; } -int ast_format_attr_init(void) +int ast_format_can_be_smoothed(const struct ast_format *format) { - interfaces = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, - 283, interface_hash_cb, interface_cmp_cb); - if (!interfaces) { - return -1; - } - - ast_cli_register_multiple(my_clis, ARRAY_LEN(my_clis)); - ast_register_cleanup(format_attr_shutdown); - return 0; + return format->codec->smooth; } -static int custom_celt_format(struct ast_format_list *entry, unsigned int maxbitrate, unsigned int framesize) +enum ast_media_type ast_format_get_type(const struct ast_format *format) { - if (!entry->samplespersecond) { - ast_log(LOG_WARNING, "Custom CELT format definition '%s' requires sample rate to be defined.\n", entry->name); - } - ast_format_set(&entry->format, AST_FORMAT_CELT, 0); - if (!has_interface(&entry->format)) { - return -1; - } - - snprintf(entry->desc, sizeof(entry->desc), "CELT Custom Format %ukhz", entry->samplespersecond/1000); - - ast_format_append(&entry->format, - CELT_ATTR_KEY_SAMP_RATE, entry->samplespersecond, - CELT_ATTR_KEY_MAX_BITRATE, maxbitrate, - CELT_ATTR_KEY_FRAME_SIZE, framesize, - AST_FORMAT_ATTR_END); - - entry->fr_len = 80; - entry->min_ms = 20; - entry->max_ms = 20; - entry->inc_ms = 20; - entry->def_ms = 20; - return 0; + return format->codec->type; } -static int custom_silk_format(struct ast_format_list *entry, unsigned int maxbitrate, int usedtx, int usefec, int packetloss_percentage) +unsigned int ast_format_get_default_ms(const struct ast_format *format) { - if (!entry->samplespersecond) { - ast_log(LOG_WARNING, "Custom SILK format definition '%s' requires sample rate to be defined.\n", entry->name); - } - ast_format_set(&entry->format, AST_FORMAT_SILK, 0); - - if (!has_interface(&entry->format)) { - return -1; - } - - switch (entry->samplespersecond) { - case 8000: - ast_copy_string(entry->desc, "SILK Custom Format 8khz", sizeof(entry->desc)); - ast_format_append(&entry->format, - SILK_ATTR_KEY_SAMP_RATE, SILK_ATTR_VAL_SAMP_8KHZ, - AST_FORMAT_ATTR_END); - break; - case 12000: - ast_copy_string(entry->desc, "SILK Custom Format 12khz", sizeof(entry->desc)); - ast_format_append(&entry->format, - SILK_ATTR_KEY_SAMP_RATE, SILK_ATTR_VAL_SAMP_12KHZ, - AST_FORMAT_ATTR_END); - break; - case 16000: - ast_copy_string(entry->desc, "SILK Custom Format 16khz", sizeof(entry->desc)); - ast_format_append(&entry->format, - SILK_ATTR_KEY_SAMP_RATE, SILK_ATTR_VAL_SAMP_16KHZ, - AST_FORMAT_ATTR_END); - break; - case 24000: - ast_copy_string(entry->desc, "SILK Custom Format 24khz", sizeof(entry->desc)); - ast_format_append(&entry->format, - SILK_ATTR_KEY_SAMP_RATE, SILK_ATTR_VAL_SAMP_24KHZ, - AST_FORMAT_ATTR_END); - break; - default: - ast_log(LOG_WARNING, "Custom SILK format definition '%s' can not support sample rate %u\n", entry->name, entry->samplespersecond); - return -1; - } - ast_format_append(&entry->format, - SILK_ATTR_KEY_MAX_BITRATE, maxbitrate, - SILK_ATTR_KEY_DTX, usedtx ? 1 : 0, - SILK_ATTR_KEY_FEC, usefec ? 1 : 0, - SILK_ATTR_KEY_PACKETLOSS_PERCENTAGE, packetloss_percentage, - AST_FORMAT_ATTR_END); - - entry->fr_len = 80; - entry->min_ms = 20; - entry->max_ms = 20; - entry->inc_ms = 20; - entry->def_ms = 20; - return 0; + return format->codec->default_ms; } -static int conf_process_format_name(const char *name, enum ast_format_id *id) +unsigned int ast_format_get_minimum_ms(const struct ast_format *format) { - if (!strcasecmp(name, "silk")) { - *id = AST_FORMAT_SILK; - } else if (!strcasecmp(name, "celt")) { - *id = AST_FORMAT_CELT; - } else { - *id = 0; - return -1; - } - return 0; + return format->codec->minimum_ms; } -static int conf_process_sample_rate(const char *rate, unsigned int *result) +unsigned int ast_format_get_maximum_ms(const struct ast_format *format) { - if (!strcasecmp(rate, "8000")) { - *result = 8000; - } else if (!strcasecmp(rate, "12000")) { - *result = 12000; - } else if (!strcasecmp(rate, "16000")) { - *result = 16000; - } else if (!strcasecmp(rate, "24000")) { - *result = 24000; - } else if (!strcasecmp(rate, "32000")) { - *result = 32000; - } else if (!strcasecmp(rate, "44100")) { - *result = 44100; - } else if (!strcasecmp(rate, "48000")) { - *result = 48000; - } else if (!strcasecmp(rate, "96000")) { - *result = 96000; - } else if (!strcasecmp(rate, "192000")) { - *result = 192000; - } else { - *result = 0; - return -1; - } - - return 0; + return format->codec->maximum_ms; } -static int load_format_config(void) -{ - struct ast_flags config_flags = { 0, }; - struct ast_config *cfg = ast_config_load(FORMAT_CONFIG, config_flags); - struct ast_format_list entry; - struct ast_variable *var; - char *cat = NULL; - int add_it = 0; - - struct { - enum ast_format_id id; - unsigned int maxbitrate; - unsigned int framesize; - unsigned int packetloss_percentage; - int usefec; - int usedtx; - } settings; - - if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) { - return 0; - } - - /* remove all custom formats from the AO2 Container. Note, this has no affect on the - * global format list until the list is rebuild. That is why this is okay to do while - * reloading the config. */ - ao2_callback(format_list, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, list_all_custom, NULL); - while ((cat = ast_category_browse(cfg, cat))) { - memset(&entry, 0, sizeof(entry)); - memset(&settings, 0, sizeof(settings)); - add_it = 0; - - if (!(ast_variable_retrieve(cfg, cat, "type"))) { - continue; - } - ast_copy_string(entry.name, cat, sizeof(entry.name)); - var = ast_variable_browse(cfg, cat); - for (var = ast_variable_browse(cfg, cat); var; var = var->next) { - if (!strcasecmp(var->name, "type") && conf_process_format_name(var->value, &settings.id)) { - ast_log(LOG_WARNING, "Can not make custom format type for '%s' at line %d of %s\n", - var->value, var->lineno, FORMAT_CONFIG); - continue; - } else if (!strcasecmp(var->name, "samprate") && conf_process_sample_rate(var->value, &entry.samplespersecond)) { - ast_log(LOG_WARNING, "Sample rate '%s' at line %d of %s is not supported.\n", - var->value, var->lineno, FORMAT_CONFIG); - } else if (!strcasecmp(var->name, "maxbitrate")) { - if (sscanf(var->value, "%30u", &settings.maxbitrate) != 1) { - ast_log(LOG_WARNING, "maxbitrate '%s' at line %d of %s is not supported.\n", - var->value, var->lineno, FORMAT_CONFIG); - } - } else if (!strcasecmp(var->name, "framesize")) { - if (sscanf(var->value, "%30u", &settings.framesize) != 1) { - ast_log(LOG_WARNING, "framesize '%s' at line %d of %s is not supported.\n", - var->value, var->lineno, FORMAT_CONFIG); - } - } else if (!strcasecmp(var->name, "dtx")) { - settings.usedtx = ast_true(var->value) ? 1 : 0; - } else if (!strcasecmp(var->name, "fec")) { - settings.usefec = ast_true(var->value) ? 1 : 0; - } else if (!strcasecmp(var->name, "packetloss_percentage")) { - if ((sscanf(var->value, "%30u", &settings.packetloss_percentage) != 1) || (settings.packetloss_percentage > 100)) { - ast_log(LOG_WARNING, "packetloss_percentage '%s' at line %d of %s is not supported.\n", - var->value, var->lineno, FORMAT_CONFIG); - } - } - } - - switch (settings.id) { - case AST_FORMAT_SILK: - if (!(custom_silk_format(&entry, settings.maxbitrate, settings.usedtx, settings.usefec, settings.packetloss_percentage))) { - add_it = 1; - } - break; - case AST_FORMAT_CELT: - if (!(custom_celt_format(&entry, settings.maxbitrate, settings.framesize))) { - add_it = 1; - } - break; - default: - ast_log(LOG_WARNING, "Can not create custom format %s\n", entry.name); - } - - if (add_it) { - format_list_add_custom(&entry); - } - } - ast_config_destroy(cfg); - build_format_list_array(); - return 0; +unsigned int ast_format_get_minimum_bytes(const struct ast_format *format) +{ + return format->codec->minimum_bytes; } -int ast_format_attr_reg_interface(const struct ast_format_attr_interface *interface) +unsigned int ast_format_get_sample_rate(const struct ast_format *format) { - int x; - size_t f_len; - const struct ast_format_list *f_list; - struct interface_ao2_wrapper *wrapper; - struct interface_ao2_wrapper tmp_wrapper = { - .id = interface->id, - }; - - /* - * Grab the write lock before checking for duplicates in - * anticipation of adding a new interface and to prevent a - * duplicate from sneaking in between the check and add. - */ - ao2_wrlock(interfaces); - - /* check for duplicates first*/ - if ((wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_NOLOCK)))) { - ao2_unlock(interfaces); - ast_log(LOG_WARNING, "Can not register attribute interface for format id %u, interface already exists.\n", interface->id); - ao2_ref(wrapper, -1); - return -1; - } - - wrapper = ao2_alloc_options(sizeof(*wrapper), NULL, AO2_ALLOC_OPT_LOCK_RWLOCK); - if (!wrapper) { - ao2_unlock(interfaces); - return -1; - } - - wrapper->interface = interface; - wrapper->id = interface->id; - - /* The write lock is already held. */ - ao2_link_flags(interfaces, wrapper, OBJ_NOLOCK); - ao2_unlock(interfaces); - - ao2_ref(wrapper, -1); - - /* This will find all custom formats in codecs.conf for this new registered interface */ - load_format_config(); - - /* update the RTP engine to all custom formats created for this interface */ - f_list = ast_format_list_get(&f_len); - for (x = 0; x < f_len; x++) { - if (f_list[x].format.id == tmp_wrapper.id) { - ast_rtp_engine_load_format(&f_list[x].format); - } - } - f_list = ast_format_list_destroy(f_list); - return 0; + return format->codec->sample_rate; } -int ast_format_attr_unreg_interface(const struct ast_format_attr_interface *interface) +unsigned int ast_format_determine_length(const struct ast_format *format, unsigned int samples) { - int x; - size_t f_len; - const struct ast_format_list *f_list; - struct interface_ao2_wrapper *wrapper; - struct interface_ao2_wrapper tmp_wrapper = { - .id = interface->id, - }; - - if (!(wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_UNLINK)))) { - return -1; - } - - ao2_wrlock(wrapper); - wrapper->interface = NULL; - ao2_unlock(wrapper); - - ao2_ref(wrapper, -1); - - /* update the RTP engine to remove all custom formats created for this interface */ - f_list = ast_format_list_get(&f_len); - for (x = 0; x < f_len; x++) { - if (f_list[x].format.id == tmp_wrapper.id) { - ast_rtp_engine_unload_format(&f_list[x].format); - } - } - - /* This will remove all custom formats previously created for this interface */ - load_format_config(); - f_list = ast_format_list_destroy(f_list); - return 0; + return ast_codec_determine_length(format->codec, samples); } diff --git a/main/format_cache.c b/main/format_cache.c new file mode 100644 index 000000000..c170a76f9 --- /dev/null +++ b/main/format_cache.c @@ -0,0 +1,519 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2014, Digium, Inc. + * + * Joshua Colp <jcolp@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 Media Format Cache API + * + * \author Joshua Colp <jcolp@digium.com> + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/logger.h" +#include "asterisk/format.h" +#include "asterisk/format_cache.h" +#include "asterisk/astobj2.h" +#include "asterisk/strings.h" + +/*! + * \brief Built-in cached signed linear 8kHz format. + */ +struct ast_format *ast_format_slin; + +/*! + * \brief Built-in cached signed linear 12kHz format. + */ +struct ast_format *ast_format_slin12; + +/*! + * \brief Built-in cached signed linear 16kHz format. + */ +struct ast_format *ast_format_slin16; + +/*! + * \brief Built-in cached signed linear 24kHz format. + */ +struct ast_format *ast_format_slin24; + +/*! + * \brief Built-in cached signed linear 32kHz format. + */ +struct ast_format *ast_format_slin32; + +/*! + * \brief Built-in cached signed linear 44kHz format. + */ +struct ast_format *ast_format_slin44; + +/*! + * \brief Built-in cached signed linear 48kHz format. + */ +struct ast_format *ast_format_slin48; + +/*! + * \brief Built-in cached signed linear 96kHz format. + */ +struct ast_format *ast_format_slin96; + +/*! + * \brief Built-in cached signed linear 192kHz format. + */ +struct ast_format *ast_format_slin192; + +/*! + * \brief Built-in cached ulaw format. + */ +struct ast_format *ast_format_ulaw; + +/*! + * \brief Built-in cached alaw format. + */ +struct ast_format *ast_format_alaw; + +/*! + * \brief Built-in cached testlaw format. + */ +struct ast_format *ast_format_testlaw; + +/*! + * \brief Built-in cached gsm format. + */ +struct ast_format *ast_format_gsm; + +/*! + * \brief Built-in cached adpcm format. + */ +struct ast_format *ast_format_adpcm; + +/*! + * \brief Built-in cached g722 format. + */ +struct ast_format *ast_format_g722; + +/*! + * \brief Built-in cached g726 format. + */ +struct ast_format *ast_format_g726; + +/*! + * \brief Built-in cached g726-aal2 format. + */ +struct ast_format *ast_format_g726_aal2; + +/*! + * \brief Built-in cached ilbc format. + */ +struct ast_format *ast_format_ilbc; + +/*! + * \brief Built-in cached ilbc format. + */ +struct ast_format *ast_format_lpc10; + +/*! + * \brief Built-in cached speex format. + */ +struct ast_format *ast_format_speex; + +/*! + * \brief Built-in cached speex at 16kHz format. + */ +struct ast_format *ast_format_speex16; + +/*! + * \brief Built-in cached speex at 32kHz format. + */ +struct ast_format *ast_format_speex32; + +/*! + * \brief Built-in cached g723.1 format. + */ +struct ast_format *ast_format_g723; + +/*! + * \brief Built-in cached g729 format. + */ +struct ast_format *ast_format_g729; + +/*! + * \brief Built-in cached g719 format. + */ +struct ast_format *ast_format_g719; + +/*! + * \brief Built-in cached h261 format. + */ +struct ast_format *ast_format_h261; + +/*! + * \brief Built-in cached h263 format. + */ +struct ast_format *ast_format_h263; + +/*! + * \brief Built-in cached h263 plus format. + */ +struct ast_format *ast_format_h263p; + +/*! + * \brief Built-in cached h264 format. + */ +struct ast_format *ast_format_h264; + +/*! + * \brief Built-in cached mp4 format. + */ +struct ast_format *ast_format_mp4; + +/*! + * \brief Built-in cached vp8 format. + */ +struct ast_format *ast_format_vp8; + +/*! + * \brief Built-in cached jpeg format. + */ +struct ast_format *ast_format_jpeg; + +/*! + * \brief Built-in cached png format. + */ +struct ast_format *ast_format_png; + +/*! + * \brief Built-in cached siren14 format. + */ +struct ast_format *ast_format_siren14; + +/*! + * \brief Built-in cached siren7 format. + */ +struct ast_format *ast_format_siren7; + +/*! + * \brief Built-in cached opus format. + */ +struct ast_format *ast_format_opus; + +/*! + * \brief Built-in cached t140 format. + */ +struct ast_format *ast_format_t140; + +/*! + * \brief Built-in cached t140 red format. + */ +struct ast_format *ast_format_t140_red; + +/*! + * \brief Built-in cached vp8 format. + */ +struct ast_format *ast_format_vp8; + +/*! + * \brief Built-in "null" format. + */ +struct ast_format *ast_format_none; + +/*! \brief Number of buckets to use for the media format cache (should be prime for performance reasons) */ +#define CACHE_BUCKETS 53 + +/*! \brief Cached formats */ +static struct ao2_container *formats; + +static int format_hash_cb(const void *obj, int flags) +{ + const struct ast_format *format; + const char *key; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_KEY: + key = obj; + return ast_str_case_hash(key); + case OBJ_SEARCH_OBJECT: + format = obj; + return ast_str_case_hash(ast_format_get_name(format)); + default: + /* Hash can only work on something with a full key. */ + ast_assert(0); + return 0; + } +} + +static int format_cmp_cb(void *obj, void *arg, int flags) +{ + const struct ast_format *left = obj; + const struct ast_format *right = arg; + const char *right_key = arg; + int cmp; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: + right_key = ast_format_get_name(right); + /* Fall through */ + case OBJ_SEARCH_KEY: + cmp = strcasecmp(ast_format_get_name(left), right_key); + break; + case OBJ_SEARCH_PARTIAL_KEY: + cmp = strncasecmp(ast_format_get_name(left), right_key, strlen(right_key)); + break; + default: + ast_assert(0); + cmp = 0; + break; + } + if (cmp) { + return 0; + } + + return CMP_MATCH; +} + +/*! \brief Function called when the process is shutting down */ +static void format_cache_shutdown(void) +{ + ao2_cleanup(formats); + formats = NULL; + + ao2_replace(ast_format_g723, NULL); + ao2_replace(ast_format_ulaw, NULL); + ao2_replace(ast_format_alaw, NULL); + ao2_replace(ast_format_gsm, NULL); + ao2_replace(ast_format_g726, NULL); + ao2_replace(ast_format_g726_aal2, NULL); + ao2_replace(ast_format_adpcm, NULL); + ao2_replace(ast_format_slin, NULL); + ao2_replace(ast_format_slin12, NULL); + ao2_replace(ast_format_slin16, NULL); + ao2_replace(ast_format_slin24, NULL); + ao2_replace(ast_format_slin32, NULL); + ao2_replace(ast_format_slin44, NULL); + ao2_replace(ast_format_slin48, NULL); + ao2_replace(ast_format_slin96, NULL); + ao2_replace(ast_format_slin192, NULL); + ao2_replace(ast_format_lpc10, NULL); + ao2_replace(ast_format_g729, NULL); + ao2_replace(ast_format_speex, NULL); + ao2_replace(ast_format_speex16, NULL); + ao2_replace(ast_format_speex32, NULL); + ao2_replace(ast_format_ilbc, NULL); + ao2_replace(ast_format_g722, NULL); + ao2_replace(ast_format_siren7, NULL); + ao2_replace(ast_format_siren14, NULL); + ao2_replace(ast_format_testlaw, NULL); + ao2_replace(ast_format_g719, NULL); + ao2_replace(ast_format_opus, NULL); + ao2_replace(ast_format_jpeg, NULL); + ao2_replace(ast_format_png, NULL); + ao2_replace(ast_format_h261, NULL); + ao2_replace(ast_format_h263, NULL); + ao2_replace(ast_format_h263p, NULL); + ao2_replace(ast_format_h264, NULL); + ao2_replace(ast_format_mp4, NULL); + ao2_replace(ast_format_vp8, NULL); + ao2_replace(ast_format_t140_red, NULL); + ao2_replace(ast_format_t140, NULL); + ao2_replace(ast_format_none, NULL); +} + +int ast_format_cache_init(void) +{ + formats = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, CACHE_BUCKETS, + format_hash_cb, format_cmp_cb); + if (!formats) { + return -1; + } + + ast_register_atexit(format_cache_shutdown); + + return 0; +} + +static void set_cached_format(const char *name, struct ast_format *format) +{ + if (!strcmp(name, "g723")) { + ao2_replace(ast_format_g723, format); + } else if (!strcmp(name, "ulaw")) { + ao2_replace(ast_format_ulaw, format); + } else if (!strcmp(name, "alaw")) { + ao2_replace(ast_format_alaw, format); + } else if (!strcmp(name, "gsm")) { + ao2_replace(ast_format_gsm, format); + } else if (!strcmp(name, "g726")) { + ao2_replace(ast_format_g726, format); + } else if (!strcmp(name, "g726aal2")) { + ao2_replace(ast_format_g726_aal2, format); + } else if (!strcmp(name, "adpcm")) { + ao2_replace(ast_format_adpcm, format); + } else if (!strcmp(name, "slin")) { + ao2_replace(ast_format_slin, format); + } else if (!strcmp(name, "slin12")) { + ao2_replace(ast_format_slin12, format); + } else if (!strcmp(name, "slin16")) { + ao2_replace(ast_format_slin16, format); + } else if (!strcmp(name, "slin24")) { + ao2_replace(ast_format_slin24, format); + } else if (!strcmp(name, "slin32")) { + ao2_replace(ast_format_slin32, format); + } else if (!strcmp(name, "slin44")) { + ao2_replace(ast_format_slin44, format); + } else if (!strcmp(name, "slin48")) { + ao2_replace(ast_format_slin48, format); + } else if (!strcmp(name, "slin96")) { + ao2_replace(ast_format_slin96, format); + } else if (!strcmp(name, "slin192")) { + ao2_replace(ast_format_slin192, format); + } else if (!strcmp(name, "lpc10")) { + ao2_replace(ast_format_lpc10, format); + } else if (!strcmp(name, "g729")) { + ao2_replace(ast_format_g729, format); + } else if (!strcmp(name, "speex")) { + ao2_replace(ast_format_speex, format); + } else if (!strcmp(name, "speex16")) { + ao2_replace(ast_format_speex16, format); + } else if (!strcmp(name, "speex32")) { + ao2_replace(ast_format_speex32, format); + } else if (!strcmp(name, "ilbc")) { + ao2_replace(ast_format_ilbc, format); + } else if (!strcmp(name, "g722")) { + ao2_replace(ast_format_g722, format); + } else if (!strcmp(name, "siren7")) { + ao2_replace(ast_format_siren7, format); + } else if (!strcmp(name, "siren14")) { + ao2_replace(ast_format_siren14, format); + } else if (!strcmp(name, "testlaw")) { + ao2_replace(ast_format_testlaw, format); + } else if (!strcmp(name, "g719")) { + ao2_replace(ast_format_g719, format); + } else if (!strcmp(name, "opus")) { + ao2_replace(ast_format_opus, format); + } else if (!strcmp(name, "jpeg")) { + ao2_replace(ast_format_jpeg, format); + } else if (!strcmp(name, "png")) { + ao2_replace(ast_format_png, format); + } else if (!strcmp(name, "h261")) { + ao2_replace(ast_format_h261, format); + } else if (!strcmp(name, "h263")) { + ao2_replace(ast_format_h263, format); + } else if (!strcmp(name, "h263p")) { + ao2_replace(ast_format_h263p, format); + } else if (!strcmp(name, "h264")) { + ao2_replace(ast_format_h264, format); + } else if (!strcmp(name, "mpeg4")) { + ao2_replace(ast_format_mp4, format); + } else if (!strcmp(name, "vp8")) { + ao2_replace(ast_format_vp8, format); + } else if (!strcmp(name, "red")) { + ao2_replace(ast_format_t140_red, format); + } else if (!strcmp(name, "t140")) { + ao2_replace(ast_format_t140, format); + } else if (!strcmp(name, "none")) { + ao2_replace(ast_format_none, format); + } +} + +int ast_format_cache_set(struct ast_format *format) +{ + SCOPED_AO2WRLOCK(lock, formats); + struct ast_format *old_format; + + ast_assert(format != NULL); + + if (ast_strlen_zero(ast_format_get_name(format))) { + return -1; + } + + old_format = ao2_find(formats, ast_format_get_name(format), OBJ_SEARCH_KEY | OBJ_NOLOCK); + if (old_format) { + ao2_unlink_flags(formats, old_format, OBJ_NOLOCK); + } + ao2_link_flags(formats, format, OBJ_NOLOCK); + + set_cached_format(ast_format_get_name(format), format); + + ast_verb(2, "%s cached format with name '%s'\n", + old_format ? "Updated" : "Created", + ast_format_get_name(format)); + + ao2_cleanup(old_format); + + return 0; +} + +struct ast_format *__ast_format_cache_get(const char *name) +{ + if (ast_strlen_zero(name)) { + return NULL; + } + + return ao2_find(formats, name, OBJ_SEARCH_KEY); +} + +struct ast_format *__ast_format_cache_get_debug(const char *name, const char *tag, const char *file, int line, const char *func) +{ + if (ast_strlen_zero(name)) { + return NULL; + } + + return __ao2_find_debug(formats, name, OBJ_SEARCH_KEY, S_OR(tag, "ast_format_cache_get"), file, line, func); +} + +struct ast_format *ast_format_cache_get_slin_by_rate(unsigned int rate) +{ + if (rate >= 192000) { + return ast_format_slin192; + } else if (rate >= 96000) { + return ast_format_slin96; + } else if (rate >= 48000) { + return ast_format_slin48; + } else if (rate >= 44100) { + return ast_format_slin44; + } else if (rate >= 32000) { + return ast_format_slin32; + } else if (rate >= 24000) { + return ast_format_slin24; + } else if (rate >= 16000) { + return ast_format_slin16; + } else if (rate >= 12000) { + return ast_format_slin12; + } + return ast_format_slin; +} + +int ast_format_cache_is_slinear(struct ast_format *format) +{ + if ((ast_format_cmp(format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) + || (ast_format_cmp(format, ast_format_slin16) == AST_FORMAT_CMP_EQUAL) + || (ast_format_cmp(format, ast_format_slin24) == AST_FORMAT_CMP_EQUAL) + || (ast_format_cmp(format, ast_format_slin32) == AST_FORMAT_CMP_EQUAL) + || (ast_format_cmp(format, ast_format_slin44) == AST_FORMAT_CMP_EQUAL) + || (ast_format_cmp(format, ast_format_slin48) == AST_FORMAT_CMP_EQUAL) + || (ast_format_cmp(format, ast_format_slin96) == AST_FORMAT_CMP_EQUAL) + || (ast_format_cmp(format, ast_format_slin192) == AST_FORMAT_CMP_EQUAL)) { + return 1; + } + + return 0; +}
\ No newline at end of file diff --git a/main/format_cap.c b/main/format_cap.c index 96c2fa3aa..65e469fc1 100644 --- a/main/format_cap.c +++ b/main/format_cap.c @@ -1,9 +1,9 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 2010, Digium, Inc. + * Copyright (C) 2014, Digium, Inc. * - * David Vossel <dvossel@digium.com> + * Joshua Colp <jcolp@digium.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact @@ -16,11 +16,11 @@ * at the top of the source tree. */ -/*! - * \file - * \brief Format Capability API +/*! \file + * + * \brief Format Capabilities API * - * \author David Vossel <dvossel@digium.com> + * \author Joshua Colp <jcolp@digium.com> */ /*** MODULEINFO @@ -29,653 +29,674 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") -#include "asterisk/_private.h" +#include "asterisk/logger.h" #include "asterisk/format.h" #include "asterisk/format_cap.h" -#include "asterisk/frame.h" +#include "asterisk/format_cache.h" +#include "asterisk/codec.h" #include "asterisk/astobj2.h" +#include "asterisk/strings.h" +#include "asterisk/vector.h" +#include "asterisk/linkedlists.h" #include "asterisk/utils.h" -#define FORMAT_STR_BUFSIZE 256 +/*! \brief Structure used for capability formats, adds framing */ +struct format_cap_framed { + /*! \brief A pointer to the format */ + struct ast_format *format; + /*! \brief The format framing size */ + unsigned int framing; + /*! \brief Linked list information */ + AST_LIST_ENTRY(format_cap_framed) entry; +}; +/*! \brief Format capabilities structure, holds formats + preference order + etc */ struct ast_format_cap { - /* The capabilities structure is just an ao2 container of ast_formats */ - struct ao2_container *formats; - struct ao2_iterator it; - /*! TRUE if the formats container created without a lock. */ - struct ast_flags flags; - unsigned int string_cache_valid; - char format_strs[0]; + /*! \brief Vector of formats, indexed using the codec identifier */ + AST_VECTOR(, struct format_cap_framed_list) formats; + /*! \brief Vector of formats, added in preference order */ + AST_VECTOR(, struct format_cap_framed *) preference_order; + /*! \brief Global framing size, applies to all formats if no framing present on format */ + unsigned int framing; }; -/*! 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) +/*! \brief Linked list for formats */ +AST_LIST_HEAD_NOLOCK(format_cap_framed_list, format_cap_framed); + +/*! \brief Dummy empty list for when we are inserting a new list */ +static const struct format_cap_framed_list format_cap_framed_list_empty = AST_LIST_HEAD_NOLOCK_INIT_VALUE; + +/*! \brief Destructor for format capabilities structure */ +static void format_cap_destroy(void *obj) { - 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; + struct ast_format_cap *cap = obj; + int idx; + + for (idx = 0; idx < AST_VECTOR_SIZE(&cap->formats); idx++) { + struct format_cap_framed_list *list = AST_VECTOR_GET_ADDR(&cap->formats, idx); + struct format_cap_framed *framed; + + while ((framed = AST_LIST_REMOVE_HEAD(list, entry))) { + ao2_ref(framed, -1); + } + } + AST_VECTOR_FREE(&cap->formats); + + for (idx = 0; idx < AST_VECTOR_SIZE(&cap->preference_order); idx++) { + struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, idx); + + /* This will always be non-null, unlike formats */ + ao2_ref(framed, -1); + } + AST_VECTOR_FREE(&cap->preference_order); } -static int hash_cb(const void *obj, const int flags) +static inline void format_cap_init(struct ast_format_cap *cap, enum ast_format_cap_flags flags) { - const struct ast_format *format = obj; - return format->id; + AST_VECTOR_INIT(&cap->formats, 0); + + /* TODO: Look at common usage of this and determine a good starting point */ + AST_VECTOR_INIT(&cap->preference_order, 5); + + cap->framing = UINT_MAX; } -struct ast_format_cap *ast_format_cap_alloc(enum ast_format_cap_flags flags) +struct ast_format_cap *__ast_format_cap_alloc(enum ast_format_cap_flags flags) { struct ast_format_cap *cap; - unsigned int alloc_size; - - alloc_size = sizeof(*cap); - if (flags & AST_FORMAT_CAP_FLAG_CACHE_STRINGS) { - alloc_size += FORMAT_STR_BUFSIZE; - } - cap = ast_calloc(1, alloc_size); + cap = ao2_alloc_options(sizeof(*cap), format_cap_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!cap) { return NULL; } - ast_set_flag(&cap->flags, flags); - cap->formats = ao2_container_alloc_options( - (flags & AST_FORMAT_CAP_FLAG_NOLOCK) ? - AO2_ALLOC_OPT_LOCK_NOLOCK : - AO2_ALLOC_OPT_LOCK_MUTEX, - 11, hash_cb, cmp_cb); - if (!cap->formats) { - ast_free(cap); - return NULL; - } + format_cap_init(cap, flags); return cap; } -void *ast_format_cap_destroy(struct ast_format_cap *cap) +struct ast_format_cap *__ast_format_cap_alloc_debug(enum ast_format_cap_flags flags, const char *tag, const char *file, int line, const char *func) { + struct ast_format_cap *cap; + + cap = __ao2_alloc_debug(sizeof(*cap), format_cap_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK, S_OR(tag, "ast_format_cap_alloc"), file, line, func, 1); if (!cap) { return NULL; } - ao2_ref(cap->formats, -1); - ast_free(cap); - return NULL; -} -static void format_cap_add(struct ast_format_cap *cap, const 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); - ao2_link(cap->formats, fnew); + format_cap_init(cap, flags); - ao2_ref(fnew, -1); + return cap; } -void ast_format_cap_add(struct ast_format_cap *cap, const struct ast_format *format) +void ast_format_cap_set_framing(struct ast_format_cap *cap, unsigned int framing) { - format_cap_add(cap, format); - cap->string_cache_valid = 0; + cap->framing = framing; } -void ast_format_cap_add_all_by_type(struct ast_format_cap *cap, enum ast_format_type type) +/*! \brief Destructor for format capabilities framed structure */ +static void format_cap_framed_destroy(void *obj) { - int x; - size_t f_len = 0; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); + struct format_cap_framed *framed = obj; - for (x = 0; x < f_len; x++) { - if (AST_FORMAT_GET_TYPE(f_list[x].format.id) == type) { - format_cap_add(cap, &f_list[x].format); - } - } - cap->string_cache_valid = 0; - ast_format_list_destroy(f_list); + ao2_cleanup(framed->format); } -void ast_format_cap_add_all(struct ast_format_cap *cap) +static inline int format_cap_framed_init(struct format_cap_framed *framed, struct ast_format_cap *cap, struct ast_format *format, unsigned int framing) { - int x; - size_t f_len = 0; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); + struct format_cap_framed_list *list; - for (x = 0; x < f_len; x++) { - format_cap_add(cap, &f_list[x].format); + framed->framing = framing; + + if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) { + if (AST_VECTOR_INSERT(&cap->formats, ast_format_get_codec_id(format), format_cap_framed_list_empty)) { + ao2_ref(framed, -1); + return -1; + } } - cap->string_cache_valid = 0; - ast_format_list_destroy(f_list); -} + list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format)); -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; + /* Order doesn't matter for formats, so insert at the head for performance reasons */ + ao2_ref(framed, +1); + AST_LIST_INSERT_HEAD(list, framed, entry); - if (!ast_format_cap_iscompatible(result, format)) { - format_cap_add(result, format); - } + /* This takes the allocation reference */ + AST_VECTOR_APPEND(&cap->preference_order, framed); + + cap->framing = MIN(cap->framing, framing ? framing : ast_format_get_default_ms(format)); return 0; } -void ast_format_cap_append(struct ast_format_cap *dst, const struct ast_format_cap *src) +/*! \internal \brief Determine if \c format is in \c cap */ +static int format_in_format_cap(struct ast_format_cap *cap, struct ast_format *format) { - ao2_callback(src->formats, OBJ_NODATA, append_cb, dst); - dst->string_cache_valid = 0; -} + struct format_cap_framed *framed; + int i; -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; + for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); i++) { + framed = AST_VECTOR_GET(&cap->preference_order, i); + + if (ast_format_get_codec_id(format) == ast_format_get_codec_id(framed->format)) { + return 1; + } + } - format_cap_add(result, format); return 0; } -static void format_cap_remove_all(struct ast_format_cap *cap) +int __ast_format_cap_append(struct ast_format_cap *cap, struct ast_format *format, unsigned int framing) { - ao2_callback(cap->formats, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, NULL, NULL); -} + struct format_cap_framed *framed; -void ast_format_cap_copy(struct ast_format_cap *dst, const struct ast_format_cap *src) -{ - format_cap_remove_all(dst); - ao2_callback(src->formats, OBJ_NODATA, copy_cb, dst); - dst->string_cache_valid = 0; -} + ast_assert(format != NULL); -struct ast_format_cap *ast_format_cap_dup(const struct ast_format_cap *cap) -{ - struct ast_format_cap *dst; + if (format_in_format_cap(cap, format)) { + return 0; + } - dst = ast_format_cap_alloc(ast_test_flag(&cap->flags, AST_FLAGS_ALL)); - if (!dst) { - return NULL; + framed = ao2_alloc_options(sizeof(*framed), format_cap_framed_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!framed) { + return -1; } - ao2_callback(cap->formats, OBJ_NODATA, copy_cb, dst); - dst->string_cache_valid = 0; - return dst; + framed->format = ao2_bump(format); + + return format_cap_framed_init(framed, cap, format, framing); } -int ast_format_cap_is_empty(const struct ast_format_cap *cap) +int __ast_format_cap_append_debug(struct ast_format_cap *cap, struct ast_format *format, unsigned int framing, const char *tag, const char *file, int line, const char *func) { - if (!cap) { - return 1; + struct format_cap_framed *framed; + + ast_assert(format != NULL); + + if (format_in_format_cap(cap, format)) { + return 0; } - 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; + framed = ao2_alloc_options(sizeof(*framed), format_cap_framed_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!framed) { + return -1; + } - return (ast_format_cmp(format1, format2) == AST_FORMAT_CMP_EQUAL) ? CMP_MATCH : 0; + __ao2_ref_debug(format, +1, S_OR(tag, "ast_format_cap_append"), file, line, func); + framed->format = format; + + return format_cap_framed_init(framed, cap, format, framing); } -int ast_format_cap_remove(struct ast_format_cap *cap, struct ast_format *format) +int ast_format_cap_append_by_type(struct ast_format_cap *cap, enum ast_media_type type) { - struct ast_format *fremove; + int id; - fremove = ao2_callback(cap->formats, OBJ_POINTER | OBJ_UNLINK, find_exact_cb, format); - if (fremove) { - ao2_ref(fremove, -1); - cap->string_cache_valid = 0; - return 0; + for (id = 1; id < ast_codec_get_max(); ++id) { + struct ast_codec *codec = ast_codec_get_by_id(id); + struct ast_format *format; + int res; + + if (!codec) { + continue; + } + + if ((type != AST_MEDIA_TYPE_UNKNOWN) && codec->type != type) { + ao2_ref(codec, -1); + continue; + } + + format = ast_format_create(codec); + ao2_ref(codec, -1); + + if (!format) { + return -1; + } + + /* Use the global framing or default framing of the codec */ + res = ast_format_cap_append(cap, format, 0); + ao2_ref(format, -1); + + if (res) { + return -1; + } } - return -1; + return 0; } -struct multiple_by_id_data { - struct ast_format *format; - int match_found; -}; - -static int multiple_by_id_cb(void *obj, void *arg, int flag) +int ast_format_cap_append_from_cap(struct ast_format_cap *dst, const struct ast_format_cap *src, + enum ast_media_type type) { - struct multiple_by_id_data *data = arg; - struct ast_format *format = obj; - int res; + int idx, res = 0; + + for (idx = 0; (idx < AST_VECTOR_SIZE(&src->preference_order)) && !res; ++idx) { + struct format_cap_framed *framed = AST_VECTOR_GET(&src->preference_order, idx); - res = (format->id == data->format->id) ? CMP_MATCH : 0; - if (res) { - data->match_found = 1; + if (type == AST_MEDIA_TYPE_UNKNOWN || ast_format_get_type(framed->format) == type) { + res = ast_format_cap_append(dst, framed->format, framed->framing); + } } return res; } -int ast_format_cap_remove_byid(struct ast_format_cap *cap, enum ast_format_id id) +static int format_cap_replace(struct ast_format_cap *cap, struct ast_format *format, unsigned int framing) { - struct ast_format format = { - .id = id, - }; - struct multiple_by_id_data data = { - .format = &format, - .match_found = 0, - }; - - ao2_callback(cap->formats, - OBJ_NODATA | 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) { - cap->string_cache_valid = 0; - return 0; + struct format_cap_framed *framed; + int i; + + ast_assert(format != NULL); + + for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); i++) { + framed = AST_VECTOR_GET(&cap->preference_order, i); + + if (ast_format_get_codec_id(format) == ast_format_get_codec_id(framed->format)) { + ao2_t_replace(framed->format, format, "replacing with new format"); + framed->framing = framing; + return 0; + } } return -1; } -static int multiple_by_type_cb(void *obj, void *arg, int flag) +void ast_format_cap_replace_from_cap(struct ast_format_cap *dst, const struct ast_format_cap *src, + enum ast_media_type type) { - int *type = arg; - struct ast_format *format = obj; - return ((AST_FORMAT_GET_TYPE(format->id)) == *type) ? CMP_MATCH : 0; -} + int idx; -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, - multiple_by_type_cb, - &type); - cap->string_cache_valid = 0; + for (idx = 0; (idx < AST_VECTOR_SIZE(&src->preference_order)); ++idx) { + struct format_cap_framed *framed = AST_VECTOR_GET(&src->preference_order, idx); + + if (type == AST_MEDIA_TYPE_UNKNOWN || ast_format_get_type(framed->format) == type) { + format_cap_replace(dst, framed->format, framed->framing); + } + } } -void ast_format_cap_remove_all(struct ast_format_cap *cap) +int ast_format_cap_update_by_allow_disallow(struct ast_format_cap *cap, const char *list, int allowing) { - format_cap_remove_all(cap); - cap->string_cache_valid = 0; + int res = 0, all = 0, iter_allowing; + char *parse = NULL, *this = NULL, *psize = NULL; + + parse = ast_strdupa(list); + while ((this = strsep(&parse, ","))) { + int framems = 0; + struct ast_format *format = NULL; + + iter_allowing = allowing; + if (*this == '!') { + this++; + iter_allowing = !allowing; + } + if ((psize = strrchr(this, ':'))) { + *psize++ = '\0'; + ast_debug(1, "Packetization for codec: %s is %s\n", this, psize); + if (!sscanf(psize, "%30d", &framems) || (framems < 0)) { + framems = 0; + res = -1; + ast_log(LOG_WARNING, "Bad packetization value for codec %s\n", this); + continue; + } + } + all = strcasecmp(this, "all") ? 0 : 1; + + if (!all && !(format = ast_format_cache_get(this))) { + ast_log(LOG_WARNING, "Cannot %s unknown format '%s'\n", iter_allowing ? "allow" : "disallow", this); + res = -1; + continue; + } + + if (cap) { + if (iter_allowing) { + if (all) { + ast_format_cap_append_by_type(cap, AST_MEDIA_TYPE_UNKNOWN); + } else { + ast_format_cap_append(cap, format, framems); + } + } else { + if (all) { + ast_format_cap_remove_by_type(cap, AST_MEDIA_TYPE_UNKNOWN); + } else { + ast_format_cap_remove(cap, format); + } + } + } + + ao2_cleanup(format); + } + return res; } -void ast_format_cap_set(struct ast_format_cap *cap, struct ast_format *format) +size_t ast_format_cap_count(const struct ast_format_cap *cap) { - format_cap_remove_all(cap); - format_cap_add(cap, format); - cap->string_cache_valid = 0; + return AST_VECTOR_SIZE(&cap->preference_order); } -int ast_format_cap_get_compatible_format(const struct ast_format_cap *cap, const struct ast_format *format, struct ast_format *result) +struct ast_format *ast_format_cap_get_format(const struct ast_format_cap *cap, int position) { - struct ast_format *f; - struct ast_format_cap *tmp_cap = (struct ast_format_cap *) cap; + struct format_cap_framed *framed; + + ast_assert(position < AST_VECTOR_SIZE(&cap->preference_order)); - f = ao2_find(tmp_cap->formats, format, OBJ_POINTER); - if (f) { - ast_format_copy(result, f); - ao2_ref(f, -1); - return 1; + if (position >= AST_VECTOR_SIZE(&cap->preference_order)) { + return NULL; } - ast_format_clear(result); - return 0; + + framed = AST_VECTOR_GET(&cap->preference_order, position); + + ast_assert(framed->format != ast_format_none); + ao2_ref(framed->format, +1); + return framed->format; } -int ast_format_cap_iscompatible(const struct ast_format_cap *cap, const struct ast_format *format) +struct ast_format *ast_format_cap_get_best_by_type(const struct ast_format_cap *cap, enum ast_media_type type) { - struct ast_format *f; - struct ast_format_cap *tmp_cap = (struct ast_format_cap *) cap; + int i; - if (!tmp_cap) { - return 0; + if (type == AST_MEDIA_TYPE_UNKNOWN) { + return ast_format_cap_get_format(cap, 0); } - f = ao2_find(tmp_cap->formats, format, OBJ_POINTER); - if (f) { - ao2_ref(f, -1); - return 1; + for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); i++) { + struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, i); + + if (ast_format_get_type(framed->format) == type) { + ao2_ref(framed->format, +1); + ast_assert(framed->format != ast_format_none); + return framed->format; + } } - return 0; + return NULL; } -struct byid_data { - struct ast_format *result; - enum ast_format_id id; -}; -static int find_best_byid_cb(void *obj, void *arg, int flag) +unsigned int ast_format_cap_get_framing(const struct ast_format_cap *cap) { - struct ast_format *format = obj; - struct byid_data *data = arg; + return (cap->framing != UINT_MAX) ? cap->framing : 0; +} - if (data->id != format->id) { +unsigned int ast_format_cap_get_format_framing(const struct ast_format_cap *cap, const struct ast_format *format) +{ + unsigned int framing; + struct format_cap_framed_list *list; + struct format_cap_framed *framed, *result = NULL; + + if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) { return 0; } - if (!data->result->id || (ast_format_rate(data->result) < ast_format_rate(format))) { - ast_format_copy(data->result, format); + + framing = cap->framing != UINT_MAX ? cap->framing : ast_format_get_default_ms(format); + list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format)); + + AST_LIST_TRAVERSE(list, framed, entry) { + enum ast_format_cmp_res res = ast_format_cmp(format, framed->format); + + if (res == AST_FORMAT_CMP_NOT_EQUAL) { + continue; + } + + result = framed; + + if (res == AST_FORMAT_CMP_EQUAL) { + break; + } } - return 0; -} -int ast_format_cap_best_byid(const struct ast_format_cap *cap, enum ast_format_id id, struct ast_format *result) -{ - struct byid_data data; - data.result = result; - data.id = id; - - ast_format_clear(result); - ao2_callback(cap->formats, - OBJ_MULTIPLE | OBJ_NODATA, - find_best_byid_cb, - &data); - return result->id ? 1 : 0; + if (result && result->framing) { + framing = result->framing; + } + + return framing; } -/*! \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. +/*! + * \brief format_cap_framed comparator for AST_VECTOR_REMOVE_CMP_ORDERED() + * + * \param elem Element to compare against + * \param value Value to compare with the vector element. + * + * \return 0 if element does not match. + * \return Non-zero if element matches. */ -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; -}; +#define FORMAT_CAP_FRAMED_ELEM_CMP(elem, value) ((elem)->format == (value)) -static int find_joint_cb(void *obj, void *arg, int flag) +/*! + * \brief format_cap_framed vector element cleanup. + * + * \param elem Element to cleanup + * + * \return Nothing + */ +#define FORMAT_CAP_FRAMED_ELEM_CLEANUP(elem) ao2_cleanup((elem)) + +int ast_format_cap_remove(struct ast_format_cap *cap, struct ast_format *format) { - struct ast_format *format = obj; - struct find_joint_data *data = arg; + struct format_cap_framed_list *list; + struct format_cap_framed *framed; - 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++; + ast_assert(format != NULL); + + if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) { + return -1; } - return 0; -} + list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format)); -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, 0); - while ((tmp = ao2_iterator_next(&it))) { - data.format = tmp; - ao2_callback(cap2->formats, - OBJ_MULTIPLE | OBJ_NODATA, - find_joint_cb, - &data); - ao2_ref(tmp, -1); + AST_LIST_TRAVERSE_SAFE_BEGIN(list, framed, entry) { + if (!FORMAT_CAP_FRAMED_ELEM_CMP(framed, format)) { + continue; + } + + AST_LIST_REMOVE_CURRENT(entry); + FORMAT_CAP_FRAMED_ELEM_CLEANUP(framed); + break; } - ao2_iterator_destroy(&it); + AST_LIST_TRAVERSE_SAFE_END; - return data.joint_found ? 1 : 0; + return AST_VECTOR_REMOVE_CMP_ORDERED(&cap->preference_order, format, + FORMAT_CAP_FRAMED_ELEM_CMP, FORMAT_CAP_FRAMED_ELEM_CLEANUP); } -int ast_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2) +void ast_format_cap_remove_by_type(struct ast_format_cap *cap, enum ast_media_type type) { - struct ao2_iterator it; - struct ast_format *tmp; + int idx; - if (ao2_container_count(cap1->formats) != ao2_container_count(cap2->formats)) { - return 0; /* if they are not the same size, they are not identical */ - } + for (idx = 0; idx < AST_VECTOR_SIZE(&cap->formats); ++idx) { + struct format_cap_framed_list *list = AST_VECTOR_GET_ADDR(&cap->formats, idx); + struct format_cap_framed *framed; - it = ao2_iterator_init(cap1->formats, 0); - while ((tmp = ao2_iterator_next(&it))) { - if (!ast_format_cap_iscompatible(cap2, tmp)) { - ao2_ref(tmp, -1); - ao2_iterator_destroy(&it); - return 0; + AST_LIST_TRAVERSE_SAFE_BEGIN(list, framed, entry) { + if ((type != AST_MEDIA_TYPE_UNKNOWN) && + ast_format_get_type(framed->format) != type) { + continue; + } + + AST_LIST_REMOVE_CURRENT(entry); + AST_VECTOR_REMOVE_CMP_ORDERED(&cap->preference_order, framed->format, + FORMAT_CAP_FRAMED_ELEM_CMP, FORMAT_CAP_FRAMED_ELEM_CLEANUP); + ao2_ref(framed, -1); } - ao2_ref(tmp, -1); + AST_LIST_TRAVERSE_SAFE_END; } - 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 ast_format *ast_format_cap_get_compatible_format(const struct ast_format_cap *cap, const struct ast_format *format) { - struct ao2_iterator it; - struct ast_format_cap *result = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK); - struct ast_format *tmp; - struct find_joint_data data = { - .joint_found = 0, - .joint_cap = result, - }; - if (!result) { + struct format_cap_framed_list *list; + struct format_cap_framed *framed; + struct ast_format *result = NULL; + + ast_assert(format != NULL); + + if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) { return NULL; } - it = ao2_iterator_init(cap1->formats, 0); - while ((tmp = ao2_iterator_next(&it))) { - data.format = tmp; - ao2_callback(cap2->formats, - OBJ_MULTIPLE | OBJ_NODATA, - find_joint_cb, - &data); - ao2_ref(tmp, -1); - } - ao2_iterator_destroy(&it); + list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format)); - if (ao2_container_count(result->formats)) { - return result; - } + AST_LIST_TRAVERSE(list, framed, entry) { + enum ast_format_cmp_res res = ast_format_cmp(format, framed->format); - result = ast_format_cap_destroy(result); - return NULL; -} + if (res == AST_FORMAT_CMP_NOT_EQUAL) { + continue; + } -static int joint_copy_helper(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result, int append) -{ - struct ao2_iterator it; - struct ast_format *tmp; - struct find_joint_data data = { - .joint_cap = result, - .joint_found = 0, - }; - if (!append) { - format_cap_remove_all(result); - } - it = ao2_iterator_init(cap1->formats, 0); - while ((tmp = ao2_iterator_next(&it))) { - data.format = tmp; - ao2_callback(cap2->formats, - OBJ_MULTIPLE | OBJ_NODATA, - find_joint_cb, - &data); - ao2_ref(tmp, -1); - } - ao2_iterator_destroy(&it); + /* Replace any current result, this one will also be a subset OR an exact match */ + ao2_cleanup(result); - result->string_cache_valid = 0; + result = ast_format_joint(format, framed->format); - return ao2_container_count(result->formats) ? 1 : 0; -} + /* If it's a match we can do no better so return asap */ + if (res == AST_FORMAT_CMP_EQUAL) { + break; + } + } -int ast_format_cap_joint_append(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result) -{ - return joint_copy_helper(cap1, cap2, result, 1); + return result; } -int ast_format_cap_joint_copy(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result) +enum ast_format_cmp_res ast_format_cap_iscompatible_format(const struct ast_format_cap *cap, + const struct ast_format *format) { - return joint_copy_helper(cap1, cap2, result, 0); -} + enum ast_format_cmp_res res = AST_FORMAT_CMP_NOT_EQUAL; + struct format_cap_framed_list *list; + struct format_cap_framed *framed; -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(AST_FORMAT_CAP_FLAG_NOLOCK); - struct ast_format *tmp; + ast_assert(format != NULL); - if (!result) { - return NULL; + if (ast_format_get_codec_id(format) >= AST_VECTOR_SIZE(&cap->formats)) { + return AST_FORMAT_CMP_NOT_EQUAL; } - /* 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, 0); - while ((tmp = ao2_iterator_next(&it))) { - if (AST_FORMAT_GET_TYPE(tmp->id) == ftype) { - /* copy format */ - ast_format_cap_add(result, tmp); + list = AST_VECTOR_GET_ADDR(&cap->formats, ast_format_get_codec_id(format)); + + AST_LIST_TRAVERSE(list, framed, entry) { + enum ast_format_cmp_res cmp = ast_format_cmp(format, framed->format); + + if (cmp == AST_FORMAT_CMP_NOT_EQUAL) { + continue; } - ao2_ref(tmp, -1); - } - ao2_iterator_destroy(&it); - if (ao2_container_count(result->formats)) { - return result; + res = cmp; + + if (res == AST_FORMAT_CMP_EQUAL) { + break; + } } - result = ast_format_cap_destroy(result); - return NULL; + return res; } - -int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_format_type type) +int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_media_type type) { - struct ao2_iterator it; - struct ast_format *tmp; + int idx; - it = ao2_iterator_init(cap->formats, 0); - while ((tmp = ao2_iterator_next(&it))) { - if (AST_FORMAT_GET_TYPE(tmp->id) == type) { - ao2_ref(tmp, -1); - ao2_iterator_destroy(&it); + for (idx = 0; idx < AST_VECTOR_SIZE(&cap->preference_order); ++idx) { + struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, idx); + + if (ast_format_get_type(framed->format) == type) { return 1; } - ao2_ref(tmp, -1); } - ao2_iterator_destroy(&it); return 0; } -void ast_format_cap_iter_start(struct ast_format_cap *cap) +int ast_format_cap_get_compatible(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, + struct ast_format_cap *result) { - /* We can unconditionally lock even if the container has no lock. */ - ao2_lock(cap->formats); - cap->it = ao2_iterator_init(cap->formats, AO2_ITERATOR_DONTLOCK); -} + int idx, res = 0; -void ast_format_cap_iter_end(struct ast_format_cap *cap) -{ - ao2_iterator_destroy(&cap->it); - /* We can unconditionally unlock even if the container has no lock. */ - ao2_unlock(cap->formats); -} + for (idx = 0; idx < AST_VECTOR_SIZE(&cap1->preference_order); ++idx) { + struct format_cap_framed *framed = AST_VECTOR_GET(&cap1->preference_order, idx); + struct ast_format *format; -int ast_format_cap_iter_next(struct ast_format_cap *cap, struct ast_format *format) -{ - struct ast_format *tmp = ao2_iterator_next(&cap->it); + format = ast_format_cap_get_compatible_format(cap2, framed->format); + if (!format) { + continue; + } - if (!tmp) { - return -1; + res = ast_format_cap_append(result, format, framed->framing); + ao2_ref(format, -1); + + if (res) { + break; + } } - ast_format_copy(format, tmp); - ao2_ref(tmp, -1); - return 0; + return res; } -static char *getformatname_multiple(char *buf, size_t size, struct ast_format_cap *cap) +int ast_format_cap_iscompatible(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2) { - int x; - unsigned len; - char *start, *end = buf; - struct ast_format tmp_fmt; - size_t f_len; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); - - if (!size) { - f_list = ast_format_list_destroy(f_list); - return buf; - } - snprintf(end, size, "("); - len = strlen(end); - end += len; - size -= len; - start = end; - for (x = 0; x < f_len; x++) { - ast_format_copy(&tmp_fmt, &f_list[x].format); - if (ast_format_cap_iscompatible(cap, &tmp_fmt)) { - snprintf(end, size, "%s|", f_list[x].name); - len = strlen(end); - end += len; - size -= len; + int idx; + + for (idx = 0; idx < AST_VECTOR_SIZE(&cap1->preference_order); ++idx) { + struct format_cap_framed *framed = AST_VECTOR_GET(&cap1->preference_order, idx); + + if (ast_format_cap_iscompatible_format(cap2, framed->format) != AST_FORMAT_CMP_NOT_EQUAL) { + return 1; } } - if (start == end) { - ast_copy_string(start, "nothing)", size); - } else if (size > 1) { - *(end - 1) = ')'; - } - f_list = ast_format_list_destroy(f_list); - return buf; + + return 0; } -char *ast_getformatname_multiple(char *buf, size_t size, struct ast_format_cap *cap) +static int internal_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2) { - if (ast_test_flag(&cap->flags, AST_FORMAT_CAP_FLAG_CACHE_STRINGS)) { - if (!cap->string_cache_valid) { - getformatname_multiple(cap->format_strs, FORMAT_STR_BUFSIZE, cap); - cap->string_cache_valid = 1; + int idx; + struct ast_format *tmp; + + for (idx = 0; idx < AST_VECTOR_SIZE(&cap1->preference_order); ++idx) { + tmp = ast_format_cap_get_format(cap1, idx); + + if (ast_format_cap_iscompatible_format(cap2, tmp) != AST_FORMAT_CMP_EQUAL) { + ao2_ref(tmp, -1); + return 0; } - ast_copy_string(buf, cap->format_strs, size); - return buf; + + ao2_ref(tmp, -1); } - return getformatname_multiple(buf, size, cap); + return 1; } -uint64_t ast_format_cap_to_old_bitfield(const struct ast_format_cap *cap) +int ast_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2) { - uint64_t res = 0; - struct ao2_iterator it; - struct ast_format *tmp; + if (AST_VECTOR_SIZE(&cap1->preference_order) != AST_VECTOR_SIZE(&cap2->preference_order)) { + return 0; /* if they are not the same size, they are not identical */ + } - it = ao2_iterator_init(cap->formats, 0); - while ((tmp = ao2_iterator_next(&it))) { - res |= ast_format_to_old_bitfield(tmp); - ao2_ref(tmp, -1); + if (!internal_format_cap_identical(cap1, cap2)) { + return 0; } - ao2_iterator_destroy(&it); - return res; + + return internal_format_cap_identical(cap2, cap1); } -void ast_format_cap_from_old_bitfield(struct ast_format_cap *dst, uint64_t src) +const char *ast_format_cap_get_names(struct ast_format_cap *cap, struct ast_str **buf) { - uint64_t tmp = 0; - int x; - struct ast_format tmp_format = { 0, }; - - format_cap_remove_all(dst); - for (x = 0; x < 64; x++) { - tmp = (1ULL << x); - if (tmp & src) { - format_cap_add(dst, ast_format_from_old_bitfield(&tmp_format, tmp)); + int i; + + ast_str_set(buf, 0, "("); + + if (!AST_VECTOR_SIZE(&cap->preference_order)) { + ast_str_append(buf, 0, "nothing)"); + return ast_str_buffer(*buf); + } + + for (i = 0; i < AST_VECTOR_SIZE(&cap->preference_order); ++i) { + int res; + struct format_cap_framed *framed = AST_VECTOR_GET(&cap->preference_order, i); + + res = ast_str_append(buf, 0, "%s%s", ast_format_get_name(framed->format), + i < AST_VECTOR_SIZE(&cap->preference_order) - 1 ? "|" : ""); + if (res < 0) { + break; } } - dst->string_cache_valid = 0; + ast_str_append(buf, 0, ")"); + + return ast_str_buffer(*buf); } diff --git a/main/format_compatibility.c b/main/format_compatibility.c new file mode 100644 index 000000000..df82bacd5 --- /dev/null +++ b/main/format_compatibility.c @@ -0,0 +1,274 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2014, Digium, Inc. + * + * Joshua Colp <jcolp@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 Media Format Bitfield Compatibility API + * + * \author Joshua Colp <jcolp@digium.com> + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/logger.h" +#include "asterisk/astobj2.h" +#include "asterisk/codec.h" +#include "asterisk/format.h" +#include "asterisk/format_compatibility.h" +#include "asterisk/format_cache.h" + +uint64_t ast_format_compatibility_format2bitfield(const struct ast_format *format) +{ + if (ast_format_cmp(format, ast_format_g723) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_G723; + } else if (ast_format_cmp(format, ast_format_gsm) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_GSM; + } else if (ast_format_cmp(format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_ULAW; + } else if (ast_format_cmp(format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_ALAW; + } else if (ast_format_cmp(format, ast_format_g726_aal2) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_G726_AAL2; + } else if (ast_format_cmp(format, ast_format_adpcm) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_ADPCM; + } else if (ast_format_cmp(format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_SLIN; + } else if (ast_format_cmp(format, ast_format_lpc10) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_LPC10; + } else if (ast_format_cmp(format, ast_format_g729) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_G729; + } else if (ast_format_cmp(format, ast_format_speex) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_SPEEX; + } else if (ast_format_cmp(format, ast_format_ilbc) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_ILBC; + } else if (ast_format_cmp(format, ast_format_g726) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_G726; + } else if (ast_format_cmp(format, ast_format_g722) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_G722; + } else if (ast_format_cmp(format, ast_format_siren7) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_SIREN7; + } else if (ast_format_cmp(format, ast_format_siren14) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_SIREN14; + } else if (ast_format_cmp(format, ast_format_slin16) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_SLIN16; + } else if (ast_format_cmp(format, ast_format_g719) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_G719; + } else if (ast_format_cmp(format, ast_format_speex16) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_SPEEX16; + } else if (ast_format_cmp(format, ast_format_opus) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_OPUS; + } else if (ast_format_cmp(format, ast_format_testlaw) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_TESTLAW; + } else if (ast_format_cmp(format, ast_format_h261) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_H261; + } else if (ast_format_cmp(format, ast_format_h263) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_H263; + } else if (ast_format_cmp(format, ast_format_h263p) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_H263P; + } else if (ast_format_cmp(format, ast_format_h264) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_H264; + } else if (ast_format_cmp(format, ast_format_mp4) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_MP4; + } else if (ast_format_cmp(format, ast_format_vp8) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_VP8; + } else if (ast_format_cmp(format, ast_format_jpeg) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_JPEG; + } else if (ast_format_cmp(format, ast_format_png) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_PNG; + } else if (ast_format_cmp(format, ast_format_t140_red) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_T140_RED; + } else if (ast_format_cmp(format, ast_format_t140) == AST_FORMAT_CMP_EQUAL) { + return AST_FORMAT_T140; + } + + return 0; +} + +uint64_t ast_format_compatibility_codec2bitfield(const struct ast_codec *codec) +{ + if (codec->id == ast_format_get_codec_id(ast_format_g723)) { + return AST_FORMAT_G723; + } else if (codec->id == ast_format_get_codec_id(ast_format_gsm)) { + return AST_FORMAT_GSM; + } else if (codec->id == ast_format_get_codec_id(ast_format_ulaw)) { + return AST_FORMAT_ULAW; + } else if (codec->id == ast_format_get_codec_id(ast_format_alaw)) { + return AST_FORMAT_ALAW; + } else if (codec->id == ast_format_get_codec_id(ast_format_g726_aal2)) { + return AST_FORMAT_G726_AAL2; + } else if (codec->id == ast_format_get_codec_id(ast_format_adpcm)) { + return AST_FORMAT_ADPCM; + } else if (codec->id == ast_format_get_codec_id(ast_format_slin)) { + return AST_FORMAT_SLIN; + } else if (codec->id == ast_format_get_codec_id(ast_format_lpc10)) { + return AST_FORMAT_LPC10; + } else if (codec->id == ast_format_get_codec_id(ast_format_g729)) { + return AST_FORMAT_G729; + } else if (codec->id == ast_format_get_codec_id(ast_format_speex)) { + return AST_FORMAT_SPEEX; + } else if (codec->id == ast_format_get_codec_id(ast_format_ilbc)) { + return AST_FORMAT_ILBC; + } else if (codec->id == ast_format_get_codec_id(ast_format_g726)) { + return AST_FORMAT_G726; + } else if (codec->id == ast_format_get_codec_id(ast_format_g722)) { + return AST_FORMAT_G722; + } else if (codec->id == ast_format_get_codec_id(ast_format_siren7)) { + return AST_FORMAT_SIREN7; + } else if (codec->id == ast_format_get_codec_id(ast_format_siren14)) { + return AST_FORMAT_SIREN14; + } else if (codec->id == ast_format_get_codec_id(ast_format_slin16)) { + return AST_FORMAT_SLIN16; + } else if (codec->id == ast_format_get_codec_id(ast_format_g719)) { + return AST_FORMAT_G719; + } else if (codec->id == ast_format_get_codec_id(ast_format_speex16)) { + return AST_FORMAT_SPEEX16; + } else if (codec->id == ast_format_get_codec_id(ast_format_opus)) { + return AST_FORMAT_OPUS; + } else if (codec->id == ast_format_get_codec_id(ast_format_testlaw)) { + return AST_FORMAT_TESTLAW; + } else if (codec->id == ast_format_get_codec_id(ast_format_h261)) { + return AST_FORMAT_H261; + } else if (codec->id == ast_format_get_codec_id(ast_format_h263)) { + return AST_FORMAT_H263; + } else if (codec->id == ast_format_get_codec_id(ast_format_h263p)) { + return AST_FORMAT_H263P; + } else if (codec->id == ast_format_get_codec_id(ast_format_h264)) { + return AST_FORMAT_H264; + } else if (codec->id == ast_format_get_codec_id(ast_format_mp4)) { + return AST_FORMAT_MP4; + } else if (codec->id == ast_format_get_codec_id(ast_format_vp8)) { + return AST_FORMAT_VP8; + } else if (codec->id == ast_format_get_codec_id(ast_format_jpeg)) { + return AST_FORMAT_JPEG; + } else if (codec->id == ast_format_get_codec_id(ast_format_png)) { + return AST_FORMAT_PNG; + } else if (codec->id == ast_format_get_codec_id(ast_format_t140_red)) { + return AST_FORMAT_T140_RED; + } else if (codec->id == ast_format_get_codec_id(ast_format_t140)) { + return AST_FORMAT_T140; + } + + return 0; +} + +struct ast_format *ast_format_compatibility_bitfield2format(uint64_t bitfield) +{ + switch (bitfield) { + /*! G.723.1 compression */ + case AST_FORMAT_G723: + return ast_format_g723; + /*! GSM compression */ + case AST_FORMAT_GSM: + return ast_format_gsm; + /*! Raw mu-law data (G.711) */ + case AST_FORMAT_ULAW: + return ast_format_ulaw; + /*! Raw A-law data (G.711) */ + case AST_FORMAT_ALAW: + return ast_format_alaw; + /*! ADPCM (G.726, 32kbps, AAL2 codeword packing) */ + case AST_FORMAT_G726_AAL2: + return ast_format_g726_aal2; + /*! ADPCM (IMA) */ + case AST_FORMAT_ADPCM: + return ast_format_adpcm; + /*! Raw 16-bit Signed Linear (8000 Hz) PCM */ + case AST_FORMAT_SLIN: + return ast_format_slin; + /*! LPC10, 180 samples/frame */ + case AST_FORMAT_LPC10: + return ast_format_lpc10; + /*! G.729A audio */ + case AST_FORMAT_G729: + return ast_format_g729; + /*! SpeeX Free Compression */ + case AST_FORMAT_SPEEX: + return ast_format_speex; + /*! iLBC Free Compression */ + case AST_FORMAT_ILBC: + return ast_format_ilbc; + /*! ADPCM (G.726, 32kbps, RFC3551 codeword packing) */ + case AST_FORMAT_G726: + return ast_format_g726; + /*! G.722 */ + case AST_FORMAT_G722: + return ast_format_g722; + /*! G.722.1 (also known as Siren7, 32kbps assumed) */ + case AST_FORMAT_SIREN7: + return ast_format_siren7; + /*! G.722.1 Annex C (also known as Siren14, 48kbps assumed) */ + case AST_FORMAT_SIREN14: + return ast_format_siren14; + /*! Raw 16-bit Signed Linear (16000 Hz) PCM */ + case AST_FORMAT_SLIN16: + return ast_format_slin16; + /*! G.719 (64 kbps assumed) */ + case AST_FORMAT_G719: + return ast_format_g719; + /*! SpeeX Wideband (16kHz) Free Compression */ + case AST_FORMAT_SPEEX16: + return ast_format_speex16; + /*! Opus audio (8kHz, 16kHz, 24kHz, 48Khz) */ + case AST_FORMAT_OPUS: + return ast_format_opus; + /*! Raw mu-law data (G.711) */ + case AST_FORMAT_TESTLAW: + return ast_format_testlaw; + + /*! H.261 Video */ + case AST_FORMAT_H261: + return ast_format_h261; + /*! H.263 Video */ + case AST_FORMAT_H263: + return ast_format_h263; + /*! H.263+ Video */ + case AST_FORMAT_H263P: + return ast_format_h263p; + /*! H.264 Video */ + case AST_FORMAT_H264: + return ast_format_h264; + /*! MPEG4 Video */ + case AST_FORMAT_MP4: + return ast_format_mp4; + /*! VP8 Video */ + case AST_FORMAT_VP8: + return ast_format_vp8; + + /*! JPEG Images */ + case AST_FORMAT_JPEG: + return ast_format_jpeg; + /*! PNG Images */ + case AST_FORMAT_PNG: + return ast_format_png; + + /*! T.140 RED Text format RFC 4103 */ + case AST_FORMAT_T140_RED: + return ast_format_t140; + /*! T.140 Text format - ITU T.140, RFC 4103 */ + case AST_FORMAT_T140: + return ast_format_t140_red; + } + return 0; +} + diff --git a/main/format_pref.c b/main/format_pref.c deleted file mode 100644 index b96184a08..000000000 --- a/main/format_pref.c +++ /dev/null @@ -1,380 +0,0 @@ -/* - * 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 - */ - -/*** MODULEINFO - <support_level>core</support_level> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); - -#include "asterisk/_private.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_format_list_get(&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_copy(&pref->formats[x], &f_list[to[x]-1].format); - } - } - ast_format_list_destroy(f_list); -} - -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; - const 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; - - if (!pref->order[0]) { - return; - } - - f_list = ast_format_list_get(&f_len); - memcpy(&oldorder, pref, sizeof(oldorder)); - memset(pref, 0, sizeof(*pref)); - - for (x = 0; x < f_len; x++) { - if (!oldorder.order[x]) { - break; - } - if (ast_format_cmp(&f_list[oldorder.order[x]-1].format, format) == AST_FORMAT_CMP_NOT_EQUAL) { - pref->order[y] = oldorder.order[x]; - ast_format_copy(&pref->formats[y], &oldorder.formats[x]); - pref->framing[y++] = oldorder.framing[x]; - } - } - ast_format_list_destroy(f_list); -} - -/*! \brief Append all codecs to a preference list, without distrubing existing order */ -void ast_codec_pref_append_all(struct ast_codec_pref *pref) -{ - int x, y, found; - size_t f_len = 0; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); - - /* leave any existing entries, and don't create duplicates (e.g. allow=ulaw,all) */ - for (x = 0; x < f_len; x++) { - /* x = codec to add */ - found = 0; - for (y = 0; y < f_len; y++) { - /* y = scan of existing preferences */ - if (!pref->order[y]) { - break; - } - if (x + 1 == pref->order[y]) { - found = 1; - break; - } - } - if (found) { - continue; - } - for (; y < f_len; y++) { - /* add x to the end of y */ - if (!pref->order[y]) - { - pref->order[y] = x + 1; - ast_format_copy(&pref->formats[y], &f_list[x].format); - break; - } - } - } -} - -/*! \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_format_list_get(&f_len); - - ast_codec_pref_remove(pref, format); - - for (x = 0; x < f_len; x++) { - if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { - 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; - } - } - } - - ast_format_list_destroy(f_list); - 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_format_list_get(&f_len); - - /* First step is to get the codecs "index number" */ - for (x = 0; x < f_len; x++) { - if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { - newindex = x + 1; - break; - } - } - /* Done if its unknown */ - if (!newindex) { - ast_format_list_destroy(f_list); - 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 we failed to find any occurrence, set to the end */ - if (x == AST_CODEC_PREF_SIZE) { - --x; - } - - if (only_if_existing && !pref->order[x]) { - ast_format_list_destroy(f_list); - 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); - ast_format_list_destroy(f_list); -} - -/*! \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_format_list_get(&f_len); - - for (x = 0; x < f_len; x++) { - if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { - idx = x; - break; - } - } - - if (idx < 0) { - ast_format_list_destroy(f_list); - 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; - } - } - - ast_format_list_destroy(f_list); - 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_format_list_get(&f_len); - - for (x = 0; x < f_len; x++) { - if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { - fmt = f_list[x]; - idx = x; - break; - } - } - - if (idx < 0) { - ast_log(AST_LOG_WARNING, "Format %s unknown; unable to get preferred codec packet size\n", ast_getformatname(format)); - ast_format_list_destroy(f_list); - return fmt; - } - - 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; - ast_format_list_destroy(f_list); - 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 = 0; - size_t f_len = 0; - const struct ast_format_list *f_list = ast_format_list_get(&f_len); - - for (x = 0; x < f_len; x++) { - slot = pref->order[x]; - - if (!slot) - break; - if (ast_format_cap_get_compatible_format(cap, &f_list[slot-1].format, result)) { - found = 1; /*format is found and stored in result */ - break; - } - } - ast_format_list_destroy(f_list); - if (found && (AST_FORMAT_GET_TYPE(result->id) == AST_FORMAT_TYPE_AUDIO)) { - return result; - } - ast_format_clear(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 76ea3f66c..64af88237 100644 --- a/main/frame.c +++ b/main/frame.c @@ -34,6 +34,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/_private.h" #include "asterisk/lock.h" #include "asterisk/frame.h" +#include "asterisk/format_cache.h" #include "asterisk/channel.h" #include "asterisk/cli.h" #include "asterisk/term.h" @@ -71,199 +72,8 @@ struct ast_frame_cache { }; #endif -#define SMOOTHER_SIZE 8000 - -enum frame_type { - TYPE_HIGH, /* 0x0 */ - TYPE_LOW, /* 0x1 */ - TYPE_SILENCE, /* 0x2 */ - TYPE_DONTSEND /* 0x3 */ -}; - -#define TYPE_MASK 0x3 - -struct ast_smoother { - int size; - struct ast_format format; - int flags; - float samplesperbyte; - unsigned int opt_needs_swap:1; - struct ast_frame f; - struct timeval delivery; - char data[SMOOTHER_SIZE]; - char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET]; - struct ast_frame *opt; - int len; -}; - struct ast_frame ast_null_frame = { AST_FRAME_NULL, }; -static int smoother_frame_feed(struct ast_smoother *s, struct ast_frame *f, int swap) -{ - if (s->flags & AST_SMOOTHER_FLAG_G729) { - if (s->len % 10) { - ast_log(LOG_NOTICE, "Dropping extra frame of G.729 since we already have a VAD frame at the end\n"); - return 0; - } - } - if (swap) { - ast_swapcopy_samples(s->data + s->len, f->data.ptr, f->samples); - } else { - memcpy(s->data + s->len, f->data.ptr, f->datalen); - } - /* If either side is empty, reset the delivery time */ - if (!s->len || ast_tvzero(f->delivery) || ast_tvzero(s->delivery)) { /* XXX really ? */ - s->delivery = f->delivery; - } - s->len += f->datalen; - - return 0; -} - -void ast_smoother_reset(struct ast_smoother *s, int bytes) -{ - memset(s, 0, sizeof(*s)); - s->size = bytes; -} - -void ast_smoother_reconfigure(struct ast_smoother *s, int bytes) -{ - /* if there is no change, then nothing to do */ - if (s->size == bytes) { - return; - } - /* set the new desired output size */ - s->size = bytes; - /* if there is no 'optimized' frame in the smoother, - * then there is nothing left to do - */ - if (!s->opt) { - return; - } - /* there is an 'optimized' frame here at the old size, - * but it must now be put into the buffer so the data - * can be extracted at the new size - */ - smoother_frame_feed(s, s->opt, s->opt_needs_swap); - s->opt = NULL; -} - -struct ast_smoother *ast_smoother_new(int size) -{ - struct ast_smoother *s; - if (size < 1) - return NULL; - if ((s = ast_malloc(sizeof(*s)))) - ast_smoother_reset(s, size); - return s; -} - -int ast_smoother_get_flags(struct ast_smoother *s) -{ - return s->flags; -} - -void ast_smoother_set_flags(struct ast_smoother *s, int flags) -{ - s->flags = flags; -} - -int ast_smoother_test_flag(struct ast_smoother *s, int flag) -{ - return (s->flags & flag); -} - -int __ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f, int swap) -{ - if (f->frametype != AST_FRAME_VOICE) { - ast_log(LOG_WARNING, "Huh? Can't smooth a non-voice frame!\n"); - return -1; - } - if (!s->format.id) { - ast_format_copy(&s->format, &f->subclass.format); - s->samplesperbyte = (float)f->samples / (float)f->datalen; - } 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.format)); - return -1; - } - if (s->len + f->datalen > SMOOTHER_SIZE) { - ast_log(LOG_WARNING, "Out of smoother space\n"); - return -1; - } - if (((f->datalen == s->size) || - ((f->datalen < 10) && (s->flags & AST_SMOOTHER_FLAG_G729))) && - !s->opt && - !s->len && - (f->offset >= AST_MIN_OFFSET)) { - /* Optimize by sending the frame we just got - on the next read, thus eliminating the douple - copy */ - if (swap) - ast_swapcopy_samples(f->data.ptr, f->data.ptr, f->samples); - s->opt = f; - s->opt_needs_swap = swap ? 1 : 0; - return 0; - } - - return smoother_frame_feed(s, f, swap); -} - -struct ast_frame *ast_smoother_read(struct ast_smoother *s) -{ - struct ast_frame *opt; - int len; - - /* IF we have an optimization frame, send it */ - if (s->opt) { - if (s->opt->offset < AST_FRIENDLY_OFFSET) - ast_log(LOG_WARNING, "Returning a frame of inappropriate offset (%d).\n", - s->opt->offset); - opt = s->opt; - s->opt = NULL; - return opt; - } - - /* Make sure we have enough data */ - if (s->len < s->size) { - /* Or, if this is a G.729 frame with VAD on it, send it immediately anyway */ - if (!((s->flags & AST_SMOOTHER_FLAG_G729) && (s->len % 10))) - return NULL; - } - len = s->size; - if (len > s->len) - len = s->len; - /* Make frame */ - s->f.frametype = AST_FRAME_VOICE; - 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; - /* Samples will be improper given VAD, but with VAD the concept really doesn't even exist */ - s->f.samples = len * s->samplesperbyte; /* XXX rounding */ - s->f.delivery = s->delivery; - /* Fill Data */ - memcpy(s->f.data.ptr, s->data, len); - s->len -= len; - /* Move remaining data to the front if applicable */ - if (s->len) { - /* In principle this should all be fine because if we are sending - G.729 VAD, the next timestamp will take over anyawy */ - 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))); - } - } - /* Return frame */ - return &s->f; -} - -void ast_smoother_free(struct ast_smoother *s) -{ - ast_free(s); -} - static struct ast_frame *ast_frame_header_new(void) { struct ast_frame *f; @@ -316,9 +126,13 @@ static void __frame_free(struct ast_frame *fr, int cache) /* Cool, only the header is malloc'd, let's just cache those for now * to keep things simple... */ struct ast_frame_cache *frames; - if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames))) && (frames->size < FRAME_CACHE_MAX_SIZE)) { + if ((fr->frametype == AST_FRAME_VOICE) || (fr->frametype == AST_FRAME_VIDEO) || + (fr->frametype == AST_FRAME_IMAGE)) { + ao2_cleanup(fr->subclass.format); + } + AST_LIST_INSERT_HEAD(&frames->list, fr, frame_list); frames->size++; return; @@ -335,7 +149,14 @@ static void __frame_free(struct ast_frame *fr, int cache) ast_free((void *) fr->src); } if (fr->mallocd & AST_MALLOCD_HDR) { + if ((fr->frametype == AST_FRAME_VOICE) || (fr->frametype == AST_FRAME_VIDEO) || + (fr->frametype == AST_FRAME_IMAGE)) { + ao2_cleanup(fr->subclass.format); + } + ast_free(fr); + } else { + fr->mallocd = 0; } } @@ -387,7 +208,12 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr) return NULL; } out->frametype = fr->frametype; - ast_format_copy(&out->subclass.format, &fr->subclass.format); + if ((fr->frametype == AST_FRAME_VOICE) || (fr->frametype == AST_FRAME_VIDEO) || + (fr->frametype == AST_FRAME_IMAGE)) { + out->subclass.format = ao2_bump(fr->subclass.format); + } else { + memcpy(&out->subclass, &fr->subclass, sizeof(out->subclass)); + } out->datalen = fr->datalen; out->samples = fr->samples; out->offset = fr->offset; @@ -494,7 +320,12 @@ struct ast_frame *ast_frdup(const struct ast_frame *f) } out->frametype = f->frametype; - ast_format_copy(&out->subclass.format, &f->subclass.format); + if ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_VIDEO) || + (f->frametype == AST_FRAME_IMAGE)) { + out->subclass.format = ao2_bump(f->subclass.format); + } else { + memcpy(&out->subclass, &f->subclass, sizeof(out->subclass)); + } out->datalen = f->datalen; out->samples = f->samples; out->delivery = f->delivery; @@ -650,7 +481,7 @@ void ast_frame_subclass2str(struct ast_frame *f, char *subclass, size_t slen, ch } break; case AST_FRAME_IMAGE: - snprintf(subclass, slen, "Image format %s\n", ast_getformatname(&f->subclass.format)); + snprintf(subclass, slen, "Image format %s\n", ast_format_get_name(f->subclass.format)); break; case AST_FRAME_HTML: switch (f->subclass.integer) { @@ -816,369 +647,13 @@ void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix) term_color(cn, name, COLOR_YELLOW, COLOR_BLACK, sizeof(cn))); } -int ast_parse_allow_disallow(struct ast_codec_pref *pref, struct ast_format_cap *cap, const char *list, int allowing) -{ - int errors = 0, framems = 0, all = 0, iter_allowing; - char *parse = NULL, *this = NULL, *psize = NULL; - struct ast_format format; - - parse = ast_strdupa(list); - while ((this = strsep(&parse, ","))) { - iter_allowing = allowing; - framems = 0; - if (*this == '!') { - this++; - iter_allowing = !allowing; - } - if ((psize = strrchr(this, ':'))) { - *psize++ = '\0'; - ast_debug(1, "Packetization for codec: %s is %s\n", this, psize); - framems = atoi(psize); - if (framems < 0) { - framems = 0; - errors++; - ast_log(LOG_WARNING, "Bad packetization value for codec %s\n", this); - } - } - all = strcasecmp(this, "all") ? 0 : 1; - - if (!all && !ast_getformatbyname(this, &format)) { - ast_log(LOG_WARNING, "Cannot %s unknown format '%s'\n", iter_allowing ? "allow" : "disallow", this); - errors++; - continue; - } - - if (cap) { - if (iter_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); - } - } - } - - if (pref) { - if (!all) { - if (iter_allowing) { - ast_codec_pref_append(pref, &format); - ast_codec_pref_setsize(pref, &format, framems); - } else { - ast_codec_pref_remove(pref, &format); - } - } else if (!iter_allowing) { - memset(pref, 0, sizeof(*pref)); - } else { - ast_codec_pref_append_all(pref); - } - } - } - return errors; -} - -static int g723_len(unsigned char buf) -{ - enum frame_type type = buf & TYPE_MASK; - - switch(type) { - case TYPE_DONTSEND: - return 0; - break; - case TYPE_SILENCE: - return 4; - break; - case TYPE_HIGH: - return 24; - break; - case TYPE_LOW: - return 20; - break; - default: - ast_log(LOG_WARNING, "Badly encoded frame (%u)\n", type); - } - return -1; -} - -static int g723_samples(unsigned char *buf, int maxlen) -{ - int pos = 0; - int samples = 0; - int res; - while(pos < maxlen) { - res = g723_len(buf[pos]); - if (res <= 0) - break; - samples += 240; - pos += res; - } - return samples; -} - -static unsigned char get_n_bits_at(unsigned char *data, int n, int bit) -{ - int byte = bit / 8; /* byte containing first bit */ - int rem = 8 - (bit % 8); /* remaining bits in first byte */ - unsigned char ret = 0; - - if (n <= 0 || n > 8) - return 0; - - if (rem < n) { - ret = (data[byte] << (n - rem)); - ret |= (data[byte + 1] >> (8 - n + rem)); - } else { - ret = (data[byte] >> (rem - n)); - } - - return (ret & (0xff >> (8 - n))); -} - -static int speex_get_wb_sz_at(unsigned char *data, int len, int bit) -{ - static const int SpeexWBSubModeSz[] = { - 4, 36, 112, 192, - 352, 0, 0, 0 }; - int off = bit; - unsigned char c; - - /* skip up to two wideband frames */ - if (((len * 8 - off) >= 5) && - get_n_bits_at(data, 1, off)) { - c = get_n_bits_at(data, 3, off + 1); - off += SpeexWBSubModeSz[c]; - - if (((len * 8 - off) >= 5) && - get_n_bits_at(data, 1, off)) { - c = get_n_bits_at(data, 3, off + 1); - off += SpeexWBSubModeSz[c]; - - if (((len * 8 - off) >= 5) && - get_n_bits_at(data, 1, off)) { - ast_log(LOG_WARNING, "Encountered corrupt speex frame; too many wideband frames in a row.\n"); - return -1; - } - } - - } - return off - bit; -} - -static int speex_samples(unsigned char *data, int len) -{ - static const int SpeexSubModeSz[] = { - 5, 43, 119, 160, - 220, 300, 364, 492, - 79, 0, 0, 0, - 0, 0, 0, 0 }; - static const int SpeexInBandSz[] = { - 1, 1, 4, 4, - 4, 4, 4, 4, - 8, 8, 16, 16, - 32, 32, 64, 64 }; - int bit = 0; - int cnt = 0; - int off; - unsigned char c; - - while ((len * 8 - bit) >= 5) { - /* skip wideband frames */ - off = speex_get_wb_sz_at(data, len, bit); - if (off < 0) { - ast_log(LOG_WARNING, "Had error while reading wideband frames for speex samples\n"); - break; - } - bit += off; - - if ((len * 8 - bit) < 5) - break; - - /* get control bits */ - c = get_n_bits_at(data, 5, bit); - bit += 5; - - if (c == 15) { - /* terminator */ - break; - } else if (c == 14) { - /* in-band signal; next 4 bits contain signal id */ - c = get_n_bits_at(data, 4, bit); - bit += 4; - bit += SpeexInBandSz[c]; - } else if (c == 13) { - /* user in-band; next 4 bits contain msg len */ - c = get_n_bits_at(data, 4, bit); - bit += 4; - /* after which it's 5-bit signal id + c bytes of data */ - bit += 5 + c * 8; - } else if (c > 8) { - /* unknown */ - ast_log(LOG_WARNING, "Unknown speex control frame %d\n", c); - break; - } else { - /* skip number bits for submode (less the 5 control bits) */ - bit += SpeexSubModeSz[c] - 5; - cnt += 160; /* new frame */ - } - } - return cnt; -} - -int ast_codec_get_samples(struct ast_frame *f) -{ - int samples = 0; - - switch (f->subclass.format.id) { - case AST_FORMAT_SPEEX: - samples = speex_samples(f->data.ptr, f->datalen); - break; - case AST_FORMAT_SPEEX16: - samples = 2 * speex_samples(f->data.ptr, f->datalen); - break; - case AST_FORMAT_SPEEX32: - samples = 4 * speex_samples(f->data.ptr, f->datalen); - break; - case AST_FORMAT_G723_1: - samples = g723_samples(f->data.ptr, f->datalen); - break; - case AST_FORMAT_ILBC: - samples = 240 * (f->datalen / 50); - break; - case AST_FORMAT_GSM: - samples = 160 * (f->datalen / 33); - break; - case AST_FORMAT_G729A: - samples = f->datalen * 8; - break; - case AST_FORMAT_SLINEAR: - case AST_FORMAT_SLINEAR16: - samples = f->datalen / 2; - break; - case AST_FORMAT_LPC10: - /* assumes that the RTP packet contains one LPC10 frame */ - samples = 22 * 8; - samples += (((char *)(f->data.ptr))[7] & 0x1) * 8; - break; - case AST_FORMAT_ULAW: - case AST_FORMAT_ALAW: - case AST_FORMAT_TESTLAW: - samples = f->datalen; - break; - case AST_FORMAT_G722: - case AST_FORMAT_ADPCM: - case AST_FORMAT_G726: - case AST_FORMAT_G726_AAL2: - samples = f->datalen * 2; - break; - case AST_FORMAT_SIREN7: - /* 16,000 samples per second at 32kbps is 4,000 bytes per second */ - samples = f->datalen * (16000 / 4000); - break; - case AST_FORMAT_SIREN14: - /* 32,000 samples per second at 48kbps is 6,000 bytes per second */ - samples = (int) f->datalen * ((float) 32000 / 6000); - break; - case AST_FORMAT_G719: - /* 48,000 samples per second at 64kbps is 8,000 bytes per second */ - samples = (int) f->datalen * ((float) 48000 / 8000); - break; - case AST_FORMAT_SILK: - if (!(ast_format_isset(&f->subclass.format, - SILK_ATTR_KEY_SAMP_RATE, - SILK_ATTR_VAL_SAMP_24KHZ, - AST_FORMAT_ATTR_END))) { - return 480; - } else if (!(ast_format_isset(&f->subclass.format, - SILK_ATTR_KEY_SAMP_RATE, - SILK_ATTR_VAL_SAMP_16KHZ, - AST_FORMAT_ATTR_END))) { - return 320; - } else if (!(ast_format_isset(&f->subclass.format, - SILK_ATTR_KEY_SAMP_RATE, - SILK_ATTR_VAL_SAMP_12KHZ, - AST_FORMAT_ATTR_END))) { - return 240; - } else { - return 160; - } - case AST_FORMAT_CELT: - /* TODO This assumes 20ms delivery right now, which is incorrect */ - samples = ast_format_rate(&f->subclass.format) / 50; - break; - case AST_FORMAT_OPUS: - /* TODO This assumes 20ms delivery right now, which is incorrect */ - samples = 960; - break; - default: - ast_log(LOG_WARNING, "Unable to calculate samples for format %s\n", ast_getformatname(&f->subclass.format)); - } - return samples; -} - -int ast_codec_get_len(struct ast_format *format, int samples) -{ - int len = 0; - - /* XXX Still need speex, and lpc10 XXX */ - switch(format->id) { - case AST_FORMAT_G723_1: - len = (samples / 240) * 20; - break; - case AST_FORMAT_ILBC: - len = (samples / 240) * 50; - break; - case AST_FORMAT_GSM: - len = (samples / 160) * 33; - break; - case AST_FORMAT_G729A: - len = samples / 8; - break; - case AST_FORMAT_SLINEAR: - case AST_FORMAT_SLINEAR16: - len = samples * 2; - break; - case AST_FORMAT_ULAW: - case AST_FORMAT_ALAW: - case AST_FORMAT_TESTLAW: - len = samples; - break; - case AST_FORMAT_G722: - case AST_FORMAT_ADPCM: - case AST_FORMAT_G726: - case AST_FORMAT_G726_AAL2: - len = samples / 2; - break; - case AST_FORMAT_SIREN7: - /* 16,000 samples per second at 32kbps is 4,000 bytes per second */ - len = samples / (16000 / 4000); - break; - case AST_FORMAT_SIREN14: - /* 32,000 samples per second at 48kbps is 6,000 bytes per second */ - len = (int) samples / ((float) 32000 / 6000); - break; - case AST_FORMAT_G719: - /* 48,000 samples per second at 64kbps is 8,000 bytes per second */ - len = (int) samples / ((float) 48000 / 8000); - break; - default: - ast_log(LOG_WARNING, "Unable to calculate sample length for format %s\n", ast_getformatname(format)); - } - - return len; -} - int ast_frame_adjust_volume(struct ast_frame *f, int adjustment) { int count; short *fdata = f->data.ptr; short adjust_value = abs(adjustment); - if ((f->frametype != AST_FRAME_VOICE) || !(ast_format_is_slinear(&f->subclass.format))) { + if ((f->frametype != AST_FRAME_VOICE) || !(ast_format_cache_is_slinear(f->subclass.format))) { return -1; } @@ -1202,10 +677,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.format.id != AST_FORMAT_SLINEAR)) + if ((f1->frametype != AST_FRAME_VOICE) || (ast_format_cmp(f1->subclass.format, ast_format_slin) != AST_FORMAT_CMP_NOT_EQUAL)) return -1; - if ((f2->frametype != AST_FRAME_VOICE) || (f2->subclass.format.id != AST_FORMAT_SLINEAR)) + if ((f2->frametype != AST_FRAME_VOICE) || (ast_format_cmp(f2->subclass.format, ast_format_slin) != AST_FORMAT_CMP_NOT_EQUAL)) return -1; if (f1->samples != f2->samples) diff --git a/main/image.c b/main/image.c index 08620d734..53638f30c 100644 --- a/main/image.c +++ b/main/image.c @@ -114,7 +114,7 @@ struct ast_frame *ast_read_image(const char *filename, const char *preflang, str AST_RWLIST_RDLOCK(&imagers); AST_RWLIST_TRAVERSE(&imagers, i, list) { /* if NULL image format, just pick the first one, otherwise match it. */ - if (!format || (ast_format_cmp(&i->format, format) == AST_FORMAT_CMP_EQUAL)) { + 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; @@ -194,7 +194,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_format_get_name(i->format)); count_fmt++; } AST_RWLIST_UNLOCK(&imagers); diff --git a/main/indications.c b/main/indications.c index 2f2bdce62..27d2b5356 100644 --- a/main/indications.c +++ b/main/indications.c @@ -37,6 +37,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/linkedlists.h" #include "asterisk/indications.h" #include "asterisk/frame.h" +#include "asterisk/format_cache.h" #include "asterisk/channel.h" #include "asterisk/utils.h" #include "asterisk/cli.h" @@ -120,7 +121,7 @@ struct playtones_state { int npos; int oldnpos; int pos; - struct ast_format origwfmt; + struct ast_format *origwfmt; struct ast_frame f; unsigned char offset[AST_FRIENDLY_OFFSET]; short data[4000]; @@ -131,13 +132,11 @@ 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) { - ast_free(ps->items); - ps->items = NULL; - } + ao2_cleanup(ps->origwfmt); + ast_free(ps->items); ast_free(ps); } @@ -151,9 +150,9 @@ static void *playtones_alloc(struct ast_channel *chan, void *params) return NULL; } - ast_format_copy(&ps->origwfmt, ast_channel_writeformat(chan)); + ps->origwfmt = ao2_bump(ast_channel_writeformat(chan)); - if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR)) { + if (ast_set_write_format(chan, ast_format_slin)) { ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", ast_channel_name(chan)); playtones_release(NULL, ps); ps = NULL; @@ -227,7 +226,7 @@ static int playtones_generator(struct ast_channel *chan, void *data, int len, in } ps->f.frametype = AST_FRAME_VOICE; - ast_format_set(&ps->f.subclass.format, AST_FORMAT_SLINEAR, 0); + ps->f.subclass.format = ast_format_slin; 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 75811886f..35ba5d46c 100644 --- a/main/manager.c +++ b/main/manager.c @@ -99,6 +99,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/bridge.h" #include "asterisk/features_config.h" #include "asterisk/rtp_engine.h" +#include "asterisk/format_cache.h" #include "asterisk/translate.h" /*** DOCUMENTATION @@ -3869,7 +3870,7 @@ static int action_status(struct mansession *s, const struct message *m) struct ast_str *write_transpath = ast_str_alloca(256); struct ast_str *read_transpath = ast_str_alloca(256); struct ast_channel *chan; - char nativeformats[256]; + struct ast_str *codec_buf = ast_str_alloca(64); int channels = 0; int all = ast_strlen_zero(name); /* set if we want all channels */ char id_text[256]; @@ -4007,10 +4008,10 @@ static int action_status(struct mansession *s, const struct message *m) ast_channel_linkedid(chan), ast_channel_appl(chan), ast_channel_data(chan), - ast_getformatname_multiple(nativeformats, sizeof(nativeformats), ast_channel_nativeformats(chan)), - ast_getformatname(ast_channel_readformat(chan)), + ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf), + ast_format_get_name(ast_channel_readformat(chan)), ast_translate_path_to_str(ast_channel_readtrans(chan), &read_transpath), - ast_getformatname(ast_channel_writeformat(chan)), + ast_format_get_name(ast_channel_writeformat(chan)), ast_translate_path_to_str(ast_channel_writetrans(chan), &write_transpath), ast_channel_callgroup(chan), ast_channel_pickupgroup(chan), @@ -4458,7 +4459,7 @@ struct fast_originate_helper { */ static void destroy_fast_originate_helper(struct fast_originate_helper *doomed) { - ast_format_cap_destroy(doomed->cap); + ao2_cleanup(doomed->cap); ast_variables_destroy(doomed->vars); ast_string_field_free_memory(doomed); ast_free(doomed); @@ -4781,8 +4782,7 @@ static int action_originate(struct mansession *s, const struct message *m) int reason = 0; char tmp[256]; char tmp2[256]; - struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK); - struct ast_format tmp_fmt; + struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); pthread_t th; int bridge_early = 0; @@ -4790,7 +4790,7 @@ static int action_originate(struct mansession *s, const struct message *m) astman_send_error(s, m, "Internal Error. Memory allocation failure."); return 0; } - ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0)); + ast_format_cap_append(cap, ast_format_slin, 0); if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid)) || (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) { @@ -4840,8 +4840,8 @@ static int action_originate(struct mansession *s, const struct message *m) } } if (!ast_strlen_zero(codecs)) { - ast_format_cap_remove_all(cap); - ast_parse_allow_disallow(NULL, cap, codecs, 1); + ast_format_cap_remove_by_type(cap, AST_MEDIA_TYPE_UNKNOWN); + ast_format_cap_update_by_allow_disallow(cap, codecs, 1); } if (!ast_strlen_zero(app) && s->session) { @@ -4957,7 +4957,7 @@ static int action_originate(struct mansession *s, const struct message *m) } fast_orig_cleanup: - ast_format_cap_destroy(cap); + ao2_cleanup(cap); return 0; } diff --git a/main/media_index.c b/main/media_index.c index a643c237f..3e3824580 100644 --- a/main/media_index.c +++ b/main/media_index.c @@ -56,7 +56,7 @@ static void media_variant_destroy(void *obj) struct media_variant *variant = obj; ast_string_field_free_memory(variant); - variant->formats = ast_format_cap_destroy(variant->formats); + ao2_cleanup(variant->formats); } static struct media_variant *media_variant_alloc(const char *variant_str) @@ -67,7 +67,7 @@ static struct media_variant *media_variant_alloc(const char *variant_str) return NULL; } - variant->formats = ast_format_cap_alloc(0); + variant->formats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); if (!variant->formats) { return NULL; } @@ -239,6 +239,7 @@ const char *ast_media_get_description(struct ast_media_index *index, const char struct ast_format_cap *ast_media_get_format_cap(struct ast_media_index *index, const char *filename, const char *variant_str) { + struct ast_format_cap *dupcap; RAII_VAR(struct media_variant *, variant, NULL, ao2_cleanup); if (ast_strlen_zero(filename) || ast_strlen_zero(variant_str)) { return NULL; @@ -249,7 +250,11 @@ struct ast_format_cap *ast_media_get_format_cap(struct ast_media_index *index, c return NULL; } - return ast_format_cap_dup(variant->formats); + dupcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (dupcap) { + ast_format_cap_append_from_cap(dupcap, variant->formats, AST_MEDIA_TYPE_UNKNOWN); + } + return dupcap; } /*! \brief Add the variant to the list of variants requested */ @@ -317,7 +322,7 @@ struct ao2_container *ast_media_get_media(struct ast_media_index *index) } /*! \brief Update an index with new format/variant information */ -static int update_file_format_info(struct ast_media_index *index, const char *filename, const char *variant_str, const struct ast_format *file_format) +static int update_file_format_info(struct ast_media_index *index, const char *filename, const char *variant_str, struct ast_format *file_format) { RAII_VAR(struct media_variant *, variant, find_variant(index, filename, variant_str), ao2_cleanup); if (!variant) { @@ -327,14 +332,14 @@ static int update_file_format_info(struct ast_media_index *index, const char *fi } } - ast_format_cap_add(variant->formats, file_format); + ast_format_cap_append(variant->formats, file_format, 0); return 0; } /*! \brief Process a media file into the index */ static int process_media_file(struct ast_media_index *index, const char *variant, const char *subdir, const char *filename_stripped, const char *ext) { - const struct ast_format *file_format; + struct ast_format *file_format; const char *file_identifier = filename_stripped; RAII_VAR(struct ast_str *, file_id_str, NULL, ast_free); diff --git a/main/rtp_engine.c b/main/rtp_engine.c index 9e3d7d108..07ef1f697 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -196,12 +196,18 @@ static AST_RWLIST_HEAD_STATIC(engines, ast_rtp_engine); /*! List of RTP glues */ static AST_RWLIST_HEAD_STATIC(glues, ast_rtp_glue); +#define MAX_RTP_MIME_TYPES 128 + /*! The following array defines the MIME Media type (and subtype) for each of our codecs, or RTP-specific data type. */ static struct ast_rtp_mime_type { + /*! \brief A mapping object between the Asterisk codec and this RTP payload */ struct ast_rtp_payload_type payload_type; - char *type; - char *subtype; + /*! \brief The media type */ + char type[16]; + /*! \brief The format type */ + char subtype[64]; + /*! \brief Expected sample rate of the /c subtype */ unsigned int sample_rate; } ast_rtp_mime_types[128]; /* This will Likely not need to grow any time soon. */ static ast_rwlock_t mime_types_lock; @@ -223,6 +229,24 @@ static ast_rwlock_t static_RTP_PT_lock; /*! \brief \ref stasis topic for RTP related messages */ static struct stasis_topic *rtp_topic; + +/*! \internal \brief Destructor for \c ast_rtp_payload_type */ +static void rtp_payload_type_dtor(void *obj) +{ + struct ast_rtp_payload_type *payload = obj; + + ao2_cleanup(payload->format); +} + +struct ast_rtp_payload_type *ast_rtp_engine_alloc_payload_type(void) +{ + struct ast_rtp_payload_type *payload; + + payload = ao2_alloc(sizeof(*payload), rtp_payload_type_dtor); + + return payload; +} + int ast_rtp_engine_register2(struct ast_rtp_engine *engine, struct ast_module *module) { struct ast_rtp_engine *current_engine; @@ -526,34 +550,30 @@ struct ast_rtp_codecs *ast_rtp_instance_get_codecs(struct ast_rtp_instance *inst return &instance->codecs; } -static int rtp_payload_type_hash(const void *obj, const int flags) +int ast_rtp_codecs_payloads_initialize(struct ast_rtp_codecs *codecs) { - const struct ast_rtp_payload_type *type = obj; - const int *payload = obj; + int res; + + codecs->framing = 0; + ast_rwlock_init(&codecs->codecs_lock); + res = AST_VECTOR_INIT(&codecs->payloads, AST_RTP_MAX_PT); - return (flags & OBJ_KEY) ? *payload : type->payload; + return res; } -static int rtp_payload_type_cmp(void *obj, void *arg, int flags) +void ast_rtp_codecs_payloads_destroy(struct ast_rtp_codecs *codecs) { - struct ast_rtp_payload_type *type1 = obj, *type2 = arg; - const int *payload = arg; + int i; - return (type1->payload == (OBJ_KEY ? *payload : type2->payload)) ? CMP_MATCH | CMP_STOP : 0; -} + for (i = 0; i < AST_VECTOR_SIZE(&codecs->payloads); i++) { + struct ast_rtp_payload_type *type; -int ast_rtp_codecs_payloads_initialize(struct ast_rtp_codecs *codecs) -{ - if (!(codecs->payloads = ao2_container_alloc(AST_RTP_MAX_PT, rtp_payload_type_hash, rtp_payload_type_cmp))) { - return -1; + type = AST_VECTOR_GET(&codecs->payloads, i); + ao2_t_cleanup(type, "destroying ast_rtp_codec"); } + AST_VECTOR_FREE(&codecs->payloads); - return 0; -} - -void ast_rtp_codecs_payloads_destroy(struct ast_rtp_codecs *codecs) -{ - ao2_cleanup(codecs->payloads); + ast_rwlock_destroy(&codecs->codecs_lock); } void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance) @@ -570,106 +590,69 @@ void ast_rtp_codecs_payloads_clear(struct ast_rtp_codecs *codecs, struct ast_rtp ast_rtp_codecs_payloads_initialize(codecs); } -void ast_rtp_codecs_payloads_default(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance) -{ - int i; - - ast_rwlock_rdlock(&static_RTP_PT_lock); - for (i = 0; i < AST_RTP_MAX_PT; i++) { - if (static_RTP_PT[i].rtp_code || static_RTP_PT[i].asterisk_format) { - struct ast_rtp_payload_type *type; - - if (!(type = ao2_alloc(sizeof(*type), NULL))) { - /* Unfortunately if this occurs the payloads container will not contain all possible default payloads - * but we err on the side of doing what we can in the hopes that the extreme memory conditions which - * caused this to occur will go away. - */ - continue; - } - - type->payload = i; - type->asterisk_format = static_RTP_PT[i].asterisk_format; - type->rtp_code = static_RTP_PT[i].rtp_code; - ast_format_copy(&type->format, &static_RTP_PT[i].format); - - ao2_link_flags(codecs->payloads, type, OBJ_NOLOCK); - - if (instance && instance->engine && instance->engine->payload_set) { - instance->engine->payload_set(instance, i, type->asterisk_format, &type->format, type->rtp_code); - } - - ao2_ref(type, -1); - } - } - ast_rwlock_unlock(&static_RTP_PT_lock); -} - void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance) { int i; - struct ast_rtp_payload_type *type; - for (i = 0; i < AST_RTP_MAX_PT; i++) { - struct ast_rtp_payload_type *new_type; + ast_rwlock_rdlock(&src->codecs_lock); + ast_rwlock_wrlock(&dest->codecs_lock); - if (!(type = ao2_find(src->payloads, &i, OBJ_KEY | OBJ_NOLOCK))) { - continue; - } + for (i = 0; i < AST_VECTOR_SIZE(&src->payloads); i++) { + struct ast_rtp_payload_type *type; - if (!(new_type = ao2_alloc(sizeof(*new_type), NULL))) { + type = AST_VECTOR_GET(&src->payloads, i); + if (!type) { continue; } - - ast_debug(2, "Copying payload %d from %p to %p\n", i, src, dest); - - new_type->payload = i; - *new_type = *type; - - ao2_link_flags(dest->payloads, new_type, OBJ_NOLOCK); - - ao2_ref(new_type, -1); + if (i < AST_VECTOR_SIZE(&dest->payloads)) { + ao2_t_cleanup(AST_VECTOR_GET(&dest->payloads, i), "cleaning up vector element about to be replaced"); + } + ast_debug(2, "Copying payload %d (%p) from %p to %p\n", i, type, src, dest); + ao2_bump(type); + AST_VECTOR_INSERT(&dest->payloads, i, type); if (instance && instance->engine && instance->engine->payload_set) { - instance->engine->payload_set(instance, i, type->asterisk_format, &type->format, type->rtp_code); + instance->engine->payload_set(instance, i, type->asterisk_format, type->format, type->rtp_code); } - - ao2_ref(type, -1); } + dest->framing = src->framing; + ast_rwlock_unlock(&dest->codecs_lock); + ast_rwlock_unlock(&src->codecs_lock); } void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload) { - struct ast_rtp_payload_type *type; + struct ast_rtp_payload_type *new_type; - ast_rwlock_rdlock(&static_RTP_PT_lock); + new_type = ast_rtp_engine_alloc_payload_type(); + if (!new_type) { + return; + } + ast_rwlock_rdlock(&static_RTP_PT_lock); if (payload < 0 || payload >= AST_RTP_MAX_PT) { ast_rwlock_unlock(&static_RTP_PT_lock); return; } - if (!(type = ao2_find(codecs->payloads, &payload, OBJ_KEY | OBJ_NOLOCK))) { - if (!(type = ao2_alloc(sizeof(*type), NULL))) { - ast_rwlock_unlock(&static_RTP_PT_lock); - return; - } - type->payload = payload; - ao2_link_flags(codecs->payloads, type, OBJ_NOLOCK); + ast_rwlock_wrlock(&codecs->codecs_lock); + if (payload < AST_VECTOR_SIZE(&codecs->payloads)) { + ao2_t_cleanup(AST_VECTOR_GET(&codecs->payloads, payload), "cleaning up replaced payload type"); } - type->asterisk_format = static_RTP_PT[payload].asterisk_format; - type->rtp_code = static_RTP_PT[payload].rtp_code; - type->payload = payload; - ast_format_copy(&type->format, &static_RTP_PT[payload].format); + new_type->asterisk_format = static_RTP_PT[payload].asterisk_format; + new_type->rtp_code = static_RTP_PT[payload].rtp_code; + new_type->payload = payload; + new_type->format = ao2_bump(static_RTP_PT[payload].format); - ast_debug(1, "Setting payload %d based on m type on %p\n", payload, codecs); + ast_debug(1, "Setting payload %d (%p) based on m type on %p\n", payload, new_type, codecs); + AST_VECTOR_INSERT(&codecs->payloads, payload, new_type); if (instance && instance->engine && instance->engine->payload_set) { - instance->engine->payload_set(instance, payload, type->asterisk_format, &type->format, type->rtp_code); + instance->engine->payload_set(instance, payload, new_type->asterisk_format, new_type->format, new_type->rtp_code); } - ao2_ref(type, -1); - + ast_rwlock_unlock(&codecs->codecs_lock); ast_rwlock_unlock(&static_RTP_PT_lock); } @@ -681,13 +664,16 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, unsigned int i; int found = 0; - if (pt < 0 || pt >= AST_RTP_MAX_PT) + ast_rwlock_rdlock(&mime_types_lock); + if (pt < 0 || pt >= AST_RTP_MAX_PT) { + ast_rwlock_unlock(&mime_types_lock); return -1; /* bogus payload type */ + } - ast_rwlock_rdlock(&mime_types_lock); + ast_rwlock_wrlock(&codecs->codecs_lock); for (i = 0; i < mime_types_len; ++i) { const struct ast_rtp_mime_type *t = &ast_rtp_mime_types[i]; - struct ast_rtp_payload_type *type; + struct ast_rtp_payload_type *new_type; if (strcasecmp(mimesubtype, t->subtype)) { continue; @@ -707,29 +693,33 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, found = 1; - if (!(type = ao2_find(codecs->payloads, &pt, OBJ_KEY | OBJ_NOLOCK))) { - if (!(type = ao2_alloc(sizeof(*type), NULL))) { - continue; - } - type->payload = pt; - ao2_link_flags(codecs->payloads, type, OBJ_NOLOCK); + new_type = ast_rtp_engine_alloc_payload_type(); + if (!new_type) { + continue; } - *type = t->payload_type; - type->payload = pt; + if (pt < AST_VECTOR_SIZE(&codecs->payloads)) { + ao2_t_cleanup(AST_VECTOR_GET(&codecs->payloads, pt), "cleaning up replaced payload type"); + } - if ((t->payload_type.format.id == AST_FORMAT_G726) && t->payload_type.asterisk_format && (options & AST_RTP_OPT_G726_NONSTANDARD)) { - ast_format_set(&type->format, AST_FORMAT_G726_AAL2, 0); + new_type->payload = pt; + new_type->asterisk_format = t->payload_type.asterisk_format; + new_type->rtp_code = t->payload_type.rtp_code; + if ((ast_format_cmp(t->payload_type.format, ast_format_g726) == AST_FORMAT_CMP_EQUAL) && + t->payload_type.asterisk_format && (options & AST_RTP_OPT_G726_NONSTANDARD)) { + new_type->format = ao2_bump(ast_format_g726_aal2); + } else { + new_type->format = ao2_bump(t->payload_type.format); } + AST_VECTOR_INSERT(&codecs->payloads, pt, new_type); if (instance && instance->engine && instance->engine->payload_set) { - instance->engine->payload_set(instance, pt, type->asterisk_format, &type->format, type->rtp_code); + instance->engine->payload_set(instance, pt, new_type->asterisk_format, new_type->format, new_type->rtp_code); } - ao2_ref(type, -1); - break; } + ast_rwlock_unlock(&codecs->codecs_lock); ast_rwlock_unlock(&mime_types_lock); return (found ? 0 : -2); @@ -742,157 +732,209 @@ int ast_rtp_codecs_payloads_set_rtpmap_type(struct ast_rtp_codecs *codecs, struc void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload) { + struct ast_rtp_payload_type *type; + if (payload < 0 || payload >= AST_RTP_MAX_PT) { return; } ast_debug(2, "Unsetting payload %d on %p\n", payload, codecs); - ao2_find(codecs->payloads, &payload, OBJ_KEY | OBJ_NOLOCK | OBJ_NODATA | OBJ_UNLINK); + ast_rwlock_wrlock(&codecs->codecs_lock); + if (payload < AST_VECTOR_SIZE(&codecs->payloads)) { + type = AST_VECTOR_GET(&codecs->payloads, payload); + ao2_cleanup(type); + AST_VECTOR_INSERT(&codecs->payloads, payload, NULL); + } if (instance && instance->engine && instance->engine->payload_set) { instance->engine->payload_set(instance, payload, 0, NULL, 0); } + + ast_rwlock_unlock(&codecs->codecs_lock); } -struct ast_rtp_payload_type ast_rtp_codecs_payload_lookup(struct ast_rtp_codecs *codecs, int payload) +struct ast_rtp_payload_type *ast_rtp_codecs_get_payload(struct ast_rtp_codecs *codecs, int payload) { - struct ast_rtp_payload_type result = { .asterisk_format = 0, }, *type; + struct ast_rtp_payload_type *type = NULL; if (payload < 0 || payload >= AST_RTP_MAX_PT) { - return result; + return NULL; } - if ((type = ao2_find(codecs->payloads, &payload, OBJ_KEY | OBJ_NOLOCK))) { - result = *type; - ao2_ref(type, -1); + ast_rwlock_rdlock(&codecs->codecs_lock); + if (payload < AST_VECTOR_SIZE(&codecs->payloads)) { + type = AST_VECTOR_GET(&codecs->payloads, payload); + ao2_bump(type); } + ast_rwlock_unlock(&codecs->codecs_lock); - if (!result.rtp_code && !result.asterisk_format) { + if (!type) { + type = ast_rtp_engine_alloc_payload_type(); + if (!type) { + return NULL; + } ast_rwlock_rdlock(&static_RTP_PT_lock); - result = static_RTP_PT[payload]; + type->asterisk_format = static_RTP_PT[payload].asterisk_format; + type->rtp_code = static_RTP_PT[payload].rtp_code; + type->payload = payload; + type->format = ao2_bump(static_RTP_PT[payload].format); ast_rwlock_unlock(&static_RTP_PT_lock); } - return result; + return type; } +int ast_rtp_codecs_payload_replace_format(struct ast_rtp_codecs *codecs, int payload, struct ast_format *format) +{ + struct ast_rtp_payload_type *type; + + if (payload < 0 || payload >= AST_RTP_MAX_PT) { + return -1; + } + + ast_rwlock_wrlock(&codecs->codecs_lock); + if (payload < AST_VECTOR_SIZE(&codecs->payloads)) { + type = AST_VECTOR_GET(&codecs->payloads, payload); + if (type && type->asterisk_format) { + ao2_replace(type->format, format); + } + } + ast_rwlock_unlock(&codecs->codecs_lock); + + return 0; +} struct ast_format *ast_rtp_codecs_get_payload_format(struct ast_rtp_codecs *codecs, int payload) { struct ast_rtp_payload_type *type; - struct ast_format *format; + struct ast_format *format = NULL; if (payload < 0 || payload >= AST_RTP_MAX_PT) { return NULL; } - if (!(type = ao2_find(codecs->payloads, &payload, OBJ_KEY | OBJ_NOLOCK))) { - return NULL; + ast_rwlock_rdlock(&codecs->codecs_lock); + if (payload < AST_VECTOR_SIZE(&codecs->payloads)) { + type = AST_VECTOR_GET(&codecs->payloads, payload); + if (type && type->asterisk_format) { + format = ao2_bump(type->format); + } } - - format = type->asterisk_format ? &type->format : NULL; - - ao2_ref(type, -1); + ast_rwlock_unlock(&codecs->codecs_lock); return format; } -static int rtp_payload_type_add_ast(void *obj, void *arg, int flags) +void ast_rtp_codecs_set_framing(struct ast_rtp_codecs *codecs, unsigned int framing) { - struct ast_rtp_payload_type *type = obj; - struct ast_format_cap *astformats = arg; - - if (type->asterisk_format) { - ast_format_cap_add(astformats, &type->format); + if (!framing) { + return; } - return 0; + ast_rwlock_wrlock(&codecs->codecs_lock); + codecs->framing = framing; + ast_rwlock_unlock(&codecs->codecs_lock); } -static int rtp_payload_type_add_nonast(void *obj, void *arg, int flags) +unsigned int ast_rtp_codecs_get_framing(struct ast_rtp_codecs *codecs) { - struct ast_rtp_payload_type *type = obj; - int *nonastformats = arg; + unsigned int framing; - if (!type->asterisk_format) { - *nonastformats |= type->rtp_code; - } + ast_rwlock_rdlock(&codecs->codecs_lock); + framing = codecs->framing; + ast_rwlock_unlock(&codecs->codecs_lock); - return 0; + return framing; } void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_format_cap *astformats, int *nonastformats) { - ast_format_cap_remove_all(astformats); + int i; + + ast_format_cap_remove_by_type(astformats, AST_MEDIA_TYPE_UNKNOWN); *nonastformats = 0; - ao2_callback(codecs->payloads, OBJ_NODATA | OBJ_MULTIPLE | OBJ_NOLOCK, rtp_payload_type_add_ast, astformats); - ao2_callback(codecs->payloads, OBJ_NODATA | OBJ_MULTIPLE | OBJ_NOLOCK, rtp_payload_type_add_nonast, nonastformats); -} + ast_rwlock_rdlock(&codecs->codecs_lock); + for (i = 0; i < AST_VECTOR_SIZE(&codecs->payloads); i++) { + struct ast_rtp_payload_type *type; -static int rtp_payload_type_find_format(void *obj, void *arg, int flags) -{ - struct ast_rtp_payload_type *type = obj; - struct ast_format *format = arg; + type = AST_VECTOR_GET(&codecs->payloads, i); + if (!type) { + continue; + } - return (type->asterisk_format && (ast_format_cmp(&type->format, format) != AST_FORMAT_CMP_NOT_EQUAL)) ? CMP_MATCH | CMP_STOP : 0; -} + if (type->asterisk_format) { + ast_format_cap_append(astformats, type->format, 0); + } else { + *nonastformats |= type->rtp_code; + } + } -static int rtp_payload_type_find_nonast_format(void *obj, void *arg, int flags) -{ - struct ast_rtp_payload_type *type = obj; - int *rtp_code = arg; + if (codecs->framing) { + ast_format_cap_set_framing(astformats, codecs->framing); + } - return ((!type->asterisk_format && (type->rtp_code == *rtp_code)) ? CMP_MATCH | CMP_STOP : 0); + ast_rwlock_unlock(&codecs->codecs_lock); } int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code) { struct ast_rtp_payload_type *type; - int i, res = -1; + int i; + int payload = -1; - if (asterisk_format && format && (type = ao2_callback(codecs->payloads, OBJ_NOLOCK, rtp_payload_type_find_format, (void*)format))) { - res = type->payload; - ao2_ref(type, -1); - return res; - } else if (!asterisk_format && (type = ao2_callback(codecs->payloads, OBJ_NOLOCK, rtp_payload_type_find_nonast_format, (void*)&code))) { - res = type->payload; - ao2_ref(type, -1); - return res; - } + ast_rwlock_rdlock(&codecs->codecs_lock); + for (i = 0; i < AST_VECTOR_SIZE(&codecs->payloads); i++) { + type = AST_VECTOR_GET(&codecs->payloads, i); + if (!type) { + continue; + } - ast_rwlock_rdlock(&static_RTP_PT_lock); - for (i = 0; i < AST_RTP_MAX_PT; i++) { - if (static_RTP_PT[i].asterisk_format && asterisk_format && format && - (ast_format_cmp(format, &static_RTP_PT[i].format) != AST_FORMAT_CMP_NOT_EQUAL)) { - res = i; - break; - } else if (!static_RTP_PT[i].asterisk_format && !asterisk_format && - (static_RTP_PT[i].rtp_code == code)) { - res = i; + if ((asterisk_format && format && ast_format_cmp(format, type->format) == AST_FORMAT_CMP_EQUAL) + || (!asterisk_format && type->rtp_code == code)) { + payload = i; break; } } - ast_rwlock_unlock(&static_RTP_PT_lock); + ast_rwlock_unlock(&codecs->codecs_lock); - return res; + if (payload < 0) { + ast_rwlock_rdlock(&static_RTP_PT_lock); + for (i = 0; i < AST_RTP_MAX_PT; i++) { + if (static_RTP_PT[i].asterisk_format && asterisk_format && format && + (ast_format_cmp(format, static_RTP_PT[i].format) != AST_FORMAT_CMP_NOT_EQUAL)) { + payload = i; + break; + } else if (!static_RTP_PT[i].asterisk_format && !asterisk_format && + (static_RTP_PT[i].rtp_code == code)) { + payload = i; + break; + } + } + ast_rwlock_unlock(&static_RTP_PT_lock); + } + + return payload; } + int ast_rtp_codecs_find_payload_code(struct ast_rtp_codecs *codecs, int code) { struct ast_rtp_payload_type *type; int res = -1; - /* Search the payload type in the codecs passed */ - if ((type = ao2_find(codecs->payloads, &code, OBJ_NOLOCK | OBJ_KEY))) - { - res = type->payload; - ao2_ref(type, -1); - return res; + ast_rwlock_rdlock(&codecs->codecs_lock); + if (code < AST_VECTOR_SIZE(&codecs->payloads)) { + type = AST_VECTOR_GET(&codecs->payloads, code); + if (type) { + res = type->payload; + } } + ast_rwlock_unlock(&codecs->codecs_lock); return res; } + const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, struct ast_format *format, int code, enum ast_rtp_options options) { int i; @@ -901,8 +943,9 @@ const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, struct ast_f ast_rwlock_rdlock(&mime_types_lock); for (i = 0; i < mime_types_len; i++) { 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)) { + (ast_format_cmp(format, ast_rtp_mime_types[i].payload_type.format) != AST_FORMAT_CMP_NOT_EQUAL)) { + if ((ast_format_cmp(format, ast_format_g726_aal2) == AST_FORMAT_CMP_EQUAL) && + (options & AST_RTP_OPT_G726_NONSTANDARD)) { res = "G726-32"; break; } else { @@ -929,7 +972,7 @@ unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, struct ast_format ast_rwlock_rdlock(&mime_types_lock); for (i = 0; i < mime_types_len; ++i) { 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)) { + (ast_format_cmp(format, ast_rtp_mime_types[i].payload_type.format) != AST_FORMAT_CMP_NOT_EQUAL)) { res = ast_rtp_mime_types[i].sample_rate; break; } else if (!ast_rtp_mime_types[i].payload_type.asterisk_format && !asterisk_format && @@ -953,15 +996,15 @@ char *ast_rtp_lookup_mime_multiple2(struct ast_str *buf, struct ast_format_cap * 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); + int x; + struct ast_format *tmp_fmt; + for (x = 0; x < ast_format_cap_count(ast_format_capability); x++) { + tmp_fmt = ast_format_cap_get_format(ast_format_capability, x); + name = ast_rtp_lookup_mime_subtype2(asterisk_format, tmp_fmt, 0, options); + ao2_ref(tmp_fmt, -1); 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); @@ -979,15 +1022,6 @@ char *ast_rtp_lookup_mime_multiple2(struct ast_str *buf, struct ast_format_cap * return ast_str_buffer(buf); } -void ast_rtp_codecs_packetization_set(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, struct ast_codec_pref *prefs) -{ - codecs->pref = *prefs; - - if (instance && instance->engine->packetization_set) { - instance->engine->packetization_set(instance, &instance->codecs.pref); - } -} - int ast_rtp_instance_dtmf_begin(struct ast_rtp_instance *instance, char digit) { return instance->engine->dtmf_begin ? instance->engine->dtmf_begin(instance, digit) : -1; @@ -1089,8 +1123,8 @@ void ast_rtp_instance_early_bridge_make_compatible(struct ast_channel *c_dst, st struct ast_rtp_glue *glue_dst, *glue_src; enum ast_rtp_glue_result audio_glue_dst_res = AST_RTP_GLUE_RESULT_FORBID, video_glue_dst_res = AST_RTP_GLUE_RESULT_FORBID; enum ast_rtp_glue_result audio_glue_src_res = AST_RTP_GLUE_RESULT_FORBID, video_glue_src_res = AST_RTP_GLUE_RESULT_FORBID; - struct ast_format_cap *cap_dst = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK); - struct ast_format_cap *cap_src = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK); + struct ast_format_cap *cap_dst = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + struct ast_format_cap *cap_src = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); /* Lock both channels so we can look for the glue that binds them together */ ast_channel_lock_both(c_dst, c_src); @@ -1131,7 +1165,7 @@ void ast_rtp_instance_early_bridge_make_compatible(struct ast_channel *c_dst, st } /* Make sure we have matching codecs */ - if (!ast_format_cap_has_joint(cap_dst, cap_src)) { + if (!ast_format_cap_iscompatible(cap_dst, cap_src)) { goto done; } @@ -1156,8 +1190,8 @@ done: ast_channel_unlock(c_dst); ast_channel_unlock(c_src); - ast_format_cap_destroy(cap_dst); - ast_format_cap_destroy(cap_src); + ao2_cleanup(cap_dst); + ao2_cleanup(cap_src); unref_instance_cond(&instance_dst); unref_instance_cond(&instance_src); @@ -1175,13 +1209,13 @@ 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; enum ast_rtp_glue_result audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID, video_glue1_res = AST_RTP_GLUE_RESULT_FORBID; - struct ast_format_cap *cap0 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK); - struct ast_format_cap *cap1 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK); + struct ast_format_cap *cap0 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + struct ast_format_cap *cap1 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); /* If there is no second channel just immediately bail out, we are of no use in that scenario */ if (!c1 || !cap1 || !cap0) { - ast_format_cap_destroy(cap0); - ast_format_cap_destroy(cap1); + ao2_cleanup(cap0); + ao2_cleanup(cap1); return -1; } @@ -1220,7 +1254,7 @@ int ast_rtp_instance_early_bridge(struct ast_channel *c0, struct ast_channel *c1 } /* Make sure we have matching codecs */ - if (!ast_format_cap_has_joint(cap0, cap1)) { + if (!ast_format_cap_iscompatible(cap0, cap1)) { goto done; } @@ -1233,8 +1267,8 @@ done: ast_channel_unlock(c0); ast_channel_unlock(c1); - ast_format_cap_destroy(cap0); - ast_format_cap_destroy(cap1); + ao2_cleanup(cap0); + ao2_cleanup(cap1); unref_instance_cond(&instance0); unref_instance_cond(&instance1); @@ -1419,7 +1453,7 @@ void ast_rtp_instance_available_formats(struct ast_rtp_instance *instance, struc { if (instance->engine->available_formats) { instance->engine->available_formats(instance, to_endpoint, to_asterisk, result); - if (!ast_format_cap_is_empty(result)) { + if (ast_format_cap_count(result)) { return; } } @@ -1631,7 +1665,27 @@ void ast_rtp_dtls_cfg_free(struct ast_rtp_dtls_cfg *dtls_cfg) ast_free(dtls_cfg->capath); } -static void set_next_mime_type(const struct ast_format *format, int rtp_code, char *type, char *subtype, unsigned int sample_rate) +/*! \internal + * \brief Small helper routine that cleans up entry i in + * \c static_RTP_PT. + */ +static void rtp_engine_static_RTP_PT_cleanup(int i) +{ + ao2_cleanup(static_RTP_PT[i].format); + memset(&static_RTP_PT[i], 0, sizeof(struct ast_rtp_payload_type)); +} + +/*! \internal + * \brief Small helper routine that cleans up entry i in + * \c ast_rtp_mime_types. + */ +static void rtp_engine_mime_type_cleanup(int i) +{ + ao2_cleanup(ast_rtp_mime_types[i].payload_type.format); + memset(&ast_rtp_mime_types[i], 0, sizeof(struct ast_rtp_mime_type)); +} + +static void set_next_mime_type(struct ast_format *format, int rtp_code, const char *type, const char *subtype, unsigned int sample_rate) { int x = mime_types_len; if (ARRAY_LEN(ast_rtp_mime_types) == mime_types_len) { @@ -1639,20 +1693,22 @@ static void set_next_mime_type(const struct ast_format *format, int rtp_code, ch } ast_rwlock_wrlock(&mime_types_lock); + /* Make sure any previous value in ast_rtp_mime_types is cleaned up */ + memset(&ast_rtp_mime_types[x], 0, sizeof(struct ast_rtp_mime_type)); if (format) { ast_rtp_mime_types[x].payload_type.asterisk_format = 1; - ast_format_copy(&ast_rtp_mime_types[x].payload_type.format, format); + ast_rtp_mime_types[x].payload_type.format = ao2_bump(format); } else { ast_rtp_mime_types[x].payload_type.rtp_code = rtp_code; } - ast_rtp_mime_types[x].type = type; - ast_rtp_mime_types[x].subtype = subtype; + ast_copy_string(ast_rtp_mime_types[x].type, type, sizeof(ast_rtp_mime_types[x].type)); + ast_copy_string(ast_rtp_mime_types[x].subtype, subtype, sizeof(ast_rtp_mime_types[x].subtype)); ast_rtp_mime_types[x].sample_rate = sample_rate; mime_types_len++; ast_rwlock_unlock(&mime_types_lock); } -static void add_static_payload(int map, const struct ast_format *format, int rtp_code) +static void add_static_payload(int map, struct ast_format *format, int rtp_code) { int x; ast_rwlock_wrlock(&static_RTP_PT_lock); @@ -1667,39 +1723,38 @@ static void add_static_payload(int map, const struct ast_format *format, int rtp } if (map < 0) { - ast_log(LOG_WARNING, "No Dynamic RTP mapping available for format %s\n" ,ast_getformatname(format)); + ast_log(LOG_WARNING, "No Dynamic RTP mapping available for format %s\n", + ast_format_get_name(format)); ast_rwlock_unlock(&static_RTP_PT_lock); return; } if (format) { static_RTP_PT[map].asterisk_format = 1; - ast_format_copy(&static_RTP_PT[map].format, format); + static_RTP_PT[map].format = ao2_bump(format); } else { static_RTP_PT[map].rtp_code = rtp_code; } ast_rwlock_unlock(&static_RTP_PT_lock); } -int ast_rtp_engine_load_format(const struct ast_format *format) +int ast_rtp_engine_load_format(struct ast_format *format) { - switch (format->id) { - case AST_FORMAT_SILK: - set_next_mime_type(format, 0, "audio", "SILK", ast_format_rate(format)); - add_static_payload(-1, format, 0); - break; - case AST_FORMAT_CELT: - set_next_mime_type(format, 0, "audio", "CELT", ast_format_rate(format)); - add_static_payload(-1, format, 0); - break; - default: - break; - } + char *codec_name = ast_strdupa(ast_format_get_name(format)); + + codec_name = ast_str_to_upper(codec_name); + + set_next_mime_type(format, + 0, + ast_codec_media_type2str(ast_format_get_type(format)), + codec_name, + ast_format_get_sample_rate(format)); + add_static_payload(-1, format, 0); return 0; } -int ast_rtp_engine_unload_format(const struct ast_format *format) +int ast_rtp_engine_unload_format(struct ast_format *format) { int x; int y = 0; @@ -1707,17 +1762,17 @@ int ast_rtp_engine_unload_format(const struct ast_format *format) ast_rwlock_wrlock(&static_RTP_PT_lock); /* remove everything pertaining to this format id from the lists */ for (x = 0; x < AST_RTP_MAX_PT; x++) { - if (ast_format_cmp(&static_RTP_PT[x].format, format) == AST_FORMAT_CMP_EQUAL) { - memset(&static_RTP_PT[x], 0, sizeof(struct ast_rtp_payload_type)); + if (ast_format_cmp(static_RTP_PT[x].format, format) == AST_FORMAT_CMP_EQUAL) { + rtp_engine_static_RTP_PT_cleanup(x); } } ast_rwlock_unlock(&static_RTP_PT_lock); - ast_rwlock_wrlock(&mime_types_lock); /* rebuild the list skipping the items matching this id */ for (x = 0; x < mime_types_len; x++) { - if (ast_format_cmp(&ast_rtp_mime_types[x].payload_type.format, format) == AST_FORMAT_CMP_EQUAL) { + if (ast_format_cmp(ast_rtp_mime_types[x].payload_type.format, format) == AST_FORMAT_CMP_EQUAL) { + rtp_engine_mime_type_cleanup(x); continue; } ast_rtp_mime_types[y] = ast_rtp_mime_types[x]; @@ -1976,16 +2031,32 @@ struct stasis_topic *ast_rtp_topic(void) static void rtp_engine_shutdown(void) { + int x; + ao2_cleanup(rtp_topic); rtp_topic = NULL; STASIS_MESSAGE_TYPE_CLEANUP(ast_rtp_rtcp_received_type); STASIS_MESSAGE_TYPE_CLEANUP(ast_rtp_rtcp_sent_type); + + ast_rwlock_wrlock(&static_RTP_PT_lock); + for (x = 0; x < AST_RTP_MAX_PT; x++) { + if (static_RTP_PT[x].format) { + rtp_engine_static_RTP_PT_cleanup(x); + } + } + ast_rwlock_unlock(&static_RTP_PT_lock); + + ast_rwlock_wrlock(&mime_types_lock); + for (x = 0; x < mime_types_len; x++) { + if (ast_rtp_mime_types[x].payload_type.format) { + rtp_engine_mime_type_cleanup(x); + } + } + ast_rwlock_unlock(&mime_types_lock); } int ast_rtp_engine_init() { - struct ast_format tmpfmt; - ast_rwlock_init(&mime_types_lock); ast_rwlock_init(&static_RTP_PT_lock); @@ -1998,90 +2069,90 @@ int ast_rtp_engine_init() ast_register_atexit(rtp_engine_shutdown); /* Define all the RTP mime types available */ - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G723_1, 0), 0, "audio", "G723", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_GSM, 0), 0, "audio", "GSM", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0), 0, "audio", "PCMU", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0), 0, "audio", "G711U", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0), 0, "audio", "PCMA", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0), 0, "audio", "G711A", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G726, 0), 0, "audio", "G726-32", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), 0, "audio", "DVI4", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0), 0, "audio", "L16", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR16, 0), 0, "audio", "L16", 16000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR16, 0), 0, "audio", "L16-256", 16000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_LPC10, 0), 0, "audio", "LPC", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G729A, 0), 0, "audio", "G729", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G729A, 0), 0, "audio", "G729A", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G729A, 0), 0, "audio", "G.729", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX, 0), 0, "audio", "speex", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX16, 0), 0, "audio", "speex", 16000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX32, 0), 0, "audio", "speex", 32000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_ILBC, 0), 0, "audio", "iLBC", 8000); + set_next_mime_type(ast_format_g723, 0, "audio", "G723", 8000); + set_next_mime_type(ast_format_gsm, 0, "audio", "GSM", 8000); + set_next_mime_type(ast_format_ulaw, 0, "audio", "PCMU", 8000); + set_next_mime_type(ast_format_ulaw, 0, "audio", "G711U", 8000); + set_next_mime_type(ast_format_alaw, 0, "audio", "PCMA", 8000); + set_next_mime_type(ast_format_alaw, 0, "audio", "G711A", 8000); + set_next_mime_type(ast_format_g726, 0, "audio", "G726-32", 8000); + set_next_mime_type(ast_format_adpcm, 0, "audio", "DVI4", 8000); + set_next_mime_type(ast_format_slin, 0, "audio", "L16", 8000); + set_next_mime_type(ast_format_slin16, 0, "audio", "L16", 16000); + set_next_mime_type(ast_format_slin16, 0, "audio", "L16-256", 16000); + set_next_mime_type(ast_format_lpc10, 0, "audio", "LPC", 8000); + set_next_mime_type(ast_format_g729, 0, "audio", "G729", 8000); + set_next_mime_type(ast_format_g729, 0, "audio", "G729A", 8000); + set_next_mime_type(ast_format_g729, 0, "audio", "G.729", 8000); + set_next_mime_type(ast_format_speex, 0, "audio", "speex", 8000); + set_next_mime_type(ast_format_speex16, 0, "audio", "speex", 16000); + set_next_mime_type(ast_format_speex32, 0, "audio", "speex", 32000); + set_next_mime_type(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 */ - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G722, 0), 0, "audio", "G722", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G726_AAL2, 0), 0, "audio", "AAL2-G726-32", 8000); + set_next_mime_type(ast_format_g722, 0, "audio", "G722", 8000); + set_next_mime_type(ast_format_g726_aal2, 0, "audio", "AAL2-G726-32", 8000); set_next_mime_type(NULL, AST_RTP_DTMF, "audio", "telephone-event", 8000); set_next_mime_type(NULL, AST_RTP_CISCO_DTMF, "audio", "cisco-telephone-event", 8000); set_next_mime_type(NULL, AST_RTP_CN, "audio", "CN", 8000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_JPEG, 0), 0, "video", "JPEG", 90000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_PNG, 0), 0, "video", "PNG", 90000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_H261, 0), 0, "video", "H261", 90000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_H263, 0), 0, "video", "H263", 90000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), 0, "video", "H263-1998", 90000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_H264, 0), 0, "video", "H264", 90000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_MP4_VIDEO, 0), 0, "video", "MP4V-ES", 90000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_T140RED, 0), 0, "text", "RED", 1000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_T140, 0), 0, "text", "T140", 1000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SIREN7, 0), 0, "audio", "G7221", 16000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SIREN14, 0), 0, "audio", "G7221", 32000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G719, 0), 0, "audio", "G719", 48000); + set_next_mime_type(ast_format_jpeg, 0, "video", "JPEG", 90000); + set_next_mime_type(ast_format_png, 0, "video", "PNG", 90000); + set_next_mime_type(ast_format_h261, 0, "video", "H261", 90000); + set_next_mime_type(ast_format_h263, 0, "video", "H263", 90000); + set_next_mime_type(ast_format_h263p, 0, "video", "h263-1998", 90000); + set_next_mime_type(ast_format_h264, 0, "video", "H264", 90000); + set_next_mime_type(ast_format_mp4, 0, "video", "MP4V-ES", 90000); + set_next_mime_type(ast_format_t140_red, 0, "text", "RED", 1000); + set_next_mime_type(ast_format_t140, 0, "text", "T140", 1000); + set_next_mime_type(ast_format_siren7, 0, "audio", "G7221", 16000); + set_next_mime_type(ast_format_siren14, 0, "audio", "G7221", 32000); + set_next_mime_type(ast_format_g719, 0, "audio", "G719", 48000); /* Opus and VP8 */ - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_OPUS, 0), 0, "audio", "opus", 48000); - set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_VP8, 0), 0, "video", "VP8", 90000); + set_next_mime_type(ast_format_opus, 0, "audio", "opus", 48000); + set_next_mime_type(ast_format_vp8, 0, "video", "VP8", 90000); /* Define the static rtp payload mappings */ - add_static_payload(0, ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0), 0); + add_static_payload(0, ast_format_ulaw, 0); #ifdef USE_DEPRECATED_G726 - add_static_payload(2, ast_format_set(&tmpfmt, AST_FORMAT_G726, 0), 0);/* Technically this is G.721, but if Cisco can do it, so can we... */ + add_static_payload(2, ast_format_g726, 0);/* Technically this is G.721, but if Cisco can do it, so can we... */ #endif - add_static_payload(3, ast_format_set(&tmpfmt, AST_FORMAT_GSM, 0), 0); - add_static_payload(4, ast_format_set(&tmpfmt, AST_FORMAT_G723_1, 0), 0); - add_static_payload(5, ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), 0);/* 8 kHz */ - add_static_payload(6, ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), 0); /* 16 kHz */ - add_static_payload(7, ast_format_set(&tmpfmt, AST_FORMAT_LPC10, 0), 0); - add_static_payload(8, ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0), 0); - add_static_payload(9, ast_format_set(&tmpfmt, AST_FORMAT_G722, 0), 0); - add_static_payload(10, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0), 0); /* 2 channels */ - add_static_payload(11, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0), 0); /* 1 channel */ + add_static_payload(3, ast_format_gsm, 0); + add_static_payload(4, ast_format_g723, 0); + add_static_payload(5, ast_format_adpcm, 0);/* 8 kHz */ + add_static_payload(6, ast_format_adpcm, 0); /* 16 kHz */ + add_static_payload(7, ast_format_lpc10, 0); + add_static_payload(8, ast_format_alaw, 0); + add_static_payload(9, ast_format_g722, 0); + add_static_payload(10, ast_format_slin, 0); /* 2 channels */ + add_static_payload(11, ast_format_slin, 0); /* 1 channel */ add_static_payload(13, NULL, AST_RTP_CN); - add_static_payload(16, ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), 0); /* 11.025 kHz */ - add_static_payload(17, ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), 0); /* 22.050 kHz */ - add_static_payload(18, ast_format_set(&tmpfmt, AST_FORMAT_G729A, 0), 0); + add_static_payload(16, ast_format_adpcm, 0); /* 11.025 kHz */ + add_static_payload(17, ast_format_adpcm, 0); /* 22.050 kHz */ + add_static_payload(18, ast_format_g729, 0); add_static_payload(19, NULL, AST_RTP_CN); /* Also used for CN */ - add_static_payload(26, ast_format_set(&tmpfmt, AST_FORMAT_JPEG, 0), 0); - add_static_payload(31, ast_format_set(&tmpfmt, AST_FORMAT_H261, 0), 0); - add_static_payload(34, ast_format_set(&tmpfmt, AST_FORMAT_H263, 0), 0); - add_static_payload(97, ast_format_set(&tmpfmt, AST_FORMAT_ILBC, 0), 0); - add_static_payload(98, ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), 0); - add_static_payload(99, ast_format_set(&tmpfmt, AST_FORMAT_H264, 0), 0); + add_static_payload(26, ast_format_jpeg, 0); + add_static_payload(31, ast_format_h261, 0); + add_static_payload(34, ast_format_h263, 0); + add_static_payload(97, ast_format_ilbc, 0); + add_static_payload(98, ast_format_h263p, 0); + add_static_payload(99, ast_format_h264, 0); add_static_payload(101, NULL, AST_RTP_DTMF); - add_static_payload(102, ast_format_set(&tmpfmt, AST_FORMAT_SIREN7, 0), 0); - add_static_payload(103, ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), 0); - add_static_payload(104, ast_format_set(&tmpfmt, AST_FORMAT_MP4_VIDEO, 0), 0); - add_static_payload(105, ast_format_set(&tmpfmt, AST_FORMAT_T140RED, 0), 0); /* Real time text chat (with redundancy encoding) */ - add_static_payload(106, ast_format_set(&tmpfmt, AST_FORMAT_T140, 0), 0); /* Real time text chat */ - add_static_payload(110, ast_format_set(&tmpfmt, AST_FORMAT_SPEEX, 0), 0); - add_static_payload(111, ast_format_set(&tmpfmt, AST_FORMAT_G726, 0), 0); - add_static_payload(112, ast_format_set(&tmpfmt, AST_FORMAT_G726_AAL2, 0), 0); - add_static_payload(115, ast_format_set(&tmpfmt, AST_FORMAT_SIREN14, 0), 0); - add_static_payload(116, ast_format_set(&tmpfmt, AST_FORMAT_G719, 0), 0); - add_static_payload(117, ast_format_set(&tmpfmt, AST_FORMAT_SPEEX16, 0), 0); - add_static_payload(118, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR16, 0), 0); /* 16 Khz signed linear */ - add_static_payload(119, ast_format_set(&tmpfmt, AST_FORMAT_SPEEX32, 0), 0); + add_static_payload(102, ast_format_siren7, 0); + add_static_payload(103, ast_format_h263p, 0); + add_static_payload(104, ast_format_mp4, 0); + add_static_payload(105, ast_format_t140_red, 0); /* Real time text chat (with redundancy encoding) */ + add_static_payload(106, ast_format_t140, 0); /* Real time text chat */ + add_static_payload(110, ast_format_speex, 0); + add_static_payload(111, ast_format_g726, 0); + add_static_payload(112, ast_format_g726_aal2, 0); + add_static_payload(115, ast_format_siren14, 0); + add_static_payload(116, ast_format_g719, 0); + add_static_payload(117, ast_format_speex16, 0); + add_static_payload(118, ast_format_slin16, 0); /* 16 Khz signed linear */ + add_static_payload(119, ast_format_speex32, 0); add_static_payload(121, NULL, AST_RTP_CISCO_DTMF); /* Must be type 121 */ /* Opus and VP8 */ - add_static_payload(100, ast_format_set(&tmpfmt, AST_FORMAT_VP8, 0), 0); - add_static_payload(107, ast_format_set(&tmpfmt, AST_FORMAT_OPUS, 0), 0); + add_static_payload(100, ast_format_vp8, 0); + add_static_payload(107, ast_format_opus, 0); return 0; } diff --git a/main/slinfactory.c b/main/slinfactory.c index 44efc420f..8c117d652 100644 --- a/main/slinfactory.c +++ b/main/slinfactory.c @@ -33,24 +33,26 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/frame.h" +#include "asterisk/format_cache.h" #include "asterisk/slinfactory.h" #include "asterisk/translate.h" +#include "asterisk/astobj2.h" void ast_slinfactory_init(struct ast_slinfactory *sf) { memset(sf, 0, sizeof(*sf)); sf->offset = sf->hold; - ast_format_set(&sf->output_format, AST_FORMAT_SLINEAR, 0); + sf->output_format = ao2_bump(ast_format_slin); } -int ast_slinfactory_init_with_format(struct ast_slinfactory *sf, const struct ast_format *slin_out) +int ast_slinfactory_init_with_format(struct ast_slinfactory *sf, struct ast_format *slin_out) { memset(sf, 0, sizeof(*sf)); sf->offset = sf->hold; - if (!ast_format_is_slinear(slin_out)) { + if (!ast_format_cache_is_slinear(slin_out)) { return -1; } - ast_format_copy(&sf->output_format, slin_out); + sf->output_format = ao2_bump(slin_out); return 0; } @@ -64,8 +66,14 @@ void ast_slinfactory_destroy(struct ast_slinfactory *sf) sf->trans = NULL; } - while ((f = AST_LIST_REMOVE_HEAD(&sf->queue, frame_list))) + while ((f = AST_LIST_REMOVE_HEAD(&sf->queue, frame_list))) { ast_frfree(f); + } + + ao2_cleanup(sf->output_format); + sf->output_format = NULL; + ao2_cleanup(sf->format); + sf->format = NULL; } int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f) @@ -83,22 +91,22 @@ int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f) return 0; } - 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)) { + 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.format))) { + if (!(sf->trans = ast_translator_build_path(sf->output_format, f->subclass.format))) { ast_log(LOG_WARNING, "Cannot build a path from %s (%u)to %s (%u)\n", - ast_getformatname(&f->subclass.format), - f->subclass.format.id, - ast_getformatname(&sf->output_format), - sf->output_format.id); + ast_format_get_name(f->subclass.format), + ast_format_get_codec_id(f->subclass.format), + ast_format_get_name(sf->output_format), + ast_format_get_codec_id(sf->output_format)); return 0; } - ast_format_copy(&sf->format, &f->subclass.format); + ao2_replace(sf->format, f->subclass.format); } if (!(begin_frame = ast_translate(sf->trans, f, 0))) { diff --git a/main/smoother.c b/main/smoother.c new file mode 100644 index 000000000..720ad8549 --- /dev/null +++ b/main/smoother.c @@ -0,0 +1,227 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, 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 Frame smoother manipulation routines + * + * \author Mark Spencer <markster@digium.com> + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/_private.h" +#include "asterisk/frame.h" +#include "asterisk/astobj2.h" +#include "asterisk/time.h" +#include "asterisk/utils.h" +#include "asterisk/format.h" +#include "asterisk/codec.h" +#include "asterisk/smoother.h" + +#define SMOOTHER_SIZE 8000 + +struct ast_smoother { + int size; + struct ast_format *format; + int flags; + float samplesperbyte; + unsigned int opt_needs_swap:1; + struct ast_frame f; + struct timeval delivery; + char data[SMOOTHER_SIZE]; + char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET]; + struct ast_frame *opt; + int len; +}; + +static int smoother_frame_feed(struct ast_smoother *s, struct ast_frame *f, int swap) +{ + if (s->flags & AST_SMOOTHER_FLAG_G729) { + if (s->len % 10) { + ast_log(LOG_NOTICE, "Dropping extra frame of G.729 since we already have a VAD frame at the end\n"); + return 0; + } + } + if (swap) { + ast_swapcopy_samples(s->data + s->len, f->data.ptr, f->samples); + } else { + memcpy(s->data + s->len, f->data.ptr, f->datalen); + } + /* If either side is empty, reset the delivery time */ + if (!s->len || ast_tvzero(f->delivery) || ast_tvzero(s->delivery)) { /* XXX really ? */ + s->delivery = f->delivery; + } + s->len += f->datalen; + + return 0; +} + +void ast_smoother_reset(struct ast_smoother *s, int bytes) +{ + ao2_cleanup(s->format); + memset(s, 0, sizeof(*s)); + s->size = bytes; +} + +void ast_smoother_reconfigure(struct ast_smoother *s, int bytes) +{ + /* if there is no change, then nothing to do */ + if (s->size == bytes) { + return; + } + /* set the new desired output size */ + s->size = bytes; + /* if there is no 'optimized' frame in the smoother, + * then there is nothing left to do + */ + if (!s->opt) { + return; + } + /* there is an 'optimized' frame here at the old size, + * but it must now be put into the buffer so the data + * can be extracted at the new size + */ + smoother_frame_feed(s, s->opt, s->opt_needs_swap); + s->opt = NULL; +} + +struct ast_smoother *ast_smoother_new(int size) +{ + struct ast_smoother *s; + if (size < 1) + return NULL; + if ((s = ast_calloc(1, sizeof(*s)))) + ast_smoother_reset(s, size); + return s; +} + +int ast_smoother_get_flags(struct ast_smoother *s) +{ + return s->flags; +} + +void ast_smoother_set_flags(struct ast_smoother *s, int flags) +{ + s->flags = flags; +} + +int ast_smoother_test_flag(struct ast_smoother *s, int flag) +{ + return (s->flags & flag); +} + +int __ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f, int swap) +{ + if (f->frametype != AST_FRAME_VOICE) { + ast_log(LOG_WARNING, "Huh? Can't smooth a non-voice frame!\n"); + return -1; + } + if (!s->format) { + s->format = ao2_bump(f->subclass.format); + s->samplesperbyte = (float)f->samples / (float)f->datalen; + } 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_format_get_name(s->format), ast_format_get_name(f->subclass.format)); + return -1; + } + if (s->len + f->datalen > SMOOTHER_SIZE) { + ast_log(LOG_WARNING, "Out of smoother space\n"); + return -1; + } + if (((f->datalen == s->size) || + ((f->datalen < 10) && (s->flags & AST_SMOOTHER_FLAG_G729))) && + !s->opt && + !s->len && + (f->offset >= AST_MIN_OFFSET)) { + /* Optimize by sending the frame we just got + on the next read, thus eliminating the douple + copy */ + if (swap) + ast_swapcopy_samples(f->data.ptr, f->data.ptr, f->samples); + s->opt = f; + s->opt_needs_swap = swap ? 1 : 0; + return 0; + } + + return smoother_frame_feed(s, f, swap); +} + +struct ast_frame *ast_smoother_read(struct ast_smoother *s) +{ + struct ast_frame *opt; + int len; + + /* IF we have an optimization frame, send it */ + if (s->opt) { + if (s->opt->offset < AST_FRIENDLY_OFFSET) + ast_log(LOG_WARNING, "Returning a frame of inappropriate offset (%d).\n", + s->opt->offset); + opt = s->opt; + s->opt = NULL; + return opt; + } + + /* Make sure we have enough data */ + if (s->len < s->size) { + /* Or, if this is a G.729 frame with VAD on it, send it immediately anyway */ + if (!((s->flags & AST_SMOOTHER_FLAG_G729) && (s->len % 10))) + return NULL; + } + len = s->size; + if (len > s->len) + len = s->len; + /* Make frame */ + s->f.frametype = AST_FRAME_VOICE; + 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; + /* Samples will be improper given VAD, but with VAD the concept really doesn't even exist */ + s->f.samples = len * s->samplesperbyte; /* XXX rounding */ + s->f.delivery = s->delivery; + /* Fill Data */ + memcpy(s->f.data.ptr, s->data, len); + s->len -= len; + /* Move remaining data to the front if applicable */ + if (s->len) { + /* In principle this should all be fine because if we are sending + G.729 VAD, the next timestamp will take over anyawy */ + 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_get_sample_rate(s->format))); + } + } + /* Return frame */ + return &s->f; +} + +void ast_smoother_free(struct ast_smoother *s) +{ + ao2_cleanup(s->format); + ast_free(s); +} + diff --git a/main/sorcery.c b/main/sorcery.c index 9488dee4b..85a699bcb 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -43,7 +43,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/taskprocessor.h" #include "asterisk/threadpool.h" #include "asterisk/json.h" -#include "asterisk/format_pref.h" /* To prevent DEBUG_FD_LEAKS from interfering with things we undef open and close */ #undef open @@ -233,10 +232,9 @@ static int chararray_handler_fn(const void *obj, const intptr_t *args, char **bu static int codec_handler_fn(const void *obj, const intptr_t *args, char **buf) { - char tmp_buf[256]; - struct ast_codec_pref *pref = (struct ast_codec_pref *)(obj + args[0]); - ast_codec_pref_string(pref, tmp_buf, sizeof(tmp_buf)); - return !(*buf = ast_strdup(tmp_buf)); + struct ast_str *codec_buf = ast_str_alloca(64); + struct ast_format_cap **cap = (struct ast_format_cap **)(obj + args[0]); + return !(*buf = ast_strdup(ast_format_cap_get_names(*cap, &codec_buf))); } static sorcery_field_handler sorcery_field_default_handler(enum aco_option_type type) diff --git a/main/sounds_index.c b/main/sounds_index.c index 41430fa45..bc5263908 100644 --- a/main/sounds_index.c +++ b/main/sounds_index.c @@ -158,10 +158,10 @@ static int show_sound_info_cb(void *obj, void *arg, int flags) { char *language = obj; struct ast_cli_args *a = arg; - struct ast_format format; + struct ast_format *format; int formats_shown = 0; RAII_VAR(struct ast_media_index *, local_index, ast_sounds_get_index(), ao2_cleanup); - RAII_VAR(struct ast_format_cap *, cap, NULL, ast_format_cap_destroy); + struct ast_format_cap *cap; const char *description = ast_media_get_description(local_index, a->argv[3], language); ast_cli(a->fd, " Language %s:\n", language); @@ -171,12 +171,14 @@ static int show_sound_info_cb(void *obj, void *arg, int flags) cap = ast_media_get_format_cap(local_index, a->argv[3], language); if (cap) { - ast_format_cap_iter_start(cap); - while (!ast_format_cap_iter_next(cap, &format)) { - ast_cli(a->fd, " Format: %s\n", ast_getformatname(&format)); + int x; + for (x = 0; x < ast_format_cap_count(cap); x++) { + format = ast_format_cap_get_format(cap, x); + ast_cli(a->fd, " Format: %s\n", ast_format_get_name(format)); + ao2_ref(format, -1); formats_shown = 1; - } - ast_format_cap_iter_end(cap); + } + ao2_ref(cap, -1); } if (!formats_shown) { diff --git a/main/translate.c b/main/translate.c index c24700b45..0696f5d83 100644 --- a/main/translate.c +++ b/main/translate.c @@ -43,6 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/sched.h" #include "asterisk/cli.h" #include "asterisk/term.h" +#include "asterisk/format.h" /*! \todo * TODO: sample frames for each supported input format. @@ -76,11 +77,11 @@ struct translator_path { static struct translator_path **__matrix; /*! - * \brief table for converting index to format id values. + * \brief table for converting index to format values. * * \note this table is protected by the table_lock. */ -static int *__indextable; +static unsigned int *__indextable; /*! protects the __indextable for resizing */ static ast_rwlock_t tablelock; @@ -99,9 +100,9 @@ static void matrix_rebuild(int samples); /*! * \internal - * \brief converts format id to index value. + * \brief converts codec id to index value. */ -static int format2index(enum ast_format_id id) +static int codec_to_index(unsigned int id) { int x; @@ -119,16 +120,34 @@ static int format2index(enum ast_format_id id) /*! * \internal - * \brief add a new format to the matrix and index table structures. + * \brief converts codec to index value. + */ +static int codec2index(struct ast_codec *codec) +{ + return codec_to_index(codec->id); +} + +/*! + * \internal + * \brief converts format to codec index value. + */ +static int format2index(struct ast_format *format) +{ + return codec_to_index(ast_format_get_codec_id(format)); +} + +/*! + * \internal + * \brief add a new codec to the matrix and index table structures. * - * \note it is perfectly safe to call this on formats already indexed. + * \note it is perfectly safe to call this on codecs already indexed. * * \retval 0, success * \retval -1, matrix and index table need to be resized */ -static int add_format2index(enum ast_format_id id) +static int add_codec2index(struct ast_codec *codec) { - if (format2index(id) >= 0) { + if (codec2index(codec) != -1) { /* format is already already indexed */ return 0; } @@ -138,7 +157,7 @@ static int add_format2index(enum ast_format_id id) ast_rwlock_unlock(&tablelock); return -1; /* hit max length */ } - __indextable[cur_max_index] = id; + __indextable[cur_max_index] = codec->id; cur_max_index++; ast_rwlock_unlock(&tablelock); @@ -147,20 +166,20 @@ static int add_format2index(enum ast_format_id id) /*! * \internal - * \brief converts index value back to format id + * \brief converts index value back to codec */ -static enum ast_format_id index2format(int index) +static struct ast_codec *index2codec(int index) { - enum ast_format_id format_id; + struct ast_codec *codec; if (index >= cur_max_index) { return 0; } ast_rwlock_rdlock(&tablelock); - format_id = __indextable[index]; + codec = ast_codec_get_by_id(__indextable[index]); ast_rwlock_unlock(&tablelock); - return format_id; + return codec; } /*! @@ -176,7 +195,7 @@ static enum ast_format_id index2format(int index) static int matrix_resize(int init) { struct translator_path **tmp_matrix = NULL; - int *tmp_table = NULL; + unsigned int *tmp_table = NULL; int old_index; int x; @@ -202,7 +221,7 @@ static int matrix_resize(int init) } /* make new index table */ - if (!(tmp_table = ast_calloc(1, sizeof(int) * index_size))) { + if (!(tmp_table = ast_calloc(1, sizeof(unsigned int) * index_size))) { goto resize_cleanup; } @@ -213,7 +232,7 @@ static int matrix_resize(int init) } ast_free(__matrix); - memcpy(tmp_table, __indextable, sizeof(int) * old_index); + memcpy(tmp_table, __indextable, sizeof(unsigned int) * old_index); ast_free(__indextable); } @@ -270,11 +289,23 @@ static struct translator_path *matrix_get(unsigned int x, unsigned int y) * wrappers around the translator routines. */ +static void destroy(struct ast_trans_pvt *pvt) +{ + struct ast_translator *t = pvt->t; + + if (t->destroy) { + t->destroy(pvt); + } + ao2_cleanup(pvt->f.subclass.format); + ast_free(pvt); + ast_module_unref(t->module); +} + /*! * \brief Allocate the descriptor, required outbuf space, * and possibly desc. */ -static void *newpvt(struct ast_translator *t, const struct ast_format *explicit_dst) +static struct ast_trans_pvt *newpvt(struct ast_translator *t) { struct ast_trans_pvt *pvt; int len; @@ -300,28 +331,49 @@ static void *newpvt(struct ast_translator *t, const struct ast_format *explicit_ if (t->buf_size) {/* finally buffer and header */ pvt->outbuf.c = ofs + AST_FRIENDLY_OFFSET; } - /* if a explicit destination format is provided, set that on the pvt so the - * translator will process it. */ - if (explicit_dst) { - ast_format_copy(&pvt->explicit_dst, explicit_dst); - } + + ast_module_ref(t->module); + /* call local init routine, if present */ if (t->newpvt && t->newpvt(pvt)) { ast_free(pvt); + ast_module_unref(t->module); return NULL; } - ast_module_ref(t->module); - return pvt; -} -static void destroy(struct ast_trans_pvt *pvt) -{ - struct ast_translator *t = pvt->t; + /* Setup normal static translation frame. */ + pvt->f.frametype = AST_FRAME_VOICE; + pvt->f.mallocd = 0; + pvt->f.offset = AST_FRIENDLY_OFFSET; + pvt->f.src = pvt->t->name; + pvt->f.data.ptr = pvt->outbuf.c; - if (t->destroy) - t->destroy(pvt); - ast_free(pvt); - ast_module_unref(t->module); + /* if the translator has not provided a format find one in the cache or create one */ + if (!pvt->f.subclass.format) { + if (!ast_strlen_zero(pvt->t->format)) { + pvt->f.subclass.format = ast_format_cache_get(pvt->t->format); + } + + if (!pvt->f.subclass.format) { + struct ast_codec *codec = ast_codec_get(t->dst_codec.name, + t->dst_codec.type, t->dst_codec.sample_rate); + if (!codec) { + ast_log(LOG_ERROR, "Unable to get destination codec\n"); + destroy(pvt); + return NULL; + } + pvt->f.subclass.format = ast_format_create(codec); + ao2_ref(codec, -1); + } + + if (!pvt->f.subclass.format) { + ast_log(LOG_ERROR, "Unable to create format\n"); + destroy(pvt); + return NULL; + } + } + + return pvt; } /*! \brief framein wrapper, deals with bound checks. */ @@ -374,8 +426,9 @@ struct ast_frame *ast_trans_frameout(struct ast_trans_pvt *pvt, if (samples) { f->samples = samples; } else { - if (pvt->samples == 0) + if (pvt->samples == 0) { return NULL; + } f->samples = pvt->samples; pvt->samples = 0; } @@ -386,13 +439,6 @@ struct ast_frame *ast_trans_frameout(struct ast_trans_pvt *pvt, pvt->datalen = 0; } - f->frametype = AST_FRAME_VOICE; - ast_format_copy(&f->subclass.format, &pvt->t->dst_format); - f->mallocd = 0; - f->offset = AST_FRIENDLY_OFFSET; - f->src = pvt->t->name; - f->data.ptr = pvt->outbuf.c; - return ast_frisolate(f); } @@ -417,11 +463,9 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a { struct ast_trans_pvt *head = NULL, *tail = NULL; int src_index, dst_index; - struct ast_format tmp_fmt1; - struct ast_format tmp_fmt2; - src_index = format2index(src->id); - dst_index = format2index(dst->id); + src_index = format2index(src); + dst_index = format2index(dst); if (src_index < 0 || dst_index < 0) { ast_log(LOG_WARNING, "No translator path: (%s codec is not valid)\n", src_index < 0 ? "starting" : "ending"); @@ -432,26 +476,16 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a while (src_index != dst_index) { struct ast_trans_pvt *cur; - struct ast_format *explicit_dst = NULL; struct ast_translator *t = matrix_get(src_index, dst_index)->step; if (!t) { - int src_id = index2format(src_index); - int dst_id = index2format(dst_index); ast_log(LOG_WARNING, "No translator path from %s to %s\n", - ast_getformatname(ast_format_set(&tmp_fmt1, src_id, 0)), - ast_getformatname(ast_format_set(&tmp_fmt2, dst_id, 0))); + ast_format_get_name(src), ast_format_get_name(dst)); AST_RWLIST_UNLOCK(&translators); return NULL; } - if (dst_index == t->dst_fmt_index) { - explicit_dst = dst; - } - if (!(cur = newpvt(t, explicit_dst))) { - int src_id = index2format(src_index); - int dst_id = index2format(dst_index); + if (!(cur = newpvt(t))) { ast_log(LOG_WARNING, "Failed to build translator step from %s to %s\n", - ast_getformatname(ast_format_set(&tmp_fmt1, src_id, 0)), - ast_getformatname(ast_format_set(&tmp_fmt2, dst_id, 0))); + ast_format_get_name(src), ast_format_get_name(dst)); if (head) { ast_translator_free_path(head); } @@ -508,7 +542,8 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, path->nextout = f->delivery; } /* Predict next incoming sample */ - path->nextin = ast_tvadd(path->nextin, ast_samp2tv(f->samples, ast_format_rate(&f->subclass.format))); + path->nextin = ast_tvadd(path->nextin, ast_samp2tv( + f->samples, ast_format_get_sample_rate(f->subclass.format))); } delivery = f->delivery; for (out = f; out && p ; p = p->next) { @@ -531,7 +566,8 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, /* Predict next outgoing timestamp from samples in this frame. */ - path->nextout = ast_tvadd(path->nextout, ast_samp2tv(out->samples, ast_format_rate(&out->subclass.format))); + path->nextout = ast_tvadd(path->nextout, ast_samp2tv( + out->samples, ast_format_get_sample_rate(out->subclass.format))); if (f->samples != out->samples && ast_test_flag(out, AST_FRFLAG_HAS_TIMING_INFO)) { ast_debug(4, "Sample size different %d vs %d\n", f->samples, out->samples); ast_clear_flag(out, AST_FRFLAG_HAS_TIMING_INFO); @@ -572,7 +608,7 @@ static void generate_computational_cost(struct ast_translator *t, int seconds) struct rusage start; struct rusage end; int cost; - int out_rate = ast_format_rate(&t->dst_format); + int out_rate = t->dst_codec.sample_rate; if (!seconds) { seconds = 1; @@ -585,7 +621,7 @@ static void generate_computational_cost(struct ast_translator *t, int seconds) return; } - pvt = newpvt(t, NULL); + pvt = newpvt(t); if (!pvt) { ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name); t->comp_cost = 999999; @@ -643,21 +679,23 @@ static void generate_computational_cost(struct ast_translator *t, int seconds) * \retval Table Cost value greater than 0. * \retval 0 on error. */ -static int generate_table_cost(struct ast_format *src, struct ast_format *dst) +static int generate_table_cost(struct ast_codec *src, struct ast_codec *dst) { - int src_rate = ast_format_rate(src); + int src_rate = src->sample_rate; int src_ll = 0; - int dst_rate = ast_format_rate(dst); + int dst_rate = dst->sample_rate; int dst_ll = 0; - if ((AST_FORMAT_GET_TYPE(src->id) != AST_FORMAT_TYPE_AUDIO) || (AST_FORMAT_GET_TYPE(dst->id) != AST_FORMAT_TYPE_AUDIO)) { + if ((src->type != AST_MEDIA_TYPE_AUDIO) || + (dst->type != AST_MEDIA_TYPE_AUDIO)) { /* This method of generating table cost is limited to audio. * Translators for media other than audio must manually set their * table cost. */ return 0; } - src_ll = ast_format_is_slinear(src); - dst_ll = ast_format_is_slinear(dst); + + src_ll = !strcmp(src->name, "slin"); + dst_ll = !strcmp(dst->name, "slin"); if (src_ll) { if (dst_ll && (src_rate == dst_rate)) { return AST_TRANS_COST_LL_LL_ORIGSAMP; @@ -763,18 +801,25 @@ static void matrix_rebuild(int samples) /* if no step already exists between x and z OR the new cost of using the intermediate * step is cheaper, use this step. */ if (!matrix_get(x, z)->step || (newtablecost < matrix_get(x, z)->table_cost)) { - struct ast_format tmpx; - struct ast_format tmpy; - struct ast_format tmpz; matrix_get(x, z)->step = matrix_get(x, y)->step; matrix_get(x, z)->table_cost = newtablecost; matrix_get(x, z)->multistep = 1; changed++; - ast_debug(10, "Discovered %u cost path from %s to %s, via %s\n", - matrix_get(x, z)->table_cost, - ast_getformatname(ast_format_set(&tmpx, index2format(x), 0)), - ast_getformatname(ast_format_set(&tmpy, index2format(z), 0)), - ast_getformatname(ast_format_set(&tmpz, index2format(y), 0))); + + if (DEBUG_ATLEAST(10)) { + struct ast_codec *x_codec = index2codec(x); + struct ast_codec *y_codec = index2codec(y); + struct ast_codec *z_codec = index2codec(z); + + ast_log(LOG_DEBUG, + "Discovered %u cost path from %s to %s, via %s\n", + matrix_get(x, z)->table_cost, x_codec->name, + y_codec->name, z_codec->name); + + ao2_ref(x_codec, -1); + ao2_ref(y_codec, -1); + ao2_ref(z_codec, -1); + } } } } @@ -785,20 +830,26 @@ static void matrix_rebuild(int samples) } } -const char *ast_translate_path_to_str(struct ast_trans_pvt *p, struct ast_str **str) +static void codec_append_name(const struct ast_codec *codec, struct ast_str **buf) { - struct ast_trans_pvt *pn = p; - char tmp[256]; + if (codec) { + ast_str_append(buf, 0, "(%s@%u)", codec->name, codec->sample_rate); + } else { + ast_str_append(buf, 0, "(nothing)"); + } +} +const char *ast_translate_path_to_str(struct ast_trans_pvt *p, struct ast_str **str) +{ if (!p || !p->t) { return ""; } - ast_str_set(str, 0, "%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), p->t->src_format.id)); - - while ( (p = pn) ) { - pn = p->next; - ast_str_append(str, 0, "->%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), p->t->dst_format.id)); + codec_append_name(&p->t->src_codec, str); + while (p) { + ast_str_append(str, 0, "->"); + codec_append_name(&p->t->dst_codec, str); + p = p->next; } return ast_str_buffer(*str); @@ -806,24 +857,24 @@ const char *ast_translate_path_to_str(struct ast_trans_pvt *p, struct ast_str ** static char *complete_trans_path_choice(const char *line, const char *word, int pos, int state) { - int which = 0; + int i = 1, which = 0; int wordlen = strlen(word); - int i; - char *ret = NULL; - size_t len = 0; - const struct ast_format_list *format_list = ast_format_list_get(&len); + struct ast_codec *codec; - for (i = 0; i < len; i++) { - if (AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) { + while ((codec = ast_codec_get_by_id(i))) { + ++i; + if (codec->type != AST_MEDIA_TYPE_AUDIO) { + ao2_ref(codec, -1); continue; } - if (!strncasecmp(word, format_list[i].name, wordlen) && ++which > state) { - ret = ast_strdup(format_list[i].name); - break; + if (!strncasecmp(word, codec->name, wordlen) && ++which > state) { + char *res = ast_strdup(codec->name); + ao2_ref(codec, -1); + return res; } + ao2_ref(codec, -1); } - ast_format_list_destroy(format_list); - return ret; + return NULL; } static void handle_cli_recalc(struct ast_cli_args *a) @@ -847,63 +898,61 @@ static void handle_cli_recalc(struct ast_cli_args *a) static char *handle_show_translation_table(struct ast_cli_args *a) { - int x; - int y; - int i; - int k; - int curlen = 0; - int longest = 0; - int f_len; - size_t f_size = 0; - const struct ast_format_list *f_list = ast_format_list_get(&f_size); + int x, y, i, k; + int longest = 0, num_codecs = 0, curlen = 0; struct ast_str *out = ast_str_create(1024); + struct ast_codec *codec; - f_len = f_size; - AST_RWLIST_RDLOCK(&translators); - ast_cli(a->fd, " Translation times between formats (in microseconds) for one second of data\n"); - ast_cli(a->fd, " Source Format (Rows) Destination Format (Columns)\n\n"); - - /* Get the length of the longest (usable?) codec name, so we know how wide the left side should be */ - for (i = 0; i < f_len; i++) { - /* translation only applies to audio right now. */ - if (AST_FORMAT_GET_TYPE(f_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) + /* Get the length of the longest (usable?) codec name, + so we know how wide the left side should be */ + for (i = 1; (codec = ast_codec_get_by_id(i)); ao2_ref(codec, -1), ++i) { + ++num_codecs; + if (codec->type != AST_MEDIA_TYPE_AUDIO) { continue; - curlen = strlen(ast_getformatname(&f_list[i].format)); + } + curlen = strlen(codec->name); if (curlen > longest) { longest = curlen; } } - for (i = -1; i < f_len; i++) { + AST_RWLIST_RDLOCK(&translators); + ast_cli(a->fd, " Translation times between formats (in microseconds) for one second of data\n"); + ast_cli(a->fd, " Source Format (Rows) Destination Format (Columns)\n\n"); + + for (i = 0; i < num_codecs; i++) { + struct ast_codec *row = i ? ast_codec_get_by_id(i) : NULL; + x = -1; - if ((i >= 0) && ((x = format2index(f_list[i].format.id)) < 0)) { - continue; - } - /* translation only applies to audio right now. */ - if (i >= 0 && (AST_FORMAT_GET_TYPE(f_list[i].format.id) != AST_FORMAT_TYPE_AUDIO)) { + if ((i > 0) && (row->type != AST_MEDIA_TYPE_AUDIO)) { + ao2_ref(row, -1); continue; } - /*Go ahead and move to next iteration if dealing with an unknown codec*/ - if (i >= 0 && !strcmp(ast_getformatname(&f_list[i].format), "unknown")) { + + if ((i > 0) && (x = codec2index(row)) == -1) { + ao2_ref(row, -1); continue; } + ast_str_set(&out, 0, " "); - for (k = -1; k < f_len; k++) { + for (k = 0; k < num_codecs; k++) { + struct ast_codec *col = k ? ast_codec_get_by_id(k) : NULL; + y = -1; - if ((k >= 0) && ((y = format2index(f_list[k].format.id)) < 0)) { + if ((k > 0) && (col->type != AST_MEDIA_TYPE_AUDIO)) { + ao2_ref(col, -1); continue; } - /* translation only applies to audio right now. */ - if (k >= 0 && (AST_FORMAT_GET_TYPE(f_list[k].format.id) != AST_FORMAT_TYPE_AUDIO)) { - continue; - } - /*Go ahead and move to next iteration if dealing with an unknown codec*/ - if (k >= 0 && !strcmp(ast_getformatname(&f_list[k].format), "unknown")) { + + if ((k > 0) && (y = codec2index(col)) == -1) { + ao2_ref(col, -1); continue; } - if (k >= 0) { - curlen = strlen(ast_getformatname(&f_list[k].format)); + + if (k > 0) { + curlen = strlen(col->name); } + if (curlen < 5) { curlen = 5; } @@ -911,12 +960,12 @@ static char *handle_show_translation_table(struct ast_cli_args *a) if (x >= 0 && y >= 0 && matrix_get(x, y)->step) { /* Actual codec output */ ast_str_append(&out, 0, "%*u", curlen + 1, (matrix_get(x, y)->table_cost/100)); - } else if (i == -1 && k >= 0) { + } else if (i == 0 && k > 0) { /* Top row - use a dynamic size */ - ast_str_append(&out, 0, "%*s", curlen + 1, ast_getformatname(&f_list[k].format)); - } else if (k == -1 && i >= 0) { + ast_str_append(&out, 0, "%*s", curlen + 1, col->name); + } else if (k == 0 && i > 0) { /* Left column - use a static size. */ - ast_str_append(&out, 0, "%*s", longest, ast_getformatname(&f_list[i].format)); + ast_str_append(&out, 0, "%*s", longest, row->name); } else if (x >= 0 && y >= 0) { /* Codec not supported */ ast_str_append(&out, 0, "%*s", curlen + 1, "-"); @@ -924,74 +973,79 @@ static char *handle_show_translation_table(struct ast_cli_args *a) /* Upper left hand corner */ ast_str_append(&out, 0, "%*s", longest, ""); } + ao2_cleanup(col); } ast_str_append(&out, 0, "\n"); ast_cli(a->fd, "%s", ast_str_buffer(out)); + ao2_cleanup(row); } ast_free(out); AST_RWLIST_UNLOCK(&translators); - ast_format_list_destroy(f_list); return CLI_SUCCESS; } -static char *handle_show_translation_path(struct ast_cli_args *a) +static char *handle_show_translation_path(struct ast_cli_args *a, const char *codec_name, unsigned int sample_rate) { - struct ast_format input_src_format; - size_t len = 0; - int i; - const struct ast_format_list *format_list = ast_format_list_get(&len); + int i = 1; struct ast_str *str = ast_str_alloca(1024); struct ast_translator *step; - char tmp[256]; + struct ast_codec *dst_codec; + struct ast_codec *src_codec = ast_codec_get(codec_name, AST_MEDIA_TYPE_AUDIO, sample_rate); - ast_format_clear(&input_src_format); - for (i = 0; i < len; i++) { - if (AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) { - continue; - } - if (!strncasecmp(format_list[i].name, a->argv[4], strlen(format_list[i].name))) { - ast_format_copy(&input_src_format, &format_list[i].format); - } - } - - if (!input_src_format.id) { - ast_cli(a->fd, "Source codec \"%s\" is not found.\n", a->argv[4]); - ast_format_list_destroy(format_list); + if (!src_codec) { + ast_cli(a->fd, "Source codec \"%s\" is not found.\n", codec_name); return CLI_FAILURE; } AST_RWLIST_RDLOCK(&translators); - ast_cli(a->fd, "--- Translation paths SRC Codec \"%s\" sample rate %d ---\n", a->argv[4], ast_format_rate(&input_src_format)); - for (i = 0; i < len; i++) { - int src; - int dst; - if ((AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) || (format_list[i].format.id == input_src_format.id)) { + ast_cli(a->fd, "--- Translation paths SRC Codec \"%s\" sample rate %u ---\n", + codec_name, src_codec->sample_rate); + + while ((dst_codec = ast_codec_get_by_id(i))) { + int src, dst; + char src_buffer[64]; + char dst_buffer[64]; + + ++i; + if (src_codec == dst_codec || + dst_codec->type != AST_MEDIA_TYPE_AUDIO) { + ao2_ref(dst_codec, -1); continue; } - dst = format2index(format_list[i].format.id); - src = format2index(input_src_format.id); - ast_str_reset(str); - if ((len >= cur_max_index) && (src >= 0) && (dst >= 0) && matrix_get(src, dst)->step) { - ast_str_append(&str, 0, "%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), matrix_get(src, dst)->step->src_format.id)); - while (src != dst) { - step = matrix_get(src, dst)->step; - if (!step) { - ast_str_reset(str); - break; + + dst = codec2index(dst_codec); + src = codec2index(src_codec); + + if (src < 0 || dst < 0) { + ast_str_set(&str, 0, "No Translation Path"); + } else { + step = matrix_get(src, dst)->step; + + if (step) { + codec_append_name(&step->src_codec, &str); + while (src != dst) { + src = step->dst_fmt_index; + step = matrix_get(src, dst)->step; + if (!step) { + ast_str_append(&str, 0, "->"); + codec_append_name(dst_codec, &str); + break; + } + ast_str_append(&str, 0, "->"); + codec_append_name(&step->src_codec, &str); } - ast_str_append(&str, 0, "->%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), step->dst_format.id)); - src = step->dst_fmt_index; } } - if (ast_strlen_zero(ast_str_buffer(str))) { - ast_str_set(&str, 0, "No Translation Path"); - } - ast_cli(a->fd, "\t%-10.10s To %-10.10s: %-60.60s\n", a->argv[4], format_list[i].name, ast_str_buffer(str)); + snprintf(src_buffer, sizeof(src_buffer), "%s:%u", src_codec->name, src_codec->sample_rate); + snprintf(dst_buffer, sizeof(dst_buffer), "%s:%u", dst_codec->name, dst_codec->sample_rate); + ast_cli(a->fd, "\t%-16.16s To %-16.16s: %-60.60s\n", + src_buffer, dst_buffer, ast_str_buffer(str)); + ast_str_reset(str); + ao2_ref(dst_codec, -1); } AST_RWLIST_UNLOCK(&translators); - - ast_format_list_destroy(format_list); + ao2_ref(src_codec, -1); return CLI_SUCCESS; } @@ -1009,8 +1063,10 @@ static char *handle_cli_core_show_translation(struct ast_cli_entry *e, int cmd, " with each conversion. If the argument 'recalc' is supplied along\n" " with optional number of seconds to test a new test will be performed\n" " as the chart is being displayed.\n" - " 2. 'core show translation paths [codec]'\n" - " This will display all the translation paths associated with a codec\n"; + " 2. 'core show translation paths [codec [sample_rate]]'\n" + " This will display all the translation paths associated with a codec.\n" + " If a codec has multiple sample rates, the sample rate must be\n" + " provided as well.\n"; return NULL; case CLI_GENERATE: if (a->pos == 3) { @@ -1019,14 +1075,22 @@ static char *handle_cli_core_show_translation(struct ast_cli_entry *e, int cmd, if (a->pos == 4 && !strcasecmp(a->argv[3], option[1])) { return complete_trans_path_choice(a->line, a->word, a->pos, a->n); } + /* BUGBUG - add tab completion for sample rates */ return NULL; } - if (a->argc > 5) + if (a->argc > 6) return CLI_SHOWUSAGE; if (a->argv[3] && !strcasecmp(a->argv[3], option[1]) && a->argc == 5) { /* show paths */ - return handle_show_translation_path(a); + return handle_show_translation_path(a, a->argv[4], 0); + } else if (a->argv[3] && !strcasecmp(a->argv[3], option[1]) && a->argc == 6) { + unsigned int sample_rate; + if (sscanf(a->argv[5], "%30u", &sample_rate) != 1) { + ast_cli(a->fd, "Invalid sample rate: %s.\n", a->argv[5]); + return CLI_FAILURE; + } + return handle_show_translation_path(a, a->argv[4], sample_rate); } else if (a->argv[3] && !strcasecmp(a->argv[3], option[0])) { /* recalc and then fall through to show table */ handle_cli_recalc(a); } else if (a->argc > 3) { /* wrong input */ @@ -1045,14 +1109,29 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) { struct ast_translator *u; char tmp[80]; + RAII_VAR(struct ast_codec *, src_codec, NULL, ao2_cleanup); + RAII_VAR(struct ast_codec *, dst_codec, NULL, ao2_cleanup); - if (add_format2index(t->src_format.id) || add_format2index(t->dst_format.id)) { + src_codec = ast_codec_get(t->src_codec.name, t->src_codec.type, t->src_codec.sample_rate); + if (!src_codec) { + ast_assert(0); + ast_log(LOG_WARNING, "Failed to register translator: unknown source codec %s\n", t->src_codec.name); + return -1; + } + + dst_codec = ast_codec_get(t->dst_codec.name, t->dst_codec.type, t->dst_codec.sample_rate); + if (!dst_codec) { + ast_log(LOG_WARNING, "Failed to register translator: unknown destination codec %s\n", t->dst_codec.name); + return -1; + } + + if (add_codec2index(src_codec) || add_codec2index(dst_codec)) { if (matrix_resize(0)) { ast_log(LOG_WARNING, "Translator matrix can not represent any more translators. Out of resources.\n"); return -1; } - add_format2index(t->src_format.id); - add_format2index(t->dst_format.id); + add_codec2index(src_codec); + add_codec2index(dst_codec); } if (!mod) { @@ -1064,15 +1143,15 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) ast_log(LOG_WARNING, "empty buf size, you need to supply one\n"); return -1; } - if (!t->table_cost && !(t->table_cost = generate_table_cost(&t->src_format, &t->dst_format))) { + if (!t->table_cost && !(t->table_cost = generate_table_cost(src_codec, dst_codec))) { ast_log(LOG_WARNING, "Table cost could not be generated for %s, " "Please set table_cost variable on translator.\n", t->name); return -1; } t->module = mod; - t->src_fmt_index = format2index(t->src_format.id); - t->dst_fmt_index = format2index(t->dst_format.id); + t->src_fmt_index = codec2index(src_codec); + t->dst_fmt_index = codec2index(dst_codec); t->active = 1; if (t->src_fmt_index < 0 || t->dst_fmt_index < 0) { @@ -1080,12 +1159,12 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) return -1; } if (t->src_fmt_index >= cur_max_index) { - ast_log(LOG_WARNING, "Source format %s is larger than cur_max_index\n", ast_getformatname(&t->src_format)); + ast_log(LOG_WARNING, "Source codec %s is larger than cur_max_index\n", t->src_codec.name); return -1; } if (t->dst_fmt_index >= cur_max_index) { - ast_log(LOG_WARNING, "Destination format %s is larger than cur_max_index\n", ast_getformatname(&t->dst_format)); + ast_log(LOG_WARNING, "Destination codec %s is larger than cur_max_index\n", t->dst_codec.name); return -1; } @@ -1106,9 +1185,9 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) generate_computational_cost(t, 1); - ast_verb(2, "Registered translator '%s' from format %s to %s, table cost, %d, computational cost %d\n", - term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)), - ast_getformatname(&t->src_format), ast_getformatname(&t->dst_format), t->table_cost, t->comp_cost); + ast_verb(2, "Registered translator '%s' from codec %s to %s, table cost, %d, computational cost %d\n", + term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)), + t->src_codec.name, t->dst_codec.name, t->table_cost, t->comp_cost); AST_RWLIST_WRLOCK(&translators); @@ -1125,7 +1204,7 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) } AST_RWLIST_TRAVERSE_SAFE_END; - /* if no existing translator was found for this format combination, + /* if no existing translator was found for this codec combination, add it to the beginning of the list */ if (t) { AST_RWLIST_INSERT_HEAD(&translators, t, list); @@ -1149,10 +1228,9 @@ int ast_unregister_translator(struct ast_translator *t) AST_RWLIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) { if (u == t) { AST_RWLIST_REMOVE_CURRENT(list); - ast_verb(2, "Unregistered translator '%s' from format %s to %s\n", + ast_verb(2, "Unregistered translator '%s' from codec %s to %s\n", term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)), - ast_getformatname(&t->src_format), - ast_getformatname(&t->dst_format)); + t->src_codec.name, t->dst_codec.name); found = 1; break; } @@ -1187,91 +1265,105 @@ void ast_translator_deactivate(struct ast_translator *t) /*! \brief Calculate our best translator source format, given costs, and a desired destination */ int ast_translator_best_choice(struct ast_format_cap *dst_cap, struct ast_format_cap *src_cap, - struct ast_format *dst_fmt_out, - struct ast_format *src_fmt_out) + struct ast_format **dst_fmt_out, + struct ast_format **src_fmt_out) { - struct ast_format best; - struct ast_format_cap *joint_cap = ast_format_cap_joint(dst_cap, src_cap); + unsigned int besttablecost = INT_MAX; + unsigned int beststeps = INT_MAX; + RAII_VAR(struct ast_format *, best, NULL, ao2_cleanup); + RAII_VAR(struct ast_format *, bestdst, NULL, ao2_cleanup); + RAII_VAR(struct ast_format_cap *, joint_cap, NULL, ao2_cleanup); + int i; + int j; - ast_format_clear(&best); + if (!(joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { + return -1; + } + ast_format_cap_get_compatible(dst_cap, src_cap, joint_cap); - if (joint_cap) { /* yes, pick one and return */ - struct ast_format tmp_fmt; + for (i = 0; i < ast_format_cap_count(joint_cap); ++i) { + struct ast_format *fmt = + ast_format_cap_get_format(joint_cap, i); - ast_format_cap_iter_start(joint_cap); - while (!ast_format_cap_iter_next(joint_cap, &tmp_fmt)) { - /* We are guaranteed to find one common format. */ - if (!best.id) { - ast_format_copy(&best, &tmp_fmt); - continue; - } - /* If there are multiple common formats, pick the one with the highest sample rate */ - if (ast_format_rate(&best) < ast_format_rate(&tmp_fmt)) { - ast_format_copy(&best, &tmp_fmt); - continue; - } + if (!fmt) { + continue; + } + + if (!best) { + /* No ao2_ref operations needed, we're done with fmt */ + best = fmt; + continue; } - ast_format_cap_iter_end(joint_cap); - /* We are done, this is a common format to both. */ - ast_format_copy(dst_fmt_out, &best); - ast_format_copy(src_fmt_out, &best); - ast_format_cap_destroy(joint_cap); + if (ast_format_get_sample_rate(best) < + ast_format_get_sample_rate(fmt)) { + ao2_replace(best, fmt); + } + ao2_ref(fmt, -1); + } + + if (best) { + ao2_replace(*dst_fmt_out, best); + ao2_replace(*src_fmt_out, best); return 0; - } else { /* No, we will need to translate */ - unsigned int besttablecost = INT_MAX; - unsigned int beststeps = INT_MAX; - struct ast_format cur_dst; - struct ast_format cur_src; - struct ast_format bestdst; + } + /* need to translate */ + AST_RWLIST_RDLOCK(&translators); - ast_format_clear(&bestdst); + for (i = 0; i < ast_format_cap_count(dst_cap); ++i) { + struct ast_format *dst = + ast_format_cap_get_format(dst_cap, i); - AST_RWLIST_RDLOCK(&translators); - ast_format_cap_iter_start(dst_cap); - while (!ast_format_cap_iter_next(dst_cap, &cur_dst)) { - ast_format_cap_iter_start(src_cap); - while (!ast_format_cap_iter_next(src_cap, &cur_src)) { - int x = format2index(cur_src.id); - int y = format2index(cur_dst.id); - struct translator_path *trans; - - if (x < 0 || y < 0) { - continue; - } - trans = matrix_get(x, y); - if (!trans || !trans->step) { - continue; - } - if (trans->table_cost < besttablecost || trans->multistep < beststeps) { - /* better than what we have so far */ - ast_format_copy(&best, &cur_src); - ast_format_copy(&bestdst, &cur_dst); - besttablecost = trans->table_cost; - beststeps = trans->multistep; - } - } - ast_format_cap_iter_end(src_cap); + if (!dst) { + continue; } - ast_format_cap_iter_end(dst_cap); - AST_RWLIST_UNLOCK(&translators); - if (best.id) { - ast_format_copy(dst_fmt_out, &bestdst); - ast_format_copy(src_fmt_out, &best); - return 0; + for (j = 0; j < ast_format_cap_count(src_cap); ++j) { + struct ast_format *src = + ast_format_cap_get_format(src_cap, j); + int x, y; + + if (!src) { + continue; + } + + x = format2index(src); + y = format2index(dst); + if (x < 0 || y < 0) { + ao2_ref(src, -1); + continue; + } + if (!matrix_get(x, y) || !(matrix_get(x, y)->step)) { + ao2_ref(src, -1); + continue; + } + if (((matrix_get(x, y)->table_cost < besttablecost) || + (matrix_get(x, y)->multistep < beststeps))) { + /* better than what we have so far */ + ao2_replace(best, src); + ao2_replace(bestdst, dst); + besttablecost = matrix_get(x, y)->table_cost; + beststeps = matrix_get(x, y)->multistep; + } + ao2_ref(src, -1); } + ao2_ref(dst, -1); + } + AST_RWLIST_UNLOCK(&translators); + if (!best) { return -1; } + ao2_replace(*dst_fmt_out, bestdst); + ao2_replace(*src_fmt_out, best); + return 0; } unsigned int ast_translate_path_steps(struct ast_format *dst_format, struct ast_format *src_format) { unsigned int res = -1; - int src, dest; /* convert bitwise format numbers into array indices */ - src = format2index(src_format->id); - dest = format2index(dst_format->id); + int src = format2index(src_format); + int dest = format2index(dst_format); if (src < 0 || dest < 0) { ast_log(LOG_WARNING, "No translator path: (%s codec is not valid)\n", src < 0 ? "starting" : "ending"); @@ -1288,25 +1380,72 @@ unsigned int ast_translate_path_steps(struct ast_format *dst_format, struct ast_ return res; } +static void check_translation_path( + struct ast_format_cap *dest, struct ast_format_cap *src, + struct ast_format_cap *result, struct ast_format *src_fmt, + enum ast_media_type type) +{ + int index, src_index = format2index(src_fmt); + /* For a given source format, traverse the list of + known formats to determine whether there exists + a translation path from the source format to the + destination format. */ + for (index = 0; (src_index >= 0) && index < cur_max_index; index++) { + struct ast_codec *codec = index2codec(index); + RAII_VAR(struct ast_format *, fmt, ast_format_create(codec), ao2_cleanup); + + ao2_ref(codec, -1); + + if (ast_format_get_type(fmt) != type) { + continue; + } + + /* if this is not a desired format, nothing to do */ + if (ast_format_cap_iscompatible_format(dest, fmt) == AST_FORMAT_CMP_NOT_EQUAL) { + continue; + } + + /* if the source is supplying this format, then + we can leave it in the result */ + if (ast_format_cap_iscompatible_format(src, fmt) == AST_FORMAT_CMP_EQUAL) { + continue; + } + + /* if we don't have a translation path from the src + to this format, remove it from the result */ + if (!matrix_get(src_index, index)->step) { + ast_format_cap_remove(result, fmt); + continue; + } + + /* now check the opposite direction */ + if (!matrix_get(index, src_index)->step) { + ast_format_cap_remove(result, fmt); + } + } + +} + void ast_translate_available_formats(struct ast_format_cap *dest, struct ast_format_cap *src, struct ast_format_cap *result) { - struct ast_format tmp_fmt; - struct ast_format cur_dest, cur_src; - int src_audio = 0; - int src_video = 0; + struct ast_format *cur_dest, *cur_src; int index; - ast_format_cap_iter_start(dest); - while (!ast_format_cap_iter_next(dest, &cur_dest)) { + for (index = 0; index < ast_format_cap_count(dest); ++index) { + if (!(cur_dest = ast_format_cap_get_format(dest, index))) { + continue; + } + /* We give preference to a joint format structure if possible */ - if (ast_format_cap_get_compatible_format(src, &cur_dest, &tmp_fmt)) { - ast_format_cap_add(result, &tmp_fmt); + if ((cur_src = ast_format_cap_get_compatible_format(src, cur_dest))) { + ast_format_cap_append(result, cur_src, 0); + ao2_ref(cur_src, -1); } else { /* Otherwise we just use the destination format */ - ast_format_cap_add(result, &cur_dest); + ast_format_cap_append(result, cur_dest, 0); } + ao2_ref(cur_dest, -1); } - ast_format_cap_iter_end(dest); /* if we don't have a source format, we just have to try all possible destination formats */ @@ -1314,91 +1453,19 @@ void ast_translate_available_formats(struct ast_format_cap *dest, struct ast_for return; } - ast_format_cap_iter_start(src); - while (!ast_format_cap_iter_next(src, &cur_src)) { - /* If we have a source audio format, get its format index */ - if (AST_FORMAT_GET_TYPE(cur_src.id) == AST_FORMAT_TYPE_AUDIO) { - src_audio = format2index(cur_src.id); - } - - /* If we have a source video format, get its format index */ - if (AST_FORMAT_GET_TYPE(cur_src.id) == AST_FORMAT_TYPE_VIDEO) { - src_video = format2index(cur_src.id); + for (index = 0; index < ast_format_cap_count(src); ++index) { + if (!(cur_src = ast_format_cap_get_format(src, index))) { + continue; } AST_RWLIST_RDLOCK(&translators); - - /* For a given source audio format, traverse the list of - known audio formats to determine whether there exists - a translation path from the source format to the - destination format. */ - for (index = 0; (src_audio >= 0) && index < cur_max_index; index++) { - ast_format_set(&tmp_fmt, index2format(index), 0); - - if (AST_FORMAT_GET_TYPE(tmp_fmt.id) != AST_FORMAT_TYPE_AUDIO) { - continue; - } - - /* if this is not a desired format, nothing to do */ - if (!ast_format_cap_iscompatible(dest, &tmp_fmt)) { - continue; - } - - /* if the source is supplying this format, then - we can leave it in the result */ - if (ast_format_cap_iscompatible(src, &tmp_fmt)) { - continue; - } - - /* if we don't have a translation path from the src - to this format, remove it from the result */ - if (!matrix_get(src_audio, index)->step) { - ast_format_cap_remove_byid(result, tmp_fmt.id); - continue; - } - - /* now check the opposite direction */ - if (!matrix_get(index, src_audio)->step) { - ast_format_cap_remove_byid(result, tmp_fmt.id); - } - } - - /* For a given source video format, traverse the list of - known video formats to determine whether there exists - a translation path from the source format to the - destination format. */ - for (index = 0; (src_video >= 0) && index < cur_max_index; index++) { - ast_format_set(&tmp_fmt, index2format(index), 0); - if (AST_FORMAT_GET_TYPE(tmp_fmt.id) != AST_FORMAT_TYPE_VIDEO) { - continue; - } - - /* if this is not a desired format, nothing to do */ - if (!ast_format_cap_iscompatible(dest, &tmp_fmt)) { - continue; - } - - /* if the source is supplying this format, then - we can leave it in the result */ - if (ast_format_cap_iscompatible(src, &tmp_fmt)) { - continue; - } - - /* if we don't have a translation path from the src - to this format, remove it from the result */ - if (!matrix_get(src_video, index)->step) { - ast_format_cap_remove_byid(result, tmp_fmt.id); - continue; - } - - /* now check the opposite direction */ - if (!matrix_get(index, src_video)->step) { - ast_format_cap_remove_byid(result, tmp_fmt.id); - } - } + check_translation_path(dest, src, result, + cur_src, AST_MEDIA_TYPE_AUDIO); + check_translation_path(dest, src, result, + cur_src, AST_MEDIA_TYPE_VIDEO); AST_RWLIST_UNLOCK(&translators); + ao2_ref(cur_src, -1); } - ast_format_cap_iter_end(src); } static void translate_shutdown(void) diff --git a/main/utils.c b/main/utils.c index 0097487a3..a916e79c8 100644 --- a/main/utils.c +++ b/main/utils.c @@ -1054,10 +1054,10 @@ struct ast_str *ast_dump_locks(void) if (!header_printed) { if (lock_info->lwp != -1) { ast_str_append(&str, 0, "=== Thread ID: 0x%lx LWP:%d (%s)\n", - (long) lock_info->thread_id, lock_info->lwp, lock_info->thread_name); + (long unsigned) lock_info->thread_id, lock_info->lwp, lock_info->thread_name); } else { ast_str_append(&str, 0, "=== Thread ID: 0x%lx (%s)\n", - (long) lock_info->thread_id, lock_info->thread_name); + (long unsigned) lock_info->thread_id, lock_info->thread_name); } header_printed = 1; } |