--- include/asterisk/format.h +++ include/asterisk/format.h @@ -219,6 +219,10 @@ * \retval -1 failure, Value was either not found, or not allowed to be accessed. */ int (* const format_attr_get_val)(const struct ast_format_attr *format_attr, int key, void *val); + + int (* const format_samples)(const struct ast_frame *f); + int (* const format_rate)(const struct ast_format *format); + int (* const allowSmoother)(void); /* * \brief Parse SDP attribute information, interpret it, and store it in ast_format_attr structure. @@ -474,1 +475,7 @@ + +int ast_format_dynamic_add(struct ast_format_list *new); +int ast_format_dynamic_get_samples(const struct ast_format *format, const struct ast_frame *frame); +int ast_format_dynamic_get_rate(const struct ast_format *format); +int ast_format_dynamic_register(struct ast_format_attr_interface *new, enum ast_format_type formatType); +int ast_format_allowSmoother(const struct ast_format *format); #endif /* _AST_FORMAT_H */ --- include/asterisk/rtp_engine.h +++ include/asterisk/rtp_engine.h @@ -2134,6 +2134,8 @@ * \param dtls_cfg a DTLS configuration structure */ void ast_rtp_dtls_cfg_free(struct ast_rtp_dtls_cfg *dtls_cfg); + +void ast_rtp_format_dynamic_add(const struct ast_format *format, int rtp_code, char *type, char *subtype, unsigned int sample_rate, int map); #if defined(__cplusplus) || defined(c_plusplus) } --- main/channel.c +++ main/channel.c @@ -928,6 +928,7 @@ }; char buf[512]; int x; + unsigned int found_audio = 0; /* Find the first preferred codec in the format given */ for (x = 0; x < ARRAY_LEN(prefs); x++) { @@ -935,6 +936,25 @@ return result; } } + + /* if just one format is audio, we know the best one */ + ast_format_cap_iter_start(cap); + while (!ast_format_cap_iter_next(cap, result)) { + if (AST_FORMAT_GET_TYPE(result->id) == AST_FORMAT_TYPE_AUDIO) { + found_audio++; + } + } + ast_format_cap_iter_end(cap); + if (1 == found_audio) { + ast_format_cap_iter_start(cap); + while (!ast_format_cap_iter_next(cap, result)) { + if (AST_FORMAT_GET_TYPE(result->id) == AST_FORMAT_TYPE_AUDIO) { + break; + } + } + ast_format_cap_iter_end(cap); + return result; + } ast_format_clear(result); ast_log(LOG_WARNING, "Don't know any of %s formats\n", ast_getformatname_multiple(buf, sizeof(buf), cap)); --- main/format.c +++ main/format.c @@ -172,7 +172,7 @@ struct interface_ao2_wrapper *wrapper; if (!(wrapper = find_interface(format))) { - ast_log(LOG_WARNING, "Could not find format interface to set.\n"); + ast_log(LOG_WARNING, "Could not find format interface for %d to set.\n", format->id); return -1; } @@ -729,6 +729,8 @@ int ast_format_rate(const struct ast_format *format) { + int rate; + switch (format->id) { case AST_FORMAT_SLINEAR12: return 12000; @@ -783,7 +785,8 @@ } } default: - return 8000; + rate = ast_format_dynamic_get_rate(format); + return (rate > 0) ? rate : 8000; } } @@ -1427,3 +1426,102 @@ f_list = ast_format_list_destroy(f_list); return 0; } + +int ast_format_dynamic_add(struct ast_format_list *new) +{ + RAII_VAR(struct ast_format_list *, entry, NULL, ao2_cleanup); + if (!(entry = ao2_alloc(sizeof(*entry), NULL))) { + return -1; + } + if (new->custom_entry != 0) { + /* + * TODO load_format_config:ao2_callback is called at + * startup, several times. That removes all custom + * formats. If you look for this feature, please, + * contact the author of this patch. Workaround: + * via the command-line interface, core unload the + * module, core load it again after each restart. + */ + ast_log(LOG_WARNING, "Custom format; is removed at restart.\n"); + } + memcpy(entry, new, sizeof(struct ast_format_list)); + /* keeps custom_entry as specified, default = 0 = static */ + ao2_link(format_list, entry); + + return build_format_list_array(); +} + +int ast_format_dynamic_get_samples(const struct ast_format *format, const struct ast_frame *f) +{ + struct interface_ao2_wrapper *wrapper; + int samples = 0; + + if ((wrapper = find_interface(format))) { + if (wrapper->interface && wrapper->interface->format_samples) { + samples = wrapper->interface->format_samples(f); + } + ao2_ref(wrapper, -1); + } + + return samples; +} + +int ast_format_dynamic_get_rate(const struct ast_format *format) +{ + struct interface_ao2_wrapper *wrapper; + int rate = -1; + + if ((wrapper = find_interface(format))) { + if (wrapper->interface && wrapper->interface->format_rate) { + rate = wrapper->interface->format_rate(format); + } + ao2_ref(wrapper, -1); + } + + return rate; +} + +int ast_format_allowSmoother(const struct ast_format *format) +{ + struct interface_ao2_wrapper *wrapper; + int result = 1; + + if ((wrapper = find_interface(format))) { + if (wrapper->interface && wrapper->interface->allowSmoother) { + result = wrapper->interface->allowSmoother(); + } + ao2_ref(wrapper, -1); + } + + return result; +} + +int ast_format_dynamic_register(struct ast_format_attr_interface *newFormatInterface, enum ast_format_type formatType) +{ + size_t x, f_len; + const struct ast_format_list *f_list; + + if (newFormatInterface->id == 0) { + /* + * CELT is the highest (audio) format as of Asterisk 11, and + * CELT might not be in the format list because is not added statically, + * or stated differently: CELT is the minimum ID (see format.h). + * Not an issue since Asterisk 12 because of OPUS which is even higher. + */ + newFormatInterface->id = AST_FORMAT_CELT + 1; + + ao2_wrlock(interfaces); + f_list = ast_format_list_get(&f_len); + for (x = 0; x < f_len; x++) { + if (AST_FORMAT_GET_TYPE(f_list[x].format.id) == formatType) { + if (newFormatInterface->id <= f_list[x].format.id) { + newFormatInterface->id = f_list[x].format.id + 1; + } + } + } + f_list = ast_format_list_destroy(f_list); + ao2_unlock(interfaces); + } + + return ast_format_attr_reg_interface(newFormatInterface); +} --- main/frame.c +++ main/frame.c @@ -1084,7 +1084,10 @@ samples = ast_format_rate(&f->subclass.format) / 50; break; default: - ast_log(LOG_WARNING, "Unable to calculate samples for format %s\n", ast_getformatname(&f->subclass.format)); + samples = ast_format_dynamic_get_samples(&f->subclass.format, f); + if (!samples) { + ast_log(LOG_WARNING, "Unable to calculate samples for format %s\n", ast_getformatname(&f->subclass.format)); + } } return samples; } --- main/rtp_engine.c +++ main/rtp_engine.c @@ -2370,3 +2370,9 @@ return 0; } + +void ast_rtp_format_dynamic_add(const struct ast_format *format, int rtp_code, char *type, char *subtype, unsigned int sample_rate, int map) +{ + set_next_mime_type(format, rtp_code, type, subtype, sample_rate); + add_static_payload(map, format, rtp_code); +} --- res/res_rtp_asterisk.c +++ res/res_rtp_asterisk.c @@ -3000,6 +3000,7 @@ struct ast_sockaddr remote_address = { {0,} }; struct ast_format subclass; int codec; + int allowSmoother = 1; ast_rtp_instance_get_remote_address(instance, &remote_address); @@ -3046,9 +3046,7 @@ } } - /* If no smoother is present see if we have to set one up */ - if (!rtp->smoother) { - struct ast_format_list fmt = ast_codec_pref_getsize(&ast_rtp_instance_get_codecs(instance)->pref, &subclass); + allowSmoother = ast_format_allowSmoother(&subclass); switch (subclass.id) { case AST_FORMAT_SPEEX: @@ -3062,8 +3060,15 @@ case AST_FORMAT_G719: /* these are all frame-based codecs and cannot be safely run through a smoother */ + allowSmoother = 0; break; default: + break; + } + + /* If no smoother is present see if we have to set one up */ + if (!rtp->smoother && allowSmoother) { + struct ast_format_list fmt = ast_codec_pref_getsize(&ast_rtp_instance_get_codecs(instance)->pref, &subclass); if (fmt.inc_ms) { if (!(rtp->smoother = ast_smoother_new((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms))) { ast_log(LOG_WARNING, "Unable to create smoother: format %s ms: %d len: %d\n", ast_getformatname(&subclass), fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms)); @@ -3075,7 +3080,6 @@ ast_debug(1, "Created smoother: format: %s ms: %d len: %d\n", ast_getformatname(&subclass), fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms)); } } - } /* Feed audio frames into the actual function that will create a frame and send it */ if (rtp->smoother) {