diff --git a/include/asterisk/codec.h b/include/asterisk/codec.h index 28befec50c..ea10c11a51 100644 --- a/include/asterisk/codec.h +++ b/include/asterisk/codec.h @@ -26,6 +26,9 @@ #ifndef _AST_CODEC_H_ #define _AST_CODEC_H_ +#define AST_SMOOTHER_FLAGS_PACK(x) ((x) << 1) +#define AST_SMOOTHER_FLAGS_UNPACK(x) ((x) >> 1) + /*! \brief Types of media */ enum ast_media_type { AST_MEDIA_TYPE_UNKNOWN = 0, diff --git a/include/asterisk/format.h b/include/asterisk/format.h index 2ce1b97e54..368e4104d9 100644 --- a/include/asterisk/format.h +++ b/include/asterisk/format.h @@ -337,6 +337,17 @@ const char *ast_format_get_codec_name(const struct ast_format *format); */ int ast_format_can_be_smoothed(const struct ast_format *format); +/*! + * \since 13.17.0 + * + * \brief Get smoother flags for this format + * + * \param format The media format + * + * \return smoother flags for the provided format + */ +int ast_format_get_smoother_flags(const struct ast_format *format); + /*! * \brief Get the media type of a format * diff --git a/include/asterisk/smoother.h b/include/asterisk/smoother.h index e63aa77bd6..65ac88921f 100644 --- a/include/asterisk/smoother.h +++ b/include/asterisk/smoother.h @@ -33,6 +33,7 @@ extern "C" { #define AST_SMOOTHER_FLAG_G729 (1 << 0) #define AST_SMOOTHER_FLAG_BE (1 << 1) +#define AST_SMOOTHER_FLAG_FORCED (1 << 2) /*! \name AST_Smoother */ diff --git a/main/codec_builtin.c b/main/codec_builtin.c index da03cce6b5..5fdfa7e12b 100644 --- a/main/codec_builtin.c +++ b/main/codec_builtin.c @@ -37,6 +37,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/format.h" #include "asterisk/format_cache.h" #include "asterisk/frame.h" +#include "asterisk/smoother.h" int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name, struct ast_module *mod); @@ -264,7 +265,7 @@ static struct ast_codec slin8 = { .minimum_bytes = 160, .samples_count = slin_samples, .get_length = slin_length, - .smooth = 1, + .smooth = AST_SMOOTHER_FLAGS_PACK(AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED), }; static struct ast_codec slin12 = { @@ -278,7 +279,7 @@ static struct ast_codec slin12 = { .minimum_bytes = 240, .samples_count = slin_samples, .get_length = slin_length, - .smooth = 1, + .smooth = AST_SMOOTHER_FLAGS_PACK(AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED), }; static struct ast_codec slin16 = { @@ -292,7 +293,7 @@ static struct ast_codec slin16 = { .minimum_bytes = 320, .samples_count = slin_samples, .get_length = slin_length, - .smooth = 1, + .smooth = AST_SMOOTHER_FLAGS_PACK(AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED), }; static struct ast_codec slin24 = { @@ -306,7 +307,7 @@ static struct ast_codec slin24 = { .minimum_bytes = 480, .samples_count = slin_samples, .get_length = slin_length, - .smooth = 1, + .smooth = AST_SMOOTHER_FLAGS_PACK(AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED), }; static struct ast_codec slin32 = { @@ -320,7 +321,7 @@ static struct ast_codec slin32 = { .minimum_bytes = 640, .samples_count = slin_samples, .get_length = slin_length, - .smooth = 1, + .smooth = AST_SMOOTHER_FLAGS_PACK(AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED), }; static struct ast_codec slin44 = { @@ -334,7 +335,7 @@ static struct ast_codec slin44 = { .minimum_bytes = 882, .samples_count = slin_samples, .get_length = slin_length, - .smooth = 1, + .smooth = AST_SMOOTHER_FLAGS_PACK(AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED), }; static struct ast_codec slin48 = { @@ -348,7 +349,7 @@ static struct ast_codec slin48 = { .minimum_bytes = 960, .samples_count = slin_samples, .get_length = slin_length, - .smooth = 1, + .smooth = AST_SMOOTHER_FLAGS_PACK(AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED), }; static struct ast_codec slin96 = { @@ -362,7 +363,7 @@ static struct ast_codec slin96 = { .minimum_bytes = 1920, .samples_count = slin_samples, .get_length = slin_length, - .smooth = 1, + .smooth = AST_SMOOTHER_FLAGS_PACK(AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED), }; static struct ast_codec slin192 = { @@ -376,7 +377,7 @@ static struct ast_codec slin192 = { .minimum_bytes = 3840, .samples_count = slin_samples, .get_length = slin_length, - .smooth = 1, + .smooth = AST_SMOOTHER_FLAGS_PACK(AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED), }; static int lpc10_samples(struct ast_frame *frame) diff --git a/main/format.c b/main/format.c index 5bf38dfb2e..758a7fc5e4 100644 --- a/main/format.c +++ b/main/format.c @@ -377,7 +377,13 @@ const char *ast_format_get_codec_name(const struct ast_format *format) int ast_format_can_be_smoothed(const struct ast_format *format) { - return format->codec->smooth; + /* Coalesce to 1 if non-zero */ + return format->codec->smooth ? 1 : 0; +} + +int ast_format_get_smoother_flags(const struct ast_format *format) +{ + return AST_SMOOTHER_FLAGS_UNPACK(format->codec->smooth); } enum ast_media_type ast_format_get_type(const struct ast_format *format) diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 673f9c1240..f75482f7fd 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -3743,7 +3743,7 @@ static int ast_rtcp_write(const void *data) } /*! \pre instance is locked */ -static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec) +static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); int pred, mark = 0; @@ -4014,10 +4014,10 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr /* If no smoother is present see if we have to set one up */ if (!rtp->smoother && ast_format_can_be_smoothed(format)) { + unsigned int smoother_flags = ast_format_get_smoother_flags(format); unsigned int framing_ms = ast_rtp_codecs_get_framing(ast_rtp_instance_get_codecs(instance)); - int is_slinear = ast_format_cache_is_slinear(format); - if (!framing_ms && is_slinear) { + if (!framing_ms && (smoother_flags & AST_SMOOTHER_FLAG_FORCED)) { framing_ms = ast_format_get_default_ms(format); } @@ -4028,9 +4028,7 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr ast_format_get_name(format), framing_ms, ast_format_get_minimum_bytes(format)); return -1; } - if (is_slinear) { - ast_smoother_set_flags(rtp->smoother, AST_SMOOTHER_FLAG_BE); - } + ast_smoother_set_flags(rtp->smoother, smoother_flags); } } @@ -4045,7 +4043,7 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr } while ((f = ast_smoother_read(rtp->smoother)) && (f->data.ptr)) { - ast_rtp_raw_write(instance, f, codec); + rtp_raw_write(instance, f, codec); } } else { int hdrlen = 12; @@ -4057,7 +4055,7 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr f = frame; } if (f->data.ptr) { - ast_rtp_raw_write(instance, f, codec); + rtp_raw_write(instance, f, codec); } if (f != frame) { ast_frfree(f); diff --git a/res/res_rtp_multicast.c b/res/res_rtp_multicast.c index ea313476e6..f1a80e485b 100644 --- a/res/res_rtp_multicast.c +++ b/res/res_rtp_multicast.c @@ -56,6 +56,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/format_cache.h" #include "asterisk/multicast_rtp.h" #include "asterisk/app.h" +#include "asterisk/smoother.h" /*! Command value used for Linksys paging to indicate we are starting */ #define LINKSYS_MCAST_STARTCMD 6 @@ -97,6 +98,7 @@ struct multicast_rtp { uint16_t seqno; unsigned int lastts; struct timeval txcore; + struct ast_smoother *smoother; }; enum { @@ -397,6 +399,10 @@ static int multicast_rtp_destroy(struct ast_rtp_instance *instance) multicast_send_control_packet(instance, multicast, LINKSYS_MCAST_STOPCMD); } + if (multicast->smoother) { + ast_smoother_free(multicast->smoother); + } + close(multicast->socket); ast_free(multicast); @@ -404,41 +410,24 @@ static int multicast_rtp_destroy(struct ast_rtp_instance *instance) return 0; } -/*! \brief Function called to broadcast some audio on a multicast instance */ -static int multicast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame) +static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec) { struct multicast_rtp *multicast = ast_rtp_instance_get_data(instance); - struct ast_frame *f = frame; - struct ast_sockaddr remote_address; - int hdrlen = 12, res = 0, codec; - unsigned char *rtpheader; unsigned int ms = calc_txstamp(multicast, &frame->delivery); + unsigned char *rtpheader; + struct ast_sockaddr remote_address = { {0,} }; int rate = rtp_get_rate(frame->subclass.format) / 1000; + int hdrlen = 12; - /* We only accept audio, nothing else */ - if (frame->frametype != AST_FRAME_VOICE) { - return 0; - } - - /* Grab the actual payload number for when we create the RTP packet */ - if ((codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 1, frame->subclass.format, 0)) < 0) { - return -1; - } - - /* If we do not have space to construct an RTP header duplicate the frame so we get some */ - if (frame->offset < hdrlen) { - f = ast_frdup(frame); - } - - /* Calucate last TS */ + /* Calculate last TS */ multicast->lastts = multicast->lastts + ms * rate; - + /* Construct an RTP header for our packet */ - rtpheader = (unsigned char *)(f->data.ptr - hdrlen); + rtpheader = (unsigned char *)(frame->data.ptr - hdrlen); put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (multicast->seqno))); - - if (ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO)) { - put_unaligned_uint32(rtpheader + 4, htonl(f->ts * 8)); + + if (ast_test_flag(frame, AST_FRFLAG_HAS_TIMING_INFO)) { + put_unaligned_uint32(rtpheader + 4, htonl(frame->ts * 8)); } else { put_unaligned_uint32(rtpheader + 4, htonl(multicast->lastts)); } @@ -451,19 +440,84 @@ static int multicast_rtp_write(struct ast_rtp_instance *instance, struct ast_fra /* Finally send it out to the eager phones listening for us */ ast_rtp_instance_get_remote_address(instance, &remote_address); - if (ast_sendto(multicast->socket, (void *) rtpheader, f->datalen + hdrlen, 0, &remote_address) < 0) { + if (ast_sendto(multicast->socket, (void *) rtpheader, frame->datalen + hdrlen, 0, &remote_address) < 0) { ast_log(LOG_ERROR, "Multicast RTP Transmission error to %s: %s\n", ast_sockaddr_stringify(&remote_address), strerror(errno)); - res = -1; + return -1; } - /* If we were forced to duplicate the frame free the new one */ - if (frame != f) { - ast_frfree(f); + return 0; +} + +/*! \brief Function called to broadcast some audio on a multicast instance */ +static int multicast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame) +{ + struct multicast_rtp *multicast = ast_rtp_instance_get_data(instance); + struct ast_format *format; + struct ast_frame *f; + int codec; + + /* We only accept audio, nothing else */ + if (frame->frametype != AST_FRAME_VOICE) { + return 0; } - return res; + /* Grab the actual payload number for when we create the RTP packet */ + if ((codec = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 1, frame->subclass.format, 0)) < 0) { + return -1; + } + + format = frame->subclass.format; + if (!multicast->smoother && ast_format_can_be_smoothed(format)) { + unsigned int smoother_flags = ast_format_get_smoother_flags(format); + unsigned int framing_ms = ast_rtp_codecs_get_framing(ast_rtp_instance_get_codecs(instance)); + + if (!framing_ms && (smoother_flags & AST_SMOOTHER_FLAG_FORCED)) { + framing_ms = ast_format_get_default_ms(format); + } + + if (framing_ms) { + multicast->smoother = ast_smoother_new((framing_ms * ast_format_get_minimum_bytes(format)) / ast_format_get_minimum_ms(format)); + if (!multicast->smoother) { + ast_log(LOG_WARNING, "Unable to create smoother: format %s ms: %u len %u\n", + ast_format_get_name(format), framing_ms, ast_format_get_minimum_bytes(format)); + return -1; + } + ast_smoother_set_flags(multicast->smoother, smoother_flags); + } + } + + if (multicast->smoother) { + if (ast_smoother_test_flag(multicast->smoother, AST_SMOOTHER_FLAG_BE)) { + ast_smoother_feed_be(multicast->smoother, frame); + } else { + ast_smoother_feed(multicast->smoother, frame); + } + + while ((f = ast_smoother_read(multicast->smoother)) && f->data.ptr) { + rtp_raw_write(instance, f, codec); + } + } else { + int hdrlen = 12; + + /* If we do not have space to construct an RTP header duplicate the frame so we get some */ + if (frame->offset < hdrlen) { + f = ast_frdup(frame); + } else { + f = frame; + } + + if (f->data.ptr) { + rtp_raw_write(instance, f, codec); + } + + if (f != frame) { + ast_frfree(f); + } + } + + return 0; } /*! \brief Function called to read from a multicast instance */