diff --git a/include/asterisk/codec.h b/include/asterisk/codec.h index 2f5756cd14..79798acd08 100644 --- a/include/asterisk/codec.h +++ b/include/asterisk/codec.h @@ -76,6 +76,8 @@ struct ast_codec { int (*get_length)(unsigned int samples); /*! \brief Whether the media can be smoothed or not */ unsigned int smooth; + /*! \brief Flags to be passed to the smoother */ + unsigned int smoother_flags; /*! \brief The module that registered this codec */ struct ast_module *mod; }; diff --git a/include/asterisk/format.h b/include/asterisk/format.h index b01592d16e..0bad96dccc 100644 --- a/include/asterisk/format.h +++ b/include/asterisk/format.h @@ -355,6 +355,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 3320900c2e..32ec12d3d4 100644 --- a/main/codec_builtin.c +++ b/main/codec_builtin.c @@ -36,6 +36,7 @@ #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); @@ -288,6 +289,7 @@ static struct ast_codec slin8 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin12 = { @@ -302,6 +304,7 @@ static struct ast_codec slin12 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin16 = { @@ -316,6 +319,7 @@ static struct ast_codec slin16 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin24 = { @@ -330,6 +334,7 @@ static struct ast_codec slin24 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin32 = { @@ -344,6 +349,7 @@ static struct ast_codec slin32 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin44 = { @@ -358,6 +364,7 @@ static struct ast_codec slin44 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin48 = { @@ -372,6 +379,7 @@ static struct ast_codec slin48 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin96 = { @@ -386,6 +394,7 @@ static struct ast_codec slin96 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = AST_SMOOTHER_FLAG_BE | AST_SMOOTHER_FLAG_FORCED, }; static struct ast_codec slin192 = { @@ -400,6 +409,7 @@ static struct ast_codec slin192 = { .samples_count = slin_samples, .get_length = slin_length, .smooth = 1, + .smoother_flags = 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 5ae5ad9862..09e736cf5f 100644 --- a/main/format.c +++ b/main/format.c @@ -391,6 +391,11 @@ int ast_format_can_be_smoothed(const struct ast_format *format) return format->codec->smooth; } +int ast_format_get_smoother_flags(const struct ast_format *format) +{ + return format->codec->smoother_flags; +} + enum ast_media_type ast_format_get_type(const struct ast_format *format) { return format->codec->type; diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 18987cee9e..c120fc1452 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -3747,7 +3747,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; @@ -4016,10 +4016,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); } @@ -4030,9 +4030,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); } } @@ -4047,7 +4045,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; @@ -4059,7 +4057,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 42de11f65e..14176da413 100644 --- a/res/res_rtp_multicast.c +++ b/res/res_rtp_multicast.c @@ -54,6 +54,7 @@ #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 @@ -95,6 +96,7 @@ struct multicast_rtp { uint16_t seqno; unsigned int lastts; struct timeval txcore; + struct ast_smoother *smoother; }; enum { @@ -395,6 +397,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); @@ -402,16 +408,53 @@ static int multicast_rtp_destroy(struct ast_rtp_instance *instance) return 0; } +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); + 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; + + /* Calculate last TS */ + multicast->lastts = multicast->lastts + ms * rate; + + /* Construct an RTP header for our packet */ + rtpheader = (unsigned char *)(frame->data.ptr - hdrlen); + put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (multicast->seqno))); + + 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)); + } + + put_unaligned_uint32(rtpheader + 8, htonl(multicast->ssrc)); + + /* Increment sequence number and wrap to 0 if it overflows 16 bits. */ + multicast->seqno = 0xFFFF & (multicast->seqno + 1); + + /* 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, 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)); + return -1; + } + + 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_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); - int rate = rtp_get_rate(frame->subclass.format) / 1000; + struct ast_format *format; + struct ast_frame *f; + int codec; /* We only accept audio, nothing else */ if (frame->frametype != AST_FRAME_VOICE) { @@ -425,45 +468,56 @@ static int multicast_rtp_write(struct ast_rtp_instance *instance, struct ast_fra 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); + 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); + } } - - /* Calucate last TS */ - multicast->lastts = multicast->lastts + ms * rate; - - /* Construct an RTP header for our packet */ - rtpheader = (unsigned char *)(f->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 (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 { - put_unaligned_uint32(rtpheader + 4, htonl(multicast->lastts)); + 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); + } } - put_unaligned_uint32(rtpheader + 8, htonl(multicast->ssrc)); - - /* Increment sequence number and wrap to 0 if it overflows 16 bits. */ - multicast->seqno = 0xFFFF & (multicast->seqno + 1); - - /* 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) { - ast_log(LOG_ERROR, "Multicast RTP Transmission error to %s: %s\n", - ast_sockaddr_stringify(&remote_address), - strerror(errno)); - res = -1; - } - - /* If we were forced to duplicate the frame free the new one */ - if (frame != f) { - ast_frfree(f); - } - - return res; + return 0; } /*! \brief Function called to read from a multicast instance */