diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/asterisk.c | 3 | ||||
-rw-r--r-- | main/audiohook.c | 265 | ||||
-rw-r--r-- | main/bridging.c | 4 | ||||
-rw-r--r-- | main/channel.c | 20 | ||||
-rw-r--r-- | main/data.c | 13 | ||||
-rw-r--r-- | main/format.c | 796 | ||||
-rw-r--r-- | main/format_cap.c | 99 | ||||
-rw-r--r-- | main/format_pref.c | 70 | ||||
-rw-r--r-- | main/frame.c | 285 | ||||
-rw-r--r-- | main/rtp_engine.c | 334 | ||||
-rw-r--r-- | main/slinfactory.c | 19 | ||||
-rw-r--r-- | main/translate.c | 120 |
12 files changed, 1489 insertions, 539 deletions
diff --git a/main/asterisk.c b/main/asterisk.c index ad8b38103..846848da8 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -143,6 +143,7 @@ int daemon(int, int); /* defined in libresolv of all places */ #include "asterisk/poll-compat.h" #include "asterisk/ccss.h" #include "asterisk/test.h" +#include "asterisk/rtp_engine.h" #include "asterisk/format.h" #include "asterisk/aoc.h" @@ -3708,6 +3709,8 @@ int main(int argc, char *argv[]) astobj2_init(); ast_format_attr_init(); + ast_format_list_init(); + ast_rtp_engine_init(); ast_autoservice_init(); diff --git a/main/audiohook.c b/main/audiohook.c index 6b2df6416..9fd2ca957 100644 --- a/main/audiohook.c +++ b/main/audiohook.c @@ -38,12 +38,22 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/frame.h" #include "asterisk/translate.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_audiohook_list { + /* If all the audiohooks in this list are capable + * of processing slinear at any sample rate, this + * variable will be set and the sample rate will + * be preserved during ast_audiohook_write_list()*/ + int native_slin_compatible; + int list_internal_samp_rate;/*!< Internal sample rate used when writing to the audiohook list */ + struct ast_audiohook_translate in_translate[2]; struct ast_audiohook_translate out_translate[2]; AST_LIST_HEAD_NOLOCK(, ast_audiohook) spy_list; @@ -51,13 +61,44 @@ struct ast_audiohook_list { AST_LIST_HEAD_NOLOCK(, ast_audiohook) manipulate_list; }; +static int audiohook_set_internal_rate(struct ast_audiohook *audiohook, int rate, int reset) +{ + struct ast_format slin; + + if (audiohook->hook_internal_samp_rate == rate) { + return 0; + } + + audiohook->hook_internal_samp_rate = rate; + + ast_format_set(&slin, ast_format_slin_by_rate(rate), 0); + /* Setup the factories that are needed for this audiohook type */ + switch (audiohook->type) { + case AST_AUDIOHOOK_TYPE_SPY: + if (reset) { + ast_slinfactory_destroy(&audiohook->read_factory); + } + ast_slinfactory_init_with_format(&audiohook->read_factory, &slin); + /* fall through */ + case AST_AUDIOHOOK_TYPE_WHISPER: + if (reset) { + ast_slinfactory_destroy(&audiohook->write_factory); + } + ast_slinfactory_init_with_format(&audiohook->write_factory, &slin); + break; + default: + break; + } + return 0; +} + /*! \brief Initialize an audiohook structure * \param audiohook Audiohook structure * \param type * \param source * \return Returns 0 on success, -1 on failure */ -int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source) +int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source, enum ast_audiohook_init_flags init_flags) { /* Need to keep the type and source */ audiohook->type = type; @@ -67,16 +108,10 @@ int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type ast_mutex_init(&audiohook->lock); ast_cond_init(&audiohook->trigger, NULL); - /* Setup the factories that are needed for this audiohook type */ - switch (type) { - case AST_AUDIOHOOK_TYPE_SPY: - ast_slinfactory_init(&audiohook->read_factory); - case AST_AUDIOHOOK_TYPE_WHISPER: - ast_slinfactory_init(&audiohook->write_factory); - break; - default: - break; - } + audiohook->init_flags = init_flags; + + /* initialize internal rate at 8khz, this will adjust if necessary */ + audiohook_set_internal_rate(audiohook, 8000, 0); /* Since we are just starting out... this audiohook is new */ ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_NEW); @@ -133,9 +168,9 @@ int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohoo *rwtime = ast_tvnow(); our_factory_samples = ast_slinfactory_available(factory); - our_factory_ms = ast_tvdiff_ms(*rwtime, previous_time) + (our_factory_samples / 8); + our_factory_ms = ast_tvdiff_ms(*rwtime, previous_time) + (our_factory_samples / (audiohook->hook_internal_samp_rate / 1000)); other_factory_samples = ast_slinfactory_available(other_factory); - other_factory_ms = other_factory_samples / 8; + other_factory_ms = other_factory_samples / (audiohook->hook_internal_samp_rate / 1000); if (ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC) && other_factory_samples && (our_factory_ms - other_factory_ms > AST_AUDIOHOOK_SYNC_TOLERANCE)) { ast_debug(1, "Flushing audiohook %p so it remains in sync\n", audiohook); @@ -143,7 +178,7 @@ int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohoo ast_slinfactory_flush(other_factory); } - if (ast_test_flag(audiohook, AST_AUDIOHOOK_SMALL_QUEUE) && (our_factory_samples > 640 || other_factory_samples > 640)) { + if (ast_test_flag(audiohook, AST_AUDIOHOOK_SMALL_QUEUE) && ((our_factory_ms > AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE) || (other_factory_ms > AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE))) { ast_debug(1, "Audiohook %p has stale audio in its factories. Flushing them both\n", audiohook); ast_slinfactory_flush(factory); ast_slinfactory_flush(other_factory); @@ -186,7 +221,7 @@ static struct ast_frame *audiohook_read_frame_single(struct ast_audiohook *audio .datalen = sizeof(buf), .samples = samples, }; - ast_format_set(&frame.subclass.format, AST_FORMAT_SLINEAR, 0); + 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)) @@ -213,7 +248,7 @@ 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_SLINEAR, 0); + 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); @@ -296,7 +331,7 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho /*! \brief Reads a frame in from the audiohook structure * \param audiohook Audiohook structure - * \param samples Number of samples wanted + * \param samples Number of samples wanted in requested output format * \param direction Direction the audio frame came from * \param format Format of frame remote side wants back * \return Returns frame on success, NULL on failure @@ -305,23 +340,39 @@ struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size { struct ast_frame *read_frame = NULL, *final_frame = NULL; struct ast_format tmp_fmt; + 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)) { + 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 { + samples_converted = samples * (ast_format_rate(format) / (float) audiohook->hook_internal_samp_rate); + } - if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ? audiohook_read_frame_both(audiohook, samples) : audiohook_read_frame_single(audiohook, samples, direction)))) + if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ? + audiohook_read_frame_both(audiohook, samples_converted) : + audiohook_read_frame_single(audiohook, samples_converted, direction)))) { return NULL; + } /* If they don't want signed linear back out, we'll have to send it through the translation path */ - if (format->id != AST_FORMAT_SLINEAR) { + if (format->id != ast_format_slin_by_rate(audiohook->hook_internal_samp_rate)) { /* Rebuild translation path if different format then previously */ 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_SLINEAR, 0)))) { + 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)))) { ast_frfree(read_frame); return NULL; } + ast_format_copy(&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); @@ -332,6 +383,18 @@ struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size return final_frame; } +static void audiohook_list_set_samplerate_compatibility(struct ast_audiohook_list *audiohook_list) +{ + struct ast_audiohook *ah = NULL; + audiohook_list->native_slin_compatible = 1; + AST_LIST_TRAVERSE(&audiohook_list->manipulate_list, ah, list) { + if (!(ah->init_flags & AST_AUDIOHOOK_MANIPULATE_ALL_RATES)) { + audiohook_list->native_slin_compatible = 0; + return; + } + } +} + /*! \brief Attach audiohook to channel * \param chan Channel * \param audiohook Audiohook structure @@ -350,6 +413,8 @@ int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audioho AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->spy_list); AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->whisper_list); AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->manipulate_list); + /* This sample rate will adjust as necessary when writing to the list. */ + chan->audiohooks->list_internal_samp_rate = 8000; } /* Drop into respective list */ @@ -360,6 +425,10 @@ int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audioho else if (audiohook->type == AST_AUDIOHOOK_TYPE_MANIPULATE) AST_LIST_INSERT_TAIL(&chan->audiohooks->manipulate_list, audiohook, list); + + audiohook_set_internal_rate(audiohook, chan->audiohooks->list_internal_samp_rate, 1); + audiohook_list_set_samplerate_compatibility(chan->audiohooks); + /* Change status over to running since it is now attached */ ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_RUNNING); @@ -546,6 +615,7 @@ int ast_audiohook_remove(struct ast_channel *chan, struct ast_audiohook *audioho else if (audiohook->type == AST_AUDIOHOOK_TYPE_MANIPULATE) AST_LIST_REMOVE(&chan->audiohooks->manipulate_list, audiohook, list); + audiohook_list_set_samplerate_compatibility(chan->audiohooks); ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE); ast_channel_unlock(chan); @@ -563,11 +633,13 @@ int ast_audiohook_remove(struct ast_channel *chan, struct ast_audiohook *audioho static struct ast_frame *dtmf_audiohook_write_list(struct ast_channel *chan, struct ast_audiohook_list *audiohook_list, enum ast_audiohook_direction direction, struct ast_frame *frame) { struct ast_audiohook *audiohook = NULL; + int removed = 0; AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) { ast_audiohook_lock(audiohook); if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) { AST_LIST_REMOVE_CURRENT(list); + removed = 1; ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE); ast_audiohook_unlock(audiohook); audiohook->manipulate_callback(audiohook, NULL, NULL, 0); @@ -579,9 +651,77 @@ static struct ast_frame *dtmf_audiohook_write_list(struct ast_channel *chan, str } AST_LIST_TRAVERSE_SAFE_END; + /* if an audiohook got removed, reset samplerate compatibility */ + if (removed) { + audiohook_list_set_samplerate_compatibility(audiohook_list); + } return frame; } +static struct ast_frame *audiohook_list_translate_to_slin(struct ast_audiohook_list *audiohook_list, + enum ast_audiohook_direction direction, struct ast_frame *frame) +{ + 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; + + /* If we are capable of maintaining doing samplerates other that 8khz, update + * the internal audiohook_list's rate and higher samplerate audio arrives. By + * updating the list's rate, all the audiohooks in the list will be updated as well + * 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); + } + + slin_id = ast_format_slin_by_rate(audiohook_list->list_internal_samp_rate); + + if (frame->subclass.format.id == slin_id) { + return new_frame; + } + + 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))) { + return NULL; + } + ast_format_copy(&in_translate->format, &frame->subclass.format); + } + if (!(new_frame = ast_translate(in_translate->trans_pvt, frame, 0))) { + return NULL; + } + + return new_frame; +} + +static struct ast_frame *audiohook_list_translate_to_native(struct ast_audiohook_list *audiohook_list, + enum ast_audiohook_direction direction, struct ast_frame *slin_frame, struct ast_format *outformat) +{ + 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) { + /* rebuild translators if necessary */ + 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))) { + return NULL; + } + ast_format_copy(&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))) { + return NULL; + } + } + return outframe; +} + /*! * \brief Pass an AUDIO frame off to be handled by the audiohook core * @@ -595,15 +735,9 @@ static struct ast_frame *dtmf_audiohook_write_list(struct ast_channel *chan, str * SLINEAR format for Part_2. * Part_2: Send middle_frame off to spies and manipulators. At this point middle_frame is * either a new frame as result of the translation, or points directly to the start_frame - * because no translation to SLINEAR audio was required. The result of this part - * is end_frame will be updated to point to middle_frame if any audiohook manipulation - * took place. - * Part_3: Translate end_frame's audio back into the format of start frame if necessary. - * At this point if middle_frame != end_frame, we are guaranteed that no manipulation - * took place and middle_frame can be freed as it was translated... If middle_frame was - * not translated and still pointed to start_frame, it would be equal to end_frame as well - * regardless if manipulation took place which would not result in this free. The result - * of this part is end_frame is guaranteed to be the format of start_frame for the return. + * because no translation to SLINEAR audio was required. + * Part_3: Translate end_frame's audio back into the format of start frame if necessary. This + * is only necessary if manipulation of middle_frame occurred. * * \param chan Channel that the list is coming off of * \param audiohook_list List of audiohooks @@ -613,27 +747,17 @@ static struct ast_frame *dtmf_audiohook_write_list(struct ast_channel *chan, str */ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, struct ast_audiohook_list *audiohook_list, enum ast_audiohook_direction direction, struct ast_frame *frame) { - struct ast_audiohook_translate *in_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->in_translate[0] : &audiohook_list->in_translate[1]); - struct ast_audiohook_translate *out_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->out_translate[0] : &audiohook_list->out_translate[1]); struct ast_frame *start_frame = frame, *middle_frame = frame, *end_frame = frame; struct ast_audiohook *audiohook = NULL; - struct ast_format tmp_fmt; - int samples = frame->samples; + int samples; + int middle_frame_manipulated = 0; + int removed = 0; /* ---Part_1. translate start_frame to SLINEAR if necessary. */ - /* If the frame coming in is not signed linear we have to send it through the in_translate path */ - if (frame->subclass.format.id != AST_FORMAT_SLINEAR) { - if (ast_format_cmp(&frame->subclass.format, &in_translate->format) == AST_FORMAT_CMP_NOT_EQUAL) { - if (in_translate->trans_pvt) - ast_translator_free_path(in_translate->trans_pvt); - if (!(in_translate->trans_pvt = ast_translator_build_path(ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0), &frame->subclass.format))) - return frame; - ast_format_copy(&in_translate->format, &frame->subclass.format); - } - if (!(middle_frame = ast_translate(in_translate->trans_pvt, frame, 0))) - return frame; - samples = middle_frame->samples; + if (!(middle_frame = audiohook_list_translate_to_slin(audiohook_list, direction, start_frame))) { + return frame; } + samples = middle_frame->samples; /* ---Part_2: Send middle_frame to spy and manipulator lists. middle_frame is guaranteed to be SLINEAR here.*/ /* Queue up signed linear frame to each spy */ @@ -641,10 +765,12 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st ast_audiohook_lock(audiohook); if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) { AST_LIST_REMOVE_CURRENT(list); + removed = 1; ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE); ast_audiohook_unlock(audiohook); continue; } + audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1); ast_audiohook_write_frame(audiohook, direction, middle_frame); ast_audiohook_unlock(audiohook); } @@ -659,10 +785,12 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st ast_audiohook_lock(audiohook); if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) { AST_LIST_REMOVE_CURRENT(list); + removed = 1; ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE); ast_audiohook_unlock(audiohook); continue; } + audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1); if (ast_slinfactory_available(&audiohook->write_factory) >= samples && ast_slinfactory_read(&audiohook->write_factory, read_buf, samples)) { /* Take audio from this whisper source and combine it into our main buffer */ for (i = 0, data1 = combine_buf, data2 = read_buf; i < samples; i++, data1++, data2++) @@ -672,9 +800,10 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st } AST_LIST_TRAVERSE_SAFE_END; /* We take all of the combined whisper sources and combine them into the audio being written out */ - for (i = 0, data1 = middle_frame->data.ptr, data2 = combine_buf; i < samples; i++, data1++, data2++) + for (i = 0, data1 = middle_frame->data.ptr, data2 = combine_buf; i < samples; i++, data1++, data2++) { ast_slinear_saturated_add(data1, data2); - end_frame = middle_frame; + } + middle_frame_manipulated = 1; } /* Pass off frame to manipulate audiohooks */ @@ -683,12 +812,14 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st ast_audiohook_lock(audiohook); if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) { AST_LIST_REMOVE_CURRENT(list); + removed = 1; ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE); ast_audiohook_unlock(audiohook); /* We basically drop all of our links to the manipulate audiohook and prod it to do it's own destructive things */ audiohook->manipulate_callback(audiohook, chan, NULL, direction); continue; } + audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1); /* Feed in frame to manipulation. */ if (audiohook->manipulate_callback(audiohook, chan, middle_frame, direction)) { /* XXX IGNORE FAILURE */ @@ -700,35 +831,27 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st ast_audiohook_unlock(audiohook); } AST_LIST_TRAVERSE_SAFE_END; - end_frame = middle_frame; + middle_frame_manipulated = 1; } /* ---Part_3: Decide what to do with the end_frame (whether to transcode or not) */ - if (middle_frame == end_frame) { - /* Middle frame was modified and became the end frame... let's see if we need to transcode */ - if (ast_format_cmp(&end_frame->subclass.format, &start_frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) { - if (ast_format_cmp(&out_translate->format, &start_frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) { - if (out_translate->trans_pvt) - ast_translator_free_path(out_translate->trans_pvt); - if (!(out_translate->trans_pvt = ast_translator_build_path(&start_frame->subclass.format, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0)))) { - /* We can't transcode this... drop our middle frame and return the original */ - ast_frfree(middle_frame); - return start_frame; - } - ast_format_copy(&out_translate->format, &start_frame->subclass.format); - } - /* Transcode from our middle (signed linear) frame to new format of the frame that came in */ - if (!(end_frame = ast_translate(out_translate->trans_pvt, middle_frame, 0))) { - /* Failed to transcode the frame... drop it and return the original */ - ast_frfree(middle_frame); - return start_frame; - } - /* Here's the scoop... middle frame is no longer of use to us */ - ast_frfree(middle_frame); + if (middle_frame_manipulated) { + 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; } } else { - /* No frame was modified, we can just drop our middle frame and pass the frame we got in out */ + end_frame = start_frame; + } + /* clean up our middle_frame if required */ + if (middle_frame != end_frame) { ast_frfree(middle_frame); + middle_frame = NULL; + } + + /* Before returning, if an audiohook got removed, reset samplerate compatibility */ + if (removed) { + audiohook_list_set_samplerate_compatibility(audiohook_list); } return end_frame; @@ -956,7 +1079,7 @@ static struct audiohook_volume *audiohook_volume_get(struct ast_channel *chan, i } /* Setup our audiohook structure so we can manipulate the audio */ - ast_audiohook_init(&audiohook_volume->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume"); + ast_audiohook_init(&audiohook_volume->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume", AST_AUDIOHOOK_MANIPULATE_ALL_RATES); audiohook_volume->audiohook.manipulate_callback = audiohook_volume_callback; /* Attach the audiohook_volume blob to the datastore and attach to the channel */ diff --git a/main/bridging.c b/main/bridging.c index 9de02aa34..f988d9694 100644 --- a/main/bridging.c +++ b/main/bridging.c @@ -721,11 +721,11 @@ static enum ast_bridge_channel_state bridge_channel_join_multithreaded(struct as /* Wait for data to either come from the channel or us to be signalled */ if (!bridge_channel->suspended) { - ast_debug(1, "Going into a multithreaded waitfor for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge); + ast_debug(10, "Going into a multithreaded waitfor for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge); chan = ast_waitfor_nandfds(&bridge_channel->chan, 1, fds, nfds, NULL, &outfd, &ms); } else { ast_mutex_lock(&bridge_channel->lock); - ast_debug(1, "Going into a multithreaded signal wait for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge); + ast_debug(10, "Going into a multithreaded signal wait for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge); ast_cond_wait(&bridge_channel->cond, &bridge_channel->lock); ast_mutex_unlock(&bridge_channel->lock); } diff --git a/main/channel.c b/main/channel.c index 093d5f619..404732823 100644 --- a/main/channel.c +++ b/main/channel.c @@ -1006,7 +1006,14 @@ struct ast_format *ast_best_codec(struct ast_format_cap *cap, struct ast_format /*! 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, @@ -1020,8 +1027,11 @@ struct ast_format *ast_best_codec(struct ast_format_cap *cap, struct ast_format /*! 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, + /*! SILK is pretty awesome. */ + AST_FORMAT_SILK, /*! Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough to use it */ AST_FORMAT_LPC10, @@ -1035,7 +1045,7 @@ struct ast_format *ast_best_codec(struct ast_format_cap *cap, struct ast_format /* Find the first preferred codec in the format given */ for (x = 0; x < ARRAY_LEN(prefs); x++) { - if (ast_format_cap_iscompatible(cap, ast_format_set(result, prefs[x], 0))) { + if (ast_format_cap_best_byid(cap, prefs[x], result)) { return result; } } @@ -5778,12 +5788,16 @@ static int ast_channel_make_compatible_helper(struct ast_channel *from, struct a * no direct conversion available. If generic PLC is * desired, then transcoding via SLINEAR is a requirement */ - use_slin = (best_src_fmt.id == AST_FORMAT_SLINEAR || best_dst_fmt.id == AST_FORMAT_SLINEAR); + use_slin = ast_format_is_slinear(&best_src_fmt) || ast_format_is_slinear(&best_dst_fmt) ? 1 : 0; if ((ast_format_cmp(&best_src_fmt, &best_dst_fmt) == AST_FORMAT_CMP_NOT_EQUAL) && (ast_opt_generic_plc || ast_opt_transcode_via_slin) && (ast_translate_path_steps(&best_dst_fmt, &best_src_fmt) != 1 || use_slin)) { - ast_format_set(&best_dst_fmt, AST_FORMAT_SLINEAR, 0); + 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); + + /* pick the best signed linear format based upon what preserves the sample rate the best. */ + ast_format_set(&best_dst_fmt, ast_format_slin_by_rate(best_sample_rate), 0); } if (ast_set_read_format(from, &best_dst_fmt) < 0) { diff --git a/main/data.c b/main/data.c index 3ca2f7c27..2503cb57d 100644 --- a/main/data.c +++ b/main/data.c @@ -3111,11 +3111,12 @@ int ast_data_add_codec(struct ast_data *root, const char *node_name, struct ast_ if (!codecs) { return -1; } - fmlist = ast_get_format_list(&fmlist_size); + fmlist = ast_format_list_get(&fmlist_size); for (x = 0; x < fmlist_size; x++) { - if (fmlist[x].id == format->id) { + 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); @@ -3124,6 +3125,7 @@ int ast_data_add_codec(struct ast_data *root, const char *node_name, struct ast_ ast_data_add_int(codec, "frame_length", fmlist[x].fr_len); } } + ast_format_list_destroy(fmlist); return 0; } @@ -3133,18 +3135,18 @@ int ast_data_add_codecs(struct ast_data *root, const char *node_name, struct ast struct ast_data *codecs, *codec; size_t fmlist_size; const struct ast_format_list *fmlist; - struct ast_format tmp_fmt; int x; codecs = ast_data_add_node(root, node_name); if (!codecs) { return -1; } - fmlist = ast_get_format_list(&fmlist_size); + fmlist = ast_format_list_get(&fmlist_size); for (x = 0; x < fmlist_size; x++) { - if (ast_format_cap_iscompatible(cap, ast_format_set(&tmp_fmt, fmlist[x].id, 0))) { + 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); @@ -3153,6 +3155,7 @@ int ast_data_add_codecs(struct ast_data *root, const char *node_name, struct ast ast_data_add_int(codec, "frame_length", fmlist[x].fr_len); } } + ast_format_list_destroy(fmlist); return 0; } diff --git a/main/format.c b/main/format.c index d77d244a6..28b15ae21 100644 --- a/main/format.c +++ b/main/format.c @@ -4,6 +4,7 @@ * Copyright (C) 2010, Digium, Inc. * * David Vossel <dvossel@digium.com> + * Mark Spencer <markster@digium.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact @@ -21,6 +22,7 @@ * \brief Format API * * \author David Vossel <dvossel@digium.com> + * \author Mark Spencer <markster@digium.com> */ #include "asterisk.h" @@ -32,6 +34,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); #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" /*! This is the container for all the format attribute interfaces. * An ao2 container was chosen for fast lookup. */ @@ -51,6 +60,17 @@ struct interface_ao2_wrapper { ast_rwlock_t wraplock; }; +/*! \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; @@ -86,6 +106,23 @@ int ast_format_get_video_mark(const struct ast_format *format) return format->fattr.rtp_marker_bit; } +static int has_interface(const struct ast_format *format) +{ + struct interface_ao2_wrapper *wrapper; + struct interface_ao2_wrapper tmp_wrapper = { + .id = format->id, + }; + + ast_rwlock_rdlock(&ilock); + if (!(wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_NOLOCK)))) { + ast_rwlock_unlock(&ilock); + return 0; + } + ast_rwlock_unlock(&ilock); + ao2_ref(wrapper, -1); + return 1; +} + static struct interface_ao2_wrapper *find_interface(const struct ast_format *format) { struct interface_ao2_wrapper *wrapper; @@ -166,7 +203,7 @@ void ast_format_clear(struct ast_format *format) /*! \internal * \brief determine if a list of attribute key value pairs are set on a format */ -static int format_isset_helper(struct ast_format *format, va_list ap) +static int format_isset_helper(const struct ast_format *format, va_list ap) { int res; struct interface_ao2_wrapper *wrapper; @@ -189,18 +226,24 @@ static int format_isset_helper(struct ast_format *format, va_list ap) return -1; } - wrapper->interface->format_attr_set(&tmp.fattr, ap); - - /* use our tmp structure to tell if the attributes are set or not */ - res = wrapper->interface->format_attr_cmp(&tmp.fattr, &format->fattr); + /* 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; + } ast_rwlock_unlock(&wrapper->wraplock); ao2_ref(wrapper, -1); - return (res == AST_FORMAT_CMP_NOT_EQUAL) ? -1 : 0; + return res; } -int ast_format_isset(struct ast_format *format, ... ) +int ast_format_isset(const struct ast_format *format, ... ) { va_list ap; int res; @@ -211,6 +254,29 @@ int ast_format_isset(struct ast_format *format, ... ) return res; } +int ast_format_get_value(const struct ast_format *format, int key, void *value) +{ + int res = 0; + struct interface_ao2_wrapper *wrapper; + if (!(wrapper = find_interface(format))) { + return -1; + } + ast_rwlock_rdlock(&wrapper->wraplock); + if (!wrapper->interface || + !wrapper->interface->format_attr_get_val) { + + ast_rwlock_unlock(&wrapper->wraplock); + ao2_ref(wrapper, -1); + return -1; + } + + res = wrapper->interface->format_attr_get_val(&format->fattr, key, value); + + ast_rwlock_unlock(&wrapper->wraplock); + ao2_ref(wrapper, -1); + + return res; +} /*! \internal * \brief cmp format attributes using an interface @@ -372,6 +438,8 @@ uint64_t ast_format_id_to_old_bitfield(enum ast_format_id id) /*! T.140 Text format - ITU T.140, RFC 4103 */ case AST_FORMAT_T140: return (1ULL << 27); + default: + return 0; /* not supported by old bitfield. */ } return 0; @@ -486,20 +554,709 @@ enum ast_format_id ast_format_id_from_old_bitfield(uint64_t src) return 0; } +int ast_format_is_slinear(const struct ast_format *format) +{ + 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; +} + +enum ast_format_id ast_format_slin_by_rate(unsigned int rate) +{ + 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 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; + } + } + 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) = ')'; + } + 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; + } + 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))) { + + ast_format_copy(result, &f_list[x].format); + f_list = ast_format_list_destroy(f_list); + return result; + } + } + 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; +} + +int ast_format_rate(const struct ast_format *format) +{ + 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; + } + default: + return 8000; + } +} + +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: + return NULL; + } + + if ((a->argc < 3) || (a->argc > 4)) { + return CLI_SHOWUSAGE; + } + + 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"); + } + + 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; + } +} + +static char *show_codec_n(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + 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; + } + + 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); + break; + } + } + + if (!found) { + ast_cli(a->fd, "Codec %d not found\n", format_id); + } + + f_list = ast_format_list_destroy(f_list); + return CLI_SUCCESS; +} + +/* 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"), +}; +int init_framer(void) +{ + ast_cli_register_multiple(my_clis, ARRAY_LEN(my_clis)); + return 0; +} + +static int format_list_add_custom(struct ast_format_list *new) +{ + struct ast_format_list *entry; + 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); + return 0; +} + +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; +} +static int list_hash_cb(const void *obj, const int flags) +{ + return ao2_container_count(format_list); +} + +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); + } + + /* 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++; + } + ao2_iterator_destroy(&it); + + ast_rwlock_unlock(&format_list_array_lock); + return 0; +} +static int format_list_init(void) +{ + struct ast_format tmpfmt; + if (!(format_list = ao2_container_alloc(283, list_hash_cb, list_cmp_cb))) { + return -1; + } + /* 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, "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) */ + + return 0; +} + +int ast_format_list_init() +{ + 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; + } + + return 0; +init_list_cleanup: + + ast_rwlock_destroy(&format_list_array_lock); + ao2_ref(format_list, -1); + if (format_list_array) { + ao2_ref(format_list_array, -1); + } + return -1; +} + int ast_format_attr_init() { + ast_cli_register_multiple(my_clis, ARRAY_LEN(my_clis)); if (ast_rwlock_init(&ilock)) { return -1; } + if (!(interfaces = ao2_container_alloc(283, interface_hash_cb, interface_cmp_cb))) { ast_rwlock_destroy(&ilock); + goto init_cleanup; + } + return 0; + +init_cleanup: + ast_rwlock_destroy(&ilock); + if (interfaces) { + ao2_ref(interfaces, -1); + } + return -1; +} + +static int custom_silk_format(struct ast_format_list *entry, unsigned int maxbitrate, int usedtx, int usefec, int packetloss_percentage) +{ + 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 %d\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; +} + +static int conf_process_format_name(const char *name, enum ast_format_id *id) +{ + if (!strcasecmp(name, "silk")) { + *id = AST_FORMAT_SILK; + } else { + *id = 0; + return -1; + } + return 0; +} + +static int conf_process_sample_rate(const char *rate, unsigned int *result) +{ + 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, "48000")) { + *result = 48000; + } else { + *result = 0; return -1; } + + return 0; +} +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 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, "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; + 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; } int ast_format_attr_reg_interface(const struct ast_format_attr_interface *interface) { + 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, @@ -530,11 +1287,25 @@ int ast_format_attr_reg_interface(const struct ast_format_attr_interface *interf 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); + } + } + return 0; } int ast_format_attr_unreg_interface(const struct ast_format_attr_interface *interface) { + 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, @@ -554,5 +1325,16 @@ int ast_format_attr_unreg_interface(const struct ast_format_attr_interface *inte 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(); + return 0; } diff --git a/main/format_cap.c b/main/format_cap.c index c8bdd4fa3..3ef0e74d3 100644 --- a/main/format_cap.c +++ b/main/format_cap.c @@ -99,7 +99,7 @@ void *ast_format_cap_destroy(struct ast_format_cap *cap) return NULL; } -void ast_format_cap_add(struct ast_format_cap *cap, struct ast_format *format) +void ast_format_cap_add(struct ast_format_cap *cap, const struct ast_format *format) { struct ast_format *fnew; @@ -122,26 +122,26 @@ void ast_format_cap_add_all_by_type(struct ast_format_cap *cap, enum ast_format_ { int x; size_t f_len = 0; - struct ast_format tmp_fmt; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const struct ast_format_list *f_list = ast_format_list_get(&f_len); for (x = 0; x < f_len; x++) { - if (AST_FORMAT_GET_TYPE(f_list[x].id) == type) { - ast_format_cap_add(cap, ast_format_set(&tmp_fmt, f_list[x].id, 0)); + if (AST_FORMAT_GET_TYPE(f_list[x].format.id) == type) { + ast_format_cap_add(cap, &f_list[x].format); } } + ast_format_list_destroy(f_list); } void ast_format_cap_add_all(struct ast_format_cap *cap) { int x; size_t f_len = 0; - struct ast_format tmp_fmt; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const struct ast_format_list *f_list = ast_format_list_get(&f_len); for (x = 0; x < f_len; x++) { - ast_format_cap_add(cap, ast_format_set(&tmp_fmt, f_list[x].id, 0)); + ast_format_cap_add(cap, &f_list[x].format); } + ast_format_list_destroy(f_list); } static int append_cb(void *obj, void *arg, int flag) @@ -288,6 +288,21 @@ void ast_format_cap_set(struct ast_format_cap *cap, struct ast_format *format) ast_format_cap_add(cap, format); } +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 *f; + struct ast_format_cap *tmp_cap = (struct ast_format_cap *) cap; + f = ao2_find(tmp_cap->formats, (struct ast_format *) format, OBJ_POINTER | tmp_cap->nolock); + + if (f) { + ast_format_copy(result, f); + ao2_ref(f, -1); + return 1; + } + ast_format_clear(result); + return 0; +} + int ast_format_cap_iscompatible(const struct ast_format_cap *cap, const struct ast_format *format) { struct ast_format *f; @@ -302,6 +317,38 @@ int ast_format_cap_iscompatible(const struct ast_format_cap *cap, const struct a return 0; } +struct byid_data { + struct ast_format *result; + enum ast_format_id id; +}; +static int find_best_byid_cb(void *obj, void *arg, int flag) +{ + struct ast_format *format = obj; + struct byid_data *data = arg; + + if (data->id != format->id) { + return 0; + } + if (!data->result->id || (ast_format_rate(data->result) < ast_format_rate(format))) { + ast_format_copy(data->result, format); + } + 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 | cap->nolock, + find_best_byid_cb, + &data); + return result->id ? 1 : 0; +} + /*! \internal * \brief this struct is just used for the ast_format_cap_joint function so we can provide * both a format and a result ast_format_cap structure as arguments to the find_joint_cb @@ -525,6 +572,42 @@ int ast_format_cap_iter_next(struct ast_format_cap *cap, struct ast_format *form return 0; } +char *ast_getformatname_multiple(char *buf, size_t size, struct ast_format_cap *cap) +{ + int x; + unsigned len; + char *start, *end = buf; + struct ast_format tmp_fmt; + 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; + } + } + 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; +} + uint64_t ast_format_cap_to_old_bitfield(const struct ast_format_cap *cap) { uint64_t res = 0; diff --git a/main/format_pref.c b/main/format_pref.c index 26801b648..f24dbec27 100644 --- a/main/format_pref.c +++ b/main/format_pref.c @@ -34,7 +34,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, int right) { size_t f_len; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const const struct ast_format_list *f_list = ast_format_list_get(&f_len); int x, differential = (int) 'A', mem; char *from, *to; @@ -57,9 +57,10 @@ void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, } to[x] = right ? (from[x] + differential) : (from[x] - differential); if (!right && to[x] && (to[x] < f_len)) { - ast_format_set(&pref->formats[x], f_list[to[x]-1].id , 0); + 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) @@ -67,7 +68,7 @@ int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size) int x; struct ast_format format; size_t total_len, slen; - char *formatname; + const char *formatname; memset(buf, 0, size); total_len = size; @@ -116,23 +117,27 @@ void ast_codec_pref_remove(struct ast_codec_pref *pref, struct ast_format *forma struct ast_codec_pref oldorder; int x, y = 0; size_t f_len = 0; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const const struct ast_format_list *f_list; - if (!pref->order[0]) + 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]) + if (!oldorder.order[x]) { break; - if (f_list[oldorder.order[x]-1].id != format->id) { + } + 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 codec to list */ @@ -140,12 +145,12 @@ int ast_codec_pref_append(struct ast_codec_pref *pref, struct ast_format *format { int x, newindex = 0; size_t f_len = 0; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + 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 (f_list[x].id == format->id) { + if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { newindex = x + 1; break; } @@ -161,6 +166,7 @@ int ast_codec_pref_append(struct ast_codec_pref *pref, struct ast_format *format } } + ast_format_list_destroy(f_list); return x; } @@ -169,18 +175,20 @@ void ast_codec_pref_prepend(struct ast_codec_pref *pref, struct ast_format *form { int x, newindex = 0; size_t f_len = 0; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + 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 (f_list[x].id == format->id) { + if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { newindex = x + 1; break; } } /* Done if its unknown */ - if (!newindex) + 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++) { @@ -188,8 +196,10 @@ void ast_codec_pref_prepend(struct ast_codec_pref *pref, struct ast_format *form break; } - if (only_if_existing && !pref->order[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) */ @@ -203,6 +213,7 @@ void ast_codec_pref_prepend(struct ast_codec_pref *pref, struct ast_format *form 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 */ @@ -210,17 +221,19 @@ int ast_codec_pref_setsize(struct ast_codec_pref *pref, struct ast_format *forma { int x, idx = -1; size_t f_len = 0; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const struct ast_format_list *f_list = ast_format_list_get(&f_len); for (x = 0; x < f_len; x++) { - if (f_list[x].id == format->id) { + if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { idx = x; break; } } - if (idx < 0) + if (idx < 0) { + ast_format_list_destroy(f_list); return -1; + } /* size validation */ if (!framems) @@ -242,6 +255,7 @@ int ast_codec_pref_setsize(struct ast_codec_pref *pref, struct ast_format *forma } } + ast_format_list_destroy(f_list); return x; } @@ -249,12 +263,12 @@ int ast_codec_pref_setsize(struct ast_codec_pref *pref, struct ast_format *forma 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, }; + struct ast_format_list fmt = { { 0, }, }; size_t f_len = 0; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const struct ast_format_list *f_list = ast_format_list_get(&f_len); for (x = 0; x < f_len; x++) { - if (f_list[x].id == format->id) { + if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { fmt = f_list[x]; idx = x; break; @@ -282,7 +296,7 @@ struct ast_format_list ast_codec_pref_getsize(struct ast_codec_pref *pref, struc framems = f_list[idx].max_ms; fmt.cur_ms = framems; - + ast_format_list_destroy(f_list); return fmt; } @@ -291,27 +305,23 @@ struct ast_format *ast_codec_choose(struct ast_codec_pref *pref, struct ast_form { int x, slot, found; size_t f_len = 0; - struct ast_format tmp_fmt; - - const struct ast_format_list *f_list = ast_get_format_list(&f_len); - - ast_format_clear(result); + 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_iscompatible(cap, ast_format_set(&tmp_fmt, f_list[slot-1].id, 0))) { - found = 1; /*format is found and stored in tmp_fmt */ + if (ast_format_cap_get_compatible_format(cap, &f_list[slot-1].format, result)) { + found = 1; /*format is found and stored in result */ break; } } - if (found && (AST_FORMAT_GET_TYPE(tmp_fmt.id) == AST_FORMAT_TYPE_AUDIO)) { - ast_format_copy(result, &tmp_fmt); + 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 6805fea46..d82a46313 100644 --- a/main/frame.c +++ b/main/frame.c @@ -92,38 +92,6 @@ struct ast_smoother { int len; }; -/*! \brief Definition of supported media formats (codecs) */ -static const struct ast_format_list AST_FORMAT_LIST[] = { - { AST_FORMAT_G723_1 , "g723", 8000, "G.723.1", 20, 30, 300, 30, 30 }, /*!< G723.1 */ - { AST_FORMAT_GSM, "gsm", 8000, "GSM", 33, 20, 300, 20, 20 }, /*!< codec_gsm.c */ - { AST_FORMAT_ULAW, "ulaw", 8000, "G.711 u-law", 80, 10, 150, 10, 20 }, /*!< codec_ulaw.c */ - { AST_FORMAT_ALAW, "alaw", 8000, "G.711 A-law", 80, 10, 150, 10, 20 }, /*!< codec_alaw.c */ - { AST_FORMAT_G726, "g726", 8000, "G.726 RFC3551", 40, 10, 300, 10, 20 }, /*!< codec_g726.c */ - { AST_FORMAT_ADPCM, "adpcm" , 8000, "ADPCM", 40, 10, 300, 10, 20 }, /*!< codec_adpcm.c */ - { AST_FORMAT_SLINEAR, "slin", 8000, "16 bit Signed Linear PCM", 160, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE }, /*!< Signed linear */ - { AST_FORMAT_LPC10, "lpc10", 8000, "LPC10", 7, 20, 20, 20, 20 }, /*!< codec_lpc10.c */ - { AST_FORMAT_G729A, "g729", 8000, "G.729A", 10, 10, 230, 10, 20, AST_SMOOTHER_FLAG_G729 }, /*!< Binary commercial distribution */ - { AST_FORMAT_SPEEX, "speex", 8000, "SpeeX", 10, 10, 60, 10, 20 }, /*!< codec_speex.c */ - { AST_FORMAT_SPEEX16, "speex16", 16000, "SpeeX 16khz", 10, 10, 60, 10, 20 }, /*!< codec_speex.c */ - { AST_FORMAT_ILBC, "ilbc", 8000, "iLBC", 50, 30, 30, 30, 30 }, /*!< codec_ilbc.c */ /* inc=30ms - workaround */ - { AST_FORMAT_G726_AAL2, "g726aal2", 8000, "G.726 AAL2", 40, 10, 300, 10, 20 }, /*!< codec_g726.c */ - { AST_FORMAT_G722, "g722", 16000, "G722", 80, 10, 150, 10, 20 }, /*!< codec_g722.c */ - { AST_FORMAT_SLINEAR16, "slin16", 16000, "16 bit Signed Linear PCM (16kHz)", 320, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE }, /*!< Signed linear (16kHz) */ - { AST_FORMAT_JPEG, "jpeg", 0, "JPEG image"}, /*!< See format_jpeg.c */ - { AST_FORMAT_PNG, "png", 0, "PNG image"}, /*!< PNG Image format */ - { AST_FORMAT_H261, "h261", 0, "H.261 Video" }, /*!< H.261 Video Passthrough */ - { AST_FORMAT_H263, "h263", 0, "H.263 Video" }, /*!< H.263 Passthrough support, see format_h263.c */ - { AST_FORMAT_H263_PLUS, "h263p", 0, "H.263+ Video" }, /*!< H.263plus passthrough support See format_h263.c */ - { AST_FORMAT_H264, "h264", 0, "H.264 Video" }, /*!< Passthrough support, see format_h263.c */ - { AST_FORMAT_MP4_VIDEO, "mpeg4", 0, "MPEG4 Video" }, /*!< Passthrough support for MPEG4 */ - { AST_FORMAT_T140RED, "red", 1, "T.140 Realtime Text with redundancy"}, /*!< Redundant T.140 Realtime Text */ - { AST_FORMAT_T140, "t140", 0, "Passthrough T.140 Realtime Text" }, /*!< Passthrough support for T.140 Realtime Text */ - { AST_FORMAT_SIREN7, "siren7", 16000, "ITU G.722.1 (Siren7, licensed from Polycom)", 80, 20, 80, 20, 20 }, /*!< Binary commercial distribution */ - { AST_FORMAT_SIREN14, "siren14", 32000, "ITU G.722.1 Annex C, (Siren14, licensed from Polycom)", 120, 20, 80, 20, 20 }, /*!< Binary commercial distribution */ - { AST_FORMAT_TESTLAW, "testlaw", 8000, "G.711 test-law", 80, 10, 150, 10, 20 }, /*!< codec_ulaw.c */ - { AST_FORMAT_G719, "g719", 48000, "ITU G.719", 160, 20, 80, 20, 20 }, -}; - struct ast_frame ast_null_frame = { AST_FRAME_NULL, }; static int smoother_frame_feed(struct ast_smoother *s, struct ast_frame *f, int swap) @@ -554,218 +522,6 @@ void ast_swapcopy_samples(void *dst, const void *src, int samples) dst_s[i] = (src_s[i]<<8) | (src_s[i]>>8); } - -const struct ast_format_list *ast_get_format_list_index(int idx) -{ - return &AST_FORMAT_LIST[idx]; -} - -const struct ast_format_list *ast_get_format_list(size_t *size) -{ - *size = ARRAY_LEN(AST_FORMAT_LIST); - return AST_FORMAT_LIST; -} - -char* ast_getformatname(struct ast_format *format) -{ - int x; - char *ret = "unknown"; - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].id == format->id) { - ret = AST_FORMAT_LIST[x].name; - break; - } - } - return ret; -} - -char *ast_getformatname_multiple(char *buf, size_t size, struct ast_format_cap *cap) -{ - int x; - unsigned len; - char *start, *end = buf; - struct ast_format tmp_fmt; - - if (!size) - return buf; - snprintf(end, size, "("); - len = strlen(end); - end += len; - size -= len; - start = end; - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - ast_format_set(&tmp_fmt, AST_FORMAT_LIST[x].id, 0); - if (ast_format_cap_iscompatible(cap, &tmp_fmt)) { - snprintf(end, size, "%s|", AST_FORMAT_LIST[x].name); - len = strlen(end); - end += len; - size -= len; - } - } - if (start == end) - ast_copy_string(start, "nothing)", size); - else if (size > 1) - *(end - 1) = ')'; - return buf; -} - -static struct ast_codec_alias_table { - char *alias; - 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; - } - return in; -} - -struct ast_format *ast_getformatbyname(const char *name, struct ast_format *result) -{ - int x; - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (!strcasecmp(AST_FORMAT_LIST[x].name,name) || - !strcasecmp(AST_FORMAT_LIST[x].name, ast_expand_codec_alias(name))) { - - ast_format_set(result, AST_FORMAT_LIST[x].id, 0); - return result; - } - } - - return NULL; -} - -char *ast_codec2str(struct ast_format *format) -{ - int x; - char *ret = "unknown"; - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].id == format->id) { - ret = AST_FORMAT_LIST[x].desc; - break; - } - } - return ret; -} - -static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - int x, found=0; - - 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"); - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (a->argc == 4) { - if (!strcasecmp(a->argv[3], "audio")) { - if (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) != AST_FORMAT_TYPE_AUDIO) { - continue; - } - } else if (!strcasecmp(a->argv[3], "video")) { - if (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) != AST_FORMAT_TYPE_VIDEO) { - continue; - } - } else if (!strcasecmp(a->argv[3], "image")) { - if (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) != AST_FORMAT_TYPE_IMAGE) { - continue; - } - } else if (!strcasecmp(a->argv[3], "text")) { - if (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) != AST_FORMAT_TYPE_TEXT) { - continue; - } - } else { - continue; - } - } - - ast_cli(a->fd, "%8u %5s %8s (%s)\n", - AST_FORMAT_LIST[x].id, - (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) == AST_FORMAT_TYPE_AUDIO) ? "audio" : - (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) == AST_FORMAT_TYPE_IMAGE) ? "image" : - (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) == AST_FORMAT_TYPE_VIDEO) ? "video" : - (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) == AST_FORMAT_TYPE_TEXT) ? "text" : - "(unk)", - AST_FORMAT_LIST[x].name, - AST_FORMAT_LIST[x].desc); - found = 1; - } - - if (!found) { - return CLI_SHOWUSAGE; - } else { - return CLI_SUCCESS; - } -} - -static char *show_codec_n(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - enum ast_format_id format_id; - int x, found = 0; - int type_punned_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; - } - format_id = type_punned_codec; - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].id == format_id) { - found = 1; - ast_cli(a->fd, "%11u %s\n", (unsigned int) format_id, AST_FORMAT_LIST[x].desc); - break; - } - } - - if (!found) - ast_cli(a->fd, "Codec %d not found\n", format_id); - - return CLI_SUCCESS; -} - /*! Dump a frame for debugging purposes */ void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix) { @@ -972,19 +728,6 @@ void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix) term_color(cn, name, COLOR_YELLOW, COLOR_BLACK, sizeof(cn))); } - -/* 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"), -}; - -int init_framer(void) -{ - ast_cli_register_multiple(my_clis, ARRAY_LEN(my_clis)); - return 0; -} - 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; @@ -1202,6 +945,9 @@ int ast_codec_get_samples(struct ast_frame *f) 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; @@ -1246,6 +992,25 @@ int ast_codec_get_samples(struct ast_frame *f) /* 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; + } default: ast_log(LOG_WARNING, "Unable to calculate samples for format %s\n", ast_getformatname(&f->subclass.format)); } @@ -1310,11 +1075,13 @@ int ast_frame_adjust_volume(struct ast_frame *f, int adjustment) short *fdata = f->data.ptr; short adjust_value = abs(adjustment); - if ((f->frametype != AST_FRAME_VOICE) || (f->subclass.format.id != AST_FORMAT_SLINEAR)) + if ((f->frametype != AST_FRAME_VOICE) || !(ast_format_is_slinear(&f->subclass.format))) { return -1; + } - if (!adjustment) + if (!adjustment) { return 0; + } for (count = 0; count < f->samples; count++) { if (adjustment > 0) { diff --git a/main/rtp_engine.c b/main/rtp_engine.c index 738b58fae..b2543893a 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -39,6 +39,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/translate.h" #include "asterisk/netsock2.h" +#include "asterisk/_private.h" struct ast_srtp_res *res_srtp = NULL; struct ast_srtp_policy_res *res_srtp_policy = NULL; @@ -83,50 +84,14 @@ static AST_RWLIST_HEAD_STATIC(glues, ast_rtp_glue); /*! The following array defines the MIME Media type (and subtype) for each of our codecs, or RTP-specific data type. */ -static const struct ast_rtp_mime_type { +static struct ast_rtp_mime_type { struct ast_rtp_payload_type payload_type; char *type; char *subtype; unsigned int sample_rate; -} ast_rtp_mime_types[] = { - {{1, {.id = AST_FORMAT_G723_1}, 0}, "audio", "G723", 8000}, - {{1, {.id = AST_FORMAT_GSM}, 0}, "audio", "GSM", 8000}, - {{1, {.id = AST_FORMAT_ULAW}, 0}, "audio", "PCMU", 8000}, - {{1, {.id = AST_FORMAT_ULAW}, 0}, "audio", "G711U", 8000}, - {{1, {.id = AST_FORMAT_ALAW}, 0}, "audio", "PCMA", 8000}, - {{1, {.id = AST_FORMAT_ALAW}, 0}, "audio", "G711A", 8000}, - {{1, {.id = AST_FORMAT_G726}, 0}, "audio", "G726-32", 8000}, - {{1, {.id = AST_FORMAT_ADPCM}, 0}, "audio", "DVI4", 8000}, - {{1, {.id = AST_FORMAT_SLINEAR}, 0}, "audio", "L16", 8000}, - {{1, {.id = AST_FORMAT_SLINEAR16}, 0}, "audio", "L16", 16000}, - {{1, {.id = AST_FORMAT_LPC10}, 0}, "audio", "LPC", 8000}, - {{1, {.id = AST_FORMAT_G729A}, 0}, "audio", "G729", 8000}, - {{1, {.id = AST_FORMAT_G729A}, 0}, "audio", "G729A", 8000}, - {{1, {.id = AST_FORMAT_G729A}, 0}, "audio", "G.729", 8000}, - {{1, {.id = AST_FORMAT_SPEEX}, 0}, "audio", "speex", 8000}, - {{1, {.id = AST_FORMAT_SPEEX16}, 0}, "audio", "speex", 16000}, - {{1, {.id = AST_FORMAT_ILBC}, 0}, "audio", "iLBC", 8000}, - /* this is the sample rate listed in the RTP profile for the G.722 - codec, *NOT* the actual sample rate of the media stream - */ - {{1, {.id = AST_FORMAT_G722}, 0}, "audio", "G722", 8000}, - {{1, {.id = AST_FORMAT_G726_AAL2}, 0}, "audio", "AAL2-G726-32", 8000}, - {{0, {.id = 0}, AST_RTP_DTMF}, "audio", "telephone-event", 8000}, - {{0, {.id = 0}, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event", 8000}, - {{0, {.id = 0}, AST_RTP_CN}, "audio", "CN", 8000}, - {{1, {.id = AST_FORMAT_JPEG}, 0}, "video", "JPEG", 90000}, - {{1, {.id = AST_FORMAT_PNG}, 0}, "video", "PNG", 90000}, - {{1, {.id = AST_FORMAT_H261}, 0}, "video", "H261", 90000}, - {{1, {.id = AST_FORMAT_H263}, 0}, "video", "H263", 90000}, - {{1, {.id = AST_FORMAT_H263_PLUS}, 0}, "video", "h263-1998", 90000}, - {{1, {.id = AST_FORMAT_H264}, 0}, "video", "H264", 90000}, - {{1, {.id = AST_FORMAT_MP4_VIDEO}, 0}, "video", "MP4V-ES", 90000}, - {{1, {.id = AST_FORMAT_T140RED}, 0}, "text", "RED", 1000}, - {{1, {.id = AST_FORMAT_T140}, 0}, "text", "T140", 1000}, - {{1, {.id = AST_FORMAT_SIREN7}, 0}, "audio", "G7221", 16000}, - {{1, {.id = AST_FORMAT_SIREN14}, 0}, "audio", "G7221", 32000}, - {{1, {.id = AST_FORMAT_G719}, 0}, "audio", "G719", 48000}, -}; +} ast_rtp_mime_types[128]; /* This will Likely not need to grow any time soon. */ +static ast_rwlock_t mime_types_lock; +static int mime_types_len = 0; /*! * \brief Mapping between Asterisk codecs and rtp payload types @@ -138,46 +103,8 @@ static const struct ast_rtp_mime_type { * See http://www.iana.org/assignments/rtp-parameters for a list of * assigned values */ -static const struct ast_rtp_payload_type static_RTP_PT[AST_RTP_MAX_PT] = { - [0] = {1, {.id = AST_FORMAT_ULAW}, 0}, - #ifdef USE_DEPRECATED_G726 - [2] = {1, {.id = AST_FORMAT_G726}, 0},/* Technically this is G.721, but if Cisco can do it, so can we... */ - #endif - [3] = {1, {.id = AST_FORMAT_GSM}, 0}, - [4] = {1, {.id = AST_FORMAT_G723_1}, 0}, - [5] = {1, {.id = AST_FORMAT_ADPCM}, 0},/* 8 kHz */ - [6] = {1, {.id = AST_FORMAT_ADPCM}, 0}, /* 16 kHz */ - [7] = {1, {.id = AST_FORMAT_LPC10}, 0}, - [8] = {1, {.id = AST_FORMAT_ALAW}, 0}, - [9] = {1, {.id = AST_FORMAT_G722}, 0}, - [10] = {1, {.id = AST_FORMAT_SLINEAR}, 0}, /* 2 channels */ - [11] = {1, {.id = AST_FORMAT_SLINEAR}, 0}, /* 1 channel */ - [13] = {0, {.id = 0}, AST_RTP_CN}, - [16] = {1, {.id = AST_FORMAT_ADPCM}, 0}, /* 11.025 kHz */ - [17] = {1, {.id = AST_FORMAT_ADPCM}, 0}, /* 22.050 kHz */ - [18] = {1, {.id = AST_FORMAT_G729A}, 0}, - [19] = {0, {.id = 0}, AST_RTP_CN}, /* Also used for CN */ - [26] = {1, {.id = AST_FORMAT_JPEG}, 0}, - [31] = {1, {.id = AST_FORMAT_H261}, 0}, - [34] = {1, {.id = AST_FORMAT_H263}, 0}, - [97] = {1, {.id = AST_FORMAT_ILBC}, 0}, - [98] = {1, {.id = AST_FORMAT_H263_PLUS}, 0}, - [99] = {1, {.id = AST_FORMAT_H264}, 0}, - [101] = {0, {.id = 0}, AST_RTP_DTMF}, - [102] = {1, {.id = AST_FORMAT_SIREN7}, 0}, - [103] = {1, {.id = AST_FORMAT_H263_PLUS}, 0}, - [104] = {1, {.id = AST_FORMAT_MP4_VIDEO}, 0}, - [105] = {1, {.id = AST_FORMAT_T140RED}, 0}, /* Real time text chat (with redundancy encoding) */ - [106] = {1, {.id = AST_FORMAT_T140}, 0}, /* Real time text chat */ - [110] = {1, {.id = AST_FORMAT_SPEEX}, 0}, - [111] = {1, {.id = AST_FORMAT_G726}, 0}, - [112] = {1, {.id = AST_FORMAT_G726_AAL2}, 0}, - [115] = {1, {.id = AST_FORMAT_SIREN14}, 0}, - [116] = {1, {.id = AST_FORMAT_G719}, 0}, - [117] = {1, {.id = AST_FORMAT_SPEEX16}, 0}, - [118] = {1, {.id = AST_FORMAT_SLINEAR16}, 0}, /* 16 Khz signed linear */ - [121] = {0, {.id = 0}, AST_RTP_CISCO_DTMF}, /* Must be type 121 */ -}; +static struct ast_rtp_payload_type static_RTP_PT[AST_RTP_MAX_PT]; +static ast_rwlock_t static_RTP_PT_lock; int ast_rtp_engine_register2(struct ast_rtp_engine *engine, struct ast_module *module) { @@ -497,6 +424,7 @@ void ast_rtp_codecs_payloads_default(struct ast_rtp_codecs *codecs, struct ast_r { 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) { @@ -508,6 +436,7 @@ void ast_rtp_codecs_payloads_default(struct ast_rtp_codecs *codecs, struct ast_r } } } + 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) @@ -529,7 +458,10 @@ void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_cod void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload) { + + ast_rwlock_rdlock(&static_RTP_PT_lock); if (payload < 0 || payload >= AST_RTP_MAX_PT || (!static_RTP_PT[payload].rtp_code && !static_RTP_PT[payload].asterisk_format)) { + ast_rwlock_unlock(&static_RTP_PT_lock); return; } @@ -542,6 +474,7 @@ void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct as if (instance && instance->engine && instance->engine->payload_set) { instance->engine->payload_set(instance, payload, codecs->payloads[payload].asterisk_format, &codecs->payloads[payload].format, codecs->payloads[payload].rtp_code); } + ast_rwlock_unlock(&static_RTP_PT_lock); } int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int pt, @@ -555,7 +488,8 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, if (pt < 0 || pt >= AST_RTP_MAX_PT) return -1; /* bogus payload type */ - for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); ++i) { + ast_rwlock_rdlock(&mime_types_lock); + for (i = 0; i < mime_types_len; ++i) { const struct ast_rtp_mime_type *t = &ast_rtp_mime_types[i]; if (strcasecmp(mimesubtype, t->subtype)) { @@ -587,6 +521,7 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, break; } + ast_rwlock_unlock(&mime_types_lock); return (found ? 0 : -2); } @@ -626,12 +561,26 @@ struct ast_rtp_payload_type ast_rtp_codecs_payload_lookup(struct ast_rtp_codecs ast_format_copy(&result.format, &codecs->payloads[payload].format); if (!result.rtp_code && !result.asterisk_format) { + ast_rwlock_rdlock(&static_RTP_PT_lock); result = static_RTP_PT[payload]; + ast_rwlock_unlock(&static_RTP_PT_lock); } return result; } + +struct ast_format *ast_rtp_codecs_get_payload_format(struct ast_rtp_codecs *codecs, int payload) +{ + if (payload < 0 || payload >= AST_RTP_MAX_PT) { + return NULL; + } + if (!codecs->payloads[payload].asterisk_format) { + return NULL; + } + return &codecs->payloads[payload].format; +} + void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_format_cap *astformats, int *nonastformats) { int i; @@ -654,7 +603,7 @@ void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_fo int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code) { int i; - + int res = -1; for (i = 0; i < AST_RTP_MAX_PT; i++) { if (codecs->payloads[i].asterisk_format && asterisk_format && format && (ast_format_cmp(format, &codecs->payloads[i].format) != AST_FORMAT_CMP_NOT_EQUAL)) { @@ -665,56 +614,71 @@ int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_form } } + 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)) { - return i; + res = i; + break; } else if (!static_RTP_PT[i].asterisk_format && !asterisk_format && (static_RTP_PT[i].rtp_code == code)) { - return i; + res = i; + break; } } + ast_rwlock_unlock(&static_RTP_PT_lock); - return -1; + 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; + const char *res = ""; - for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); i++) { + 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)) { - return "G726-32"; + res = "G726-32"; + break; } else { - return ast_rtp_mime_types[i].subtype; + res = ast_rtp_mime_types[i].subtype; + break; } } else if (!ast_rtp_mime_types[i].payload_type.asterisk_format && !asterisk_format && ast_rtp_mime_types[i].payload_type.rtp_code == code) { - return ast_rtp_mime_types[i].subtype; + res = ast_rtp_mime_types[i].subtype; + break; } } + ast_rwlock_unlock(&mime_types_lock); - return ""; + return res; } unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, struct ast_format *format, int code) { unsigned int i; + unsigned int res = 0; - for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); ++i) { + 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)) { - return ast_rtp_mime_types[i].sample_rate; + res = ast_rtp_mime_types[i].sample_rate; + break; } else if (!ast_rtp_mime_types[i].payload_type.asterisk_format && !asterisk_format && ast_rtp_mime_types[i].payload_type.rtp_code == code) { - return ast_rtp_mime_types[i].sample_rate; + res = ast_rtp_mime_types[i].sample_rate; + break; } } + ast_rwlock_unlock(&mime_types_lock); - return 0; + return res; } char *ast_rtp_lookup_mime_multiple2(struct ast_str *buf, struct ast_format_cap *ast_format_capability, int rtp_capability, const int asterisk_format, enum ast_rtp_options options) @@ -1879,3 +1843,185 @@ struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance) { return instance->srtp; } + +static void set_next_mime_type(const struct ast_format *format, int rtp_code, char *type, char *subtype, unsigned int sample_rate) +{ + int x = mime_types_len; + if (ARRAY_LEN(ast_rtp_mime_types) == mime_types_len) { + return; + } + + ast_rwlock_wrlock(&mime_types_lock); + if (format) { + ast_rtp_mime_types[x].payload_type.asterisk_format = 1; + ast_format_copy(&ast_rtp_mime_types[x].payload_type.format, 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_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) +{ + int x; + ast_rwlock_wrlock(&static_RTP_PT_lock); + if (map < 0) { + /* find next available dynamic payload slot */ + for (x = 96; x < 127; x++) { + if (!static_RTP_PT[x].asterisk_format && !static_RTP_PT[x].rtp_code) { + map = x; + break; + } + } + } + + if (map < 0) { + ast_log(LOG_WARNING, "No Dynamic RTP mapping avaliable for format %s\n" ,ast_getformatname(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); + } 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) +{ + 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; + default: + break; + } + + return 0; +} + +int ast_rtp_engine_unload_format(const struct ast_format *format) +{ + int x; + int y = 0; + + 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)); + } + } + 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) { + continue; + } + ast_rtp_mime_types[y] = ast_rtp_mime_types[x]; + y++; + } + mime_types_len = y; + ast_rwlock_unlock(&mime_types_lock); + return 0; +} + +int ast_rtp_engine_init() +{ + struct ast_format tmpfmt; + + ast_rwlock_init(&mime_types_lock); + ast_rwlock_init(&static_RTP_PT_lock); + + /* 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_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); + /* 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(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); + + /* Define the static rtp payload mappings */ + add_static_payload(0, ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0), 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... */ + #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(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(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(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(121, NULL, AST_RTP_CISCO_DTMF); /* Must be type 121 */ + + return 0; +} diff --git a/main/slinfactory.c b/main/slinfactory.c index f7363ab4b..338305b40 100644 --- a/main/slinfactory.c +++ b/main/slinfactory.c @@ -39,20 +39,14 @@ void ast_slinfactory_init(struct ast_slinfactory *sf) ast_format_set(&sf->output_format, AST_FORMAT_SLINEAR, 0); } -int ast_slinfactory_init_rate(struct ast_slinfactory *sf, unsigned int sample_rate) +int ast_slinfactory_init_with_format(struct ast_slinfactory *sf, const struct ast_format *slin_out) { memset(sf, 0, sizeof(*sf)); sf->offset = sf->hold; - switch (sample_rate) { - case 8000: - ast_format_set(&sf->output_format, AST_FORMAT_SLINEAR, 0); - break; - case 16000: - ast_format_set(&sf->output_format, AST_FORMAT_SLINEAR16, 0); - break; - default: + if (!ast_format_is_slinear(slin_out)) { return -1; } + ast_format_copy(&sf->output_format, slin_out); return 0; } @@ -93,8 +87,11 @@ int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f) if (!sf->trans) { if (!(sf->trans = ast_translator_build_path(&sf->output_format, &f->subclass.format))) { - ast_log(LOG_WARNING, "Cannot build a path from %s to %s\n", ast_getformatname(&f->subclass.format), - ast_getformatname(&sf->output_format)); + ast_log(LOG_WARNING, "Cannot build a path from %s (%d)to %s (%d)\n", + ast_getformatname(&f->subclass.format), + f->subclass.format.id, + ast_getformatname(&sf->output_format), + sf->output_format.id); return 0; } ast_format_copy(&sf->format, &f->subclass.format); diff --git a/main/translate.c b/main/translate.c index 553e70cde..caba2d393 100644 --- a/main/translate.c +++ b/main/translate.c @@ -273,7 +273,7 @@ static struct translator_path *matrix_get(unsigned int x, unsigned int y) * \brief Allocate the descriptor, required outbuf space, * and possibly desc. */ -static void *newpvt(struct ast_translator *t) +static void *newpvt(struct ast_translator *t, const struct ast_format *explicit_dst) { struct ast_trans_pvt *pvt; int len; @@ -287,16 +287,23 @@ static void *newpvt(struct ast_translator *t) if (t->buf_size) len += AST_FRIENDLY_OFFSET + t->buf_size; pvt = ast_calloc(1, len); - if (!pvt) + if (!pvt) { return NULL; + } pvt->t = t; ofs = (char *)(pvt + 1); /* pointer to data space */ if (t->desc_size) { /* first comes the descriptor */ pvt->pvt = ofs; ofs += t->desc_size; } - if (t->buf_size) /* finally buffer and header */ + 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); + } /* call local init routine, if present */ if (t->newpvt && t->newpvt(pvt)) { ast_free(pvt); @@ -424,6 +431,7 @@ 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); @@ -434,7 +442,10 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a AST_RWLIST_UNLOCK(&translators); return NULL; } - if (!(cur = newpvt(t))) { + 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); ast_log(LOG_WARNING, "Failed to build translator step from %s to %s\n", @@ -565,12 +576,12 @@ static void generate_computational_cost(struct ast_translator *t, int seconds) /* If they don't make samples, give them a terrible score */ if (!t->sample) { - ast_log(LOG_WARNING, "Translator '%s' does not produce sample frames.\n", t->name); + ast_debug(3, "Translator '%s' does not produce sample frames.\n", t->name); t->comp_cost = 999999; return; } - pvt = newpvt(t); + pvt = newpvt(t, NULL); if (!pvt) { ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name); t->comp_cost = 999999; @@ -641,13 +652,8 @@ static int generate_table_cost(struct ast_format *src, struct ast_format *dst) * table cost. */ return 0; } - if ((src->id == AST_FORMAT_SLINEAR) || (src->id == AST_FORMAT_SLINEAR16)) { - src_ll = 1; - } - if ((dst->id == AST_FORMAT_SLINEAR) || (dst->id == AST_FORMAT_SLINEAR16)) { - dst_ll = 1; - } - + src_ll = ast_format_is_slinear(src); + dst_ll = ast_format_is_slinear(dst); if (src_ll) { if (dst_ll && (src_rate == dst_rate)) { return AST_TRANS_COST_LL_LL_ORIGSAMP; @@ -778,16 +784,17 @@ static void matrix_rebuild(int samples) const char *ast_translate_path_to_str(struct ast_trans_pvt *p, struct ast_str **str) { struct ast_trans_pvt *pn = p; + char tmp[256]; if (!p || !p->t) { return ""; } - ast_str_set(str, 0, "%s", ast_getformatname(&p->t->src_format)); + 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(&p->t->dst_format)); + ast_str_append(str, 0, "->%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), p->t->dst_format.id)); } return ast_str_buffer(*str); @@ -800,10 +807,10 @@ static char *complete_trans_path_choice(const char *line, const char *word, int int i; char *ret = NULL; size_t len = 0; - const struct ast_format_list *format_list = ast_get_format_list(&len); + const struct ast_format_list *format_list = ast_format_list_get(&len); for (i = 0; i < len; i++) { - if (AST_FORMAT_GET_TYPE(format_list[i].id) != AST_FORMAT_TYPE_AUDIO) { + if (AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) { continue; } if (!strncasecmp(word, format_list[i].name, wordlen) && ++which > state) { @@ -811,6 +818,7 @@ static char *complete_trans_path_choice(const char *line, const char *word, int break; } } + ast_format_list_destroy(format_list); return ret; } @@ -835,46 +843,56 @@ static void handle_cli_recalc(struct ast_cli_args *a) static char *handle_show_translation_table(struct ast_cli_args *a) { - int x, y; + int x, y, i, k; int curlen = 0, longest = 0; - struct ast_format tmp_fmt; + int f_len = 0; + const struct ast_format_list *f_list = ast_format_list_get((size_t *) &f_len); + struct ast_str *out = ast_str_create(1024); + AST_RWLIST_RDLOCK(&translators); ast_cli(a->fd, " Translation times between formats (in microseconds) for one second of data\n"); ast_cli(a->fd, " Source Format (Rows) Destination Format (Columns)\n\n"); /* Get the length of the longest (usable?) codec name, so we know how wide the left side should be */ - for (x = 0; x < cur_max_index; x++) { + for (i = 0; i < f_len; i++) { /* translation only applies to audio right now. */ - if (AST_FORMAT_GET_TYPE(index2format(x)) != AST_FORMAT_TYPE_AUDIO) + if (AST_FORMAT_GET_TYPE(f_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) continue; - curlen = strlen(ast_getformatname(ast_format_set(&tmp_fmt, index2format(x), 0))); + curlen = strlen(ast_getformatname(&f_list[i].format)); if (curlen > longest) { longest = curlen; } } - for (x = -1; x < cur_max_index; x++) { - struct ast_str *out = ast_str_alloca(256); + for (i = -1; i < f_len; i++) { + x = -1; + if ((i >= 0) && ((x = format2index(f_list[i].format.id)) == -1)) { + continue; + } /* translation only applies to audio right now. */ - if (x >= 0 && (AST_FORMAT_GET_TYPE(index2format(x)) != AST_FORMAT_TYPE_AUDIO)) { + if (i >= 0 && (AST_FORMAT_GET_TYPE(f_list[i].format.id) != AST_FORMAT_TYPE_AUDIO)) { continue; } /*Go ahead and move to next iteration if dealing with an unknown codec*/ - if (x >= 0 && !strcmp(ast_getformatname(ast_format_set(&tmp_fmt, index2format(x), 0)), "unknown")) { + if (i >= 0 && !strcmp(ast_getformatname(&f_list[i].format), "unknown")) { continue; } - ast_str_set(&out, -1, " "); - for (y = -1; y < cur_max_index; y++) { + ast_str_set(&out, 0, " "); + for (k = -1; k < f_len; k++) { + y = -1; + if ((k >= 0) && ((y = format2index(f_list[k].format.id)) == -1)) { + continue; + } /* translation only applies to audio right now. */ - if (y >= 0 && (AST_FORMAT_GET_TYPE(index2format(y)) != AST_FORMAT_TYPE_AUDIO)) { + 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 (y >= 0 && !strcmp(ast_getformatname(ast_format_set(&tmp_fmt, index2format(y), 0)), "unknown")) { + if (k >= 0 && !strcmp(ast_getformatname(&f_list[k].format), "unknown")) { continue; } - if (y >= 0) { - curlen = strlen(ast_getformatname(ast_format_set(&tmp_fmt, index2format(y), 0))); + if (k >= 0) { + curlen = strlen(ast_getformatname(&f_list[k].format)); } if (curlen < 5) { curlen = 5; @@ -882,25 +900,27 @@ 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, -1, "%*d", curlen + 1, (matrix_get(x, y)->table_cost/100)); - } else if (x == -1 && y >= 0) { + ast_str_append(&out, 0, "%*d", curlen + 1, (matrix_get(x, y)->table_cost/100)); + } else if (i == -1 && k >= 0) { /* Top row - use a dynamic size */ - ast_str_append(&out, -1, "%*s", curlen + 1, ast_getformatname(ast_format_set(&tmp_fmt, index2format(y), 0))); - } else if (y == -1 && x >= 0) { + ast_str_append(&out, 0, "%*s", curlen + 1, ast_getformatname(&f_list[k].format)); + } else if (k == -1 && i >= 0) { /* Left column - use a static size. */ - ast_str_append(&out, -1, "%*s", longest, ast_getformatname(ast_format_set(&tmp_fmt, index2format(x), 0))); + ast_str_append(&out, 0, "%*s", longest, ast_getformatname(&f_list[i].format)); } else if (x >= 0 && y >= 0) { /* Codec not supported */ - ast_str_append(&out, -1, "%*s", curlen + 1, "-"); + ast_str_append(&out, 0, "%*s", curlen + 1, "-"); } else { /* Upper left hand corner */ - ast_str_append(&out, -1, "%*s", longest, ""); + ast_str_append(&out, 0, "%*s", longest, ""); } } - ast_str_append(&out, -1, "\n"); + ast_str_append(&out, 0, "\n"); ast_cli(a->fd, "%s", ast_str_buffer(out)); } + ast_free(out); AST_RWLIST_UNLOCK(&translators); + ast_format_list_destroy(f_list); return CLI_SUCCESS; } @@ -909,23 +929,24 @@ static char *handle_show_translation_path(struct ast_cli_args *a) struct ast_format input_src_format; size_t len = 0; int i; - const struct ast_format_list *format_list = ast_get_format_list(&len); - struct ast_str *str = ast_str_alloca(256); + const struct ast_format_list *format_list = ast_format_list_get(&len); + struct ast_str *str = ast_str_alloca(1024); struct ast_translator *step; + char tmp[256]; ast_format_clear(&input_src_format); - for (i = 0; i < len; i++) { - if (AST_FORMAT_GET_TYPE(format_list[i].id) != AST_FORMAT_TYPE_AUDIO) { + 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_set(&input_src_format, format_list[i].id, 0); + 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); return CLI_FAILURE; } @@ -934,21 +955,21 @@ static char *handle_show_translation_path(struct ast_cli_args *a) for (i = 0; i < len; i++) { int src; int dst; - if ((AST_FORMAT_GET_TYPE(format_list[i].id) != AST_FORMAT_TYPE_AUDIO) || (format_list[i].id == input_src_format.id)) { + if ((AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) || (format_list[i].format.id == input_src_format.id)) { continue; } - dst = format2index(format_list[i].id); + dst = format2index(format_list[i].format.id); src = format2index(input_src_format.id); ast_str_reset(str); if ((len >= cur_max_index) && (src != -1) && (dst != -1) && matrix_get(src, dst)->step) { - ast_str_append(&str, 0, "%s", ast_getformatname(&matrix_get(src, dst)->step->src_format)); + 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; } - ast_str_append(&str, 0, "->%s", ast_getformatname(&step->dst_format)); + ast_str_append(&str, 0, "->%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), step->dst_format.id)); src = step->dst_fmt_index; } } @@ -960,6 +981,7 @@ static char *handle_show_translation_path(struct ast_cli_args *a) } AST_RWLIST_UNLOCK(&translators); + ast_format_list_destroy(format_list); return CLI_SUCCESS; } |