]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_rtp_asterisk: Add support for raising additional RTCP messages.
authorJoshua Colp <jcolp@digium.com>
Wed, 21 Mar 2018 13:52:08 +0000 (13:52 +0000)
committerJoshua Colp <jcolp@digium.com>
Tue, 27 Mar 2018 14:38:50 +0000 (08:38 -0600)
This change extends the existing AST_FRAME_RTCP frame type to be
able to contain additional RTCP message types, such as feedback
messages. The payload type is contained in the subclass which allows
knowing what is in the frame itself.

The RTCP feedback message type is now handled and REMB[1] messages
are raised with their containing information.

This also fixes a bug where all feedback messages were triggering
video updates instead of just FIR and FUR.

Finally RTCP frames are now passed up through the Asterisk core to
what is handling the channel, mapped appropriately in the case of
bridging, and written to an outgoing stream. Since RTCP frames are
on a per-stream basis this is only done on multistream capable
channels.

[1] https://tools.ietf.org/html/draft-alvestrand-rmcat-remb-03

ASTERISK-27758
ASTERISK-26366

Change-Id: I680da0ad8d5059d5e9655d896fb9d92e9da8491e

channels/chan_pjsip.c
codecs/codec_speex.c
include/asterisk/frame.h
include/asterisk/rtp_engine.h
main/bridge_channel.c
main/channel.c
res/res_rtp_asterisk.c

index 4c30d335b24df51cbc8d5d369c77ba4cae60461c..60bb9ed309d740787e635dba74380dc40cca2f39 100644 (file)
@@ -965,6 +965,8 @@ static int chan_pjsip_write_stream(struct ast_channel *ast, int stream_num, stru
                break;
        case AST_FRAME_CNG:
                break;
+       case AST_FRAME_RTCP:
+               break;
        default:
                ast_log(LOG_WARNING, "Can't send %u type frames with PJSIP\n", frame->frametype);
                break;
index 0a93596206ccce44cfe155da796a1b1e6589741a..591fce9deb7fd9ddd976ecc7dd2e54ae9ac86251 100644 (file)
@@ -372,6 +372,11 @@ static void lintospeex_feedback(struct ast_trans_pvt *pvt, struct ast_frame *fee
        if(!exp_rtcp_fb)
                return;
 
+       /* We only accept feedback information in the form of SR and RR reports */
+       if (feedback->subclass.integer != AST_RTP_RTCP_SR && feedback->subclass.integer != AST_RTP_RTCP_RR) {
+               return;
+       }
+
        rtcp_report = (struct ast_rtp_rtcp_report *)feedback->data.ptr;
        if (rtcp_report->reception_report_count == 0)
                return;
index eb6a6479a5d36b674e84e02c41474245b368031f..c3c0f88176cbafd31fa9a5ae54d0d4cb72bb3b55 100644 (file)
@@ -127,7 +127,7 @@ enum ast_frame_type {
         * directly into bridges.
         */
        AST_FRAME_BRIDGE_ACTION_SYNC,
-       /*! RTCP feedback */
+       /*! RTCP feedback (the subclass will contain the payload type) */
        AST_FRAME_RTCP,
 };
 #define AST_FRAME_DTMF AST_FRAME_DTMF_END
index 4e32d6b32f57fbdf0206df0ad480b9cf1a864a38..b552948d21cf21bfba5cf2115df4625cb91c50bf 100644 (file)
@@ -292,6 +292,14 @@ struct ast_rtp_payload_type {
 #define AST_RTP_RTCP_SR 200
 /*! Receiver Report */
 #define AST_RTP_RTCP_RR 201
+/*! Payload Specific Feed Back (From RFC4585 also RFC5104) */
+#define AST_RTP_RTCP_PSFB    206
+
+/* Common RTCP feedback message types */
+/*! Full INTRA-frame Request (From RFC5104) */
+#define AST_RTP_RTCP_FMT_FIR   4
+/*! REMB Information (From draft-alvestrand-rmcat-remb-03) */
+#define AST_RTP_RTCP_FMT_REMB  15
 
 /*!
  * \since 12
@@ -327,6 +335,24 @@ struct ast_rtp_rtcp_report {
        struct ast_rtp_rtcp_report_block *report_block[0];
 };
 
+/*!
+ * \since 15.4.0
+ * \brief A REMB feedback message (see draft-alvestrand-rmcat-remb-03 for details) */
+struct ast_rtp_rtcp_feedback_remb {
+       unsigned int br_exp;            /*!< Exponential scaling of the mantissa for the maximum total media bit rate value */
+       unsigned int br_mantissa;       /*!< The mantissa of the maximum total media bit rate */
+};
+
+/*!
+ * \since 15.4.0
+ * \brief An object that represents data received in a feedback report */
+struct ast_rtp_rtcp_feedback {
+       unsigned int fmt; /*!< The feedback message type */
+       union {
+               struct ast_rtp_rtcp_feedback_remb remb; /*!< REMB feedback information */
+       };
+};
+
 /*! Structure that represents statistics from an RTP instance */
 struct ast_rtp_instance_stats {
        /*! Number of packets transmitted */
index 89e55713fba96cb2edcd6349bc8d56fe1adb4d6b..3aac5eb254dfe0b9d88be1b2ea580ded2e22368a 100644 (file)
@@ -653,7 +653,8 @@ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel,
                case AST_FRAME_VIDEO:
                case AST_FRAME_TEXT:
                case AST_FRAME_IMAGE:
-                       /* Media frames need to be mapped to an appropriate write stream */
+               case AST_FRAME_RTCP:
+                       /* These frames need to be mapped to an appropriate write stream */
                        if (frame->stream_num < 0) {
                                /* Map to default stream */
                                frame->stream_num = -1;
index 874f7f6823f246a4bbcbf7e0a67670a3a3487713..0794020a65c315a90e94efaa35934372aae68c15 100644 (file)
@@ -4145,8 +4145,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int
                        if (ast_channel_writetrans(chan)) {
                                ast_translate(ast_channel_writetrans(chan), f, 0);
                        }
-                       ast_frfree(f);
-                       f = &ast_null_frame;
+                       break;
                default:
                        /* Just pass it on! */
                        break;
@@ -5289,6 +5288,14 @@ int ast_write_stream(struct ast_channel *chan, int stream_num, struct ast_frame
                /* Ignore these */
                res = 0;
                break;
+       case AST_FRAME_RTCP:
+               /* RTCP information is on a per-stream basis and only available on multistream capable channels */
+               if (ast_channel_tech(chan)->write_stream && stream) {
+                       res = ast_channel_tech(chan)->write_stream(chan, ast_stream_get_position(stream), fr);
+               } else {
+                       res = 0;
+               }
+               break;
        default:
                /* At this point, fr is the incoming frame and f is NULL.  Channels do
                 * not expect to get NULL as a frame pointer and will segfault.  Hence,
index d0e48240569e7278cf44267fe9f019a6b623e1b4..b010f6c51bdfd2988ba20d41aa9205ce92059fe8 100644 (file)
 #define RTCP_PT_APP     204
 /* VP8: RTCP Feedback */
 /*! Payload Specific Feed Back (From RFC4585 also RFC5104) */
-#define RTCP_PT_PSFB    206
+#define RTCP_PT_PSFB    AST_RTP_RTCP_PSFB
 
 #define RTP_MTU                1200
 #define DTMF_SAMPLE_RATE_MS    8 /*!< DTMF samples per millisecond */
@@ -5185,6 +5185,7 @@ static const char *rtcp_payload_type2str(unsigned int pt)
 #define RTCP_SR_BLOCK_WORD_LENGTH 5
 #define RTCP_RR_BLOCK_WORD_LENGTH 6
 #define RTCP_HEADER_SSRC_LENGTH   2
+#define RTCP_FB_REMB_BLOCK_WORD_LENGTH 5
 
 static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, const unsigned char *rtcpdata, size_t size, struct ast_sockaddr *addr)
 {
@@ -5266,6 +5267,7 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
                RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report, NULL, ao2_cleanup);
                struct ast_rtp_instance *child;
                struct ast_rtp *rtp;
+               struct ast_rtp_rtcp_feedback *feedback;
 
                i = position;
                first_word = ntohl(rtcpheader[i]);
@@ -5284,7 +5286,15 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
                        min_length += (rc * RTCP_RR_BLOCK_WORD_LENGTH);
                        break;
                case RTCP_PT_FUR:
+                       break;
                case RTCP_PT_PSFB:
+                       switch (rc) {
+                       case AST_RTP_RTCP_FMT_REMB:
+                               min_length += RTCP_FB_REMB_BLOCK_WORD_LENGTH;
+                               break;
+                       default:
+                               break;
+                       }
                        break;
                case RTCP_PT_SDES:
                case RTCP_PT_BYE:
@@ -5493,6 +5503,7 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
                        /* Return an AST_FRAME_RTCP frame with the ast_rtp_rtcp_report
                         * object as a its data */
                        transport_rtp->f.frametype = AST_FRAME_RTCP;
+                       transport_rtp->f.subclass.integer = pt;
                        transport_rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET;
                        memcpy(transport_rtp->f.data.ptr, rtcp_report, sizeof(struct ast_rtp_rtcp_report));
                        transport_rtp->f.datalen = sizeof(struct ast_rtp_rtcp_report);
@@ -5514,18 +5525,55 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
                        f = &transport_rtp->f;
                        break;
                case RTCP_PT_FUR:
-               /* Handle RTCP FIR as FUR */
+               /* Handle RTCP FUR as FIR by setting the format to 4 */
+                       rc = AST_RTP_RTCP_FMT_FIR;
                case RTCP_PT_PSFB:
-                       if (rtcp_debug_test_addr(addr)) {
-                               ast_verbose("Received an RTCP Fast Update Request\n");
+                       switch (rc) {
+                       case AST_RTP_RTCP_FMT_FIR:
+                               if (rtcp_debug_test_addr(addr)) {
+                                       ast_verbose("Received an RTCP Fast Update Request\n");
+                               }
+                               transport_rtp->f.frametype = AST_FRAME_CONTROL;
+                               transport_rtp->f.subclass.integer = AST_CONTROL_VIDUPDATE;
+                               transport_rtp->f.datalen = 0;
+                               transport_rtp->f.samples = 0;
+                               transport_rtp->f.mallocd = 0;
+                               transport_rtp->f.src = "RTP";
+                               f = &transport_rtp->f;
+                               break;
+                       case AST_RTP_RTCP_FMT_REMB:
+                               /* If REMB support is not enabled ignore this message */
+                               if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_REMB)) {
+                                       break;
+                               }
+
+                               if (rtcp_debug_test_addr(addr)) {
+                                       ast_verbose("Received REMB report\n");
+                               }
+                               transport_rtp->f.frametype = AST_FRAME_RTCP;
+                               transport_rtp->f.subclass.integer = pt;
+                               transport_rtp->f.stream_num = rtp->stream_num;
+                               transport_rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET;
+                               feedback = transport_rtp->f.data.ptr;
+                               feedback->fmt = rc;
+
+                               /* We don't actually care about the SSRC information in the feedback message */
+                               first_word = ntohl(rtcpheader[i + 2]);
+                               feedback->remb.br_exp = (first_word >> 18) & ((1 << 6) - 1);
+                               feedback->remb.br_mantissa = first_word & ((1 << 18) - 1);
+
+                               transport_rtp->f.datalen = sizeof(struct ast_rtp_rtcp_feedback);
+                               transport_rtp->f.offset = AST_FRIENDLY_OFFSET;
+                               transport_rtp->f.samples = 0;
+                               transport_rtp->f.mallocd = 0;
+                               transport_rtp->f.delivery.tv_sec = 0;
+                               transport_rtp->f.delivery.tv_usec = 0;
+                               transport_rtp->f.src = "RTP";
+                               f = &transport_rtp->f;
+                               break;
+                       default:
+                               break;
                        }
-                       transport_rtp->f.frametype = AST_FRAME_CONTROL;
-                       transport_rtp->f.subclass.integer = AST_CONTROL_VIDUPDATE;
-                       transport_rtp->f.datalen = 0;
-                       transport_rtp->f.samples = 0;
-                       transport_rtp->f.mallocd = 0;
-                       transport_rtp->f.src = "RTP";
-                       f = &transport_rtp->f;
                        break;
                case RTCP_PT_SDES:
                        if (rtcp_debug_test_addr(addr)) {