# 24_video_sdp_rtcp_fb.patch
#
# Advertise rtcp-fb (NACK / PLI / FIR / goog-REMB) on every video
# codec in the offered SDP so libwebrtc-based peers (Chromium /
# react-native-webrtc / sylk-mobile) enable their NACK retransmission
# AND bandwidth-estimation feedback machinery for the stream.
#
# Background
# ----------
# Before this patch pjsip's pjmedia_endpt_create_video_sdp() emitted
# an m=video line with just rtpmap + fmtp + (optional) b=TIAS — no
# a=rtcp-fb attributes at all. libwebrtc honours that by disabling
# both NACK retransmission AND REMB bandwidth-estimation feedback
# for the stream, so:
#
#   * every individual RTP packet that drops or reorders permanently
#     corrupts the surrounding frame (NACK is the recovery path),
#   * the encoder has no signal that the link can't sustain its
#     configured bitrate, so it keeps pushing the full 800 kbps even
#     when the downlink is dropping half the packets in queue.
#
# Field reproducer: Sylk-Mobile <-> Blink VP8 call, asymmetric
# downlink loss to the iPhone. Pre-patch: framesReceived=7, lost=434.
# After rtcp-fb-only-no-REMB: framesReceived≈170, nack=1631, lost=870
# still climbing — NACK works but the link is oversubscribed and
# stays oversubscribed. After adding REMB: pjsip's encoder backs off
# to the receiver-reported safe bitrate.
#
# What this patch does
# --------------------
# After each codec is added to the m=video line, also emit:
#   a=rtcp-fb:<pt> nack
#   a=rtcp-fb:<pt> nack pli
#   a=rtcp-fb:<pt> ccm fir
#   a=rtcp-fb:<pt> goog-remb
#
# pjmedia already supports SENDING and RECEIVING all four (see
# pjmedia_rtcp_fb_*); only the SDP advertisement was missing.
#
# Note on transport profile
# -------------------------
# RFC 5124 strictly mandates the RTP/AVPF profile for rtcp-fb. We do
# not change pjsip's emitted profile (still RTP/AVP), because every
# libwebrtc-based stack — and pjsip itself — accepts rtcp-fb attrs on
# RTP/AVP for backward compat.
#
--- pjsip_orig/pjmedia/src/pjmedia/endpoint.c
+++ pjsip/pjmedia/src/pjmedia/endpoint.c
@@ -877,7 +877,79 @@
 	    attr->value = pj_strdup3(pool, buf);
 	    m->attr[m->attr_count++] = attr;
 	}
-    
+
+	/*
+	 * libwebrtc-interop: advertise rtcp-fb feedback messages so
+	 * Chromium / react-native-webrtc / sylk-mobile peers enable
+	 * their NACK retransmission and PLI/FIR keyframe-request
+	 * machinery for this stream.
+	 *
+	 * Without these attributes the peer's WebRTC stack treats us
+	 * as "no feedback supported", disables NACK on the receive
+	 * path, and every individual RTP packet drop or reorder
+	 * permanently corrupts the surrounding frame — visible in the
+	 * peer's getStats() as packetsLost growing by tens per second
+	 * with framesReceived stuck near zero and only the rare
+	 * keyframe rendering cleanly.
+	 *
+	 * The three attributes we emit per codec:
+	 *   a=rtcp-fb:<pt> nack         — NACK feedback for selective
+	 *                                  RTP packet retransmission
+	 *                                  (RFC 4585).
+	 *   a=rtcp-fb:<pt> nack pli     — Picture Loss Indication
+	 *                                  (RFC 4585).
+	 *   a=rtcp-fb:<pt> ccm fir      — Full Intra Request
+	 *                                  (RFC 5104).
+	 *
+	 * pjmedia already supports SENDING and RECEIVING these on the
+	 * RTCP path (see pjmedia_rtcp_fb_*); the missing piece was
+	 * the SDP advertisement. RFC 5124 strictly says rtcp-fb
+	 * belongs to the RTP/AVPF profile, but every libwebrtc-based
+	 * stack accepts them on RTP/AVP for backward compatibility,
+	 * and existing pjsip<->pjsip interop is unaffected (pjsip's
+	 * own SDP parser treats unrecognised rtcp-fb attributes as
+	 * informational).
+	 */
+	{
+	    static const struct {
+	        const char *fb;
+	    } VIDEO_RTCP_FB[] = {
+	        { "nack" },
+	        { "nack pli" },
+	        { "ccm fir" },
+	        /*
+	         * goog-remb: Google's Receiver Estimated Maximum Bitrate
+	         * (draft-alvestrand-rmcat-remb / RTCP-PSFB AFB type 15).
+	         * libwebrtc's congestion controller reads this off the
+	         * SDP and sends periodic REMB feedback telling us how
+	         * fast we can transmit. pjmedia parses incoming REMB
+	         * via pjmedia_rtcp_fb_decode_remb; advertising it here
+	         * means the peer will actually send it. On a lossy
+	         * downlink this lets pjsip's encoder back off from its
+	         * configured cap (e.g. 800 kbps) toward the rate the
+	         * receiver can actually sustain, instead of saturating
+	         * the queue and forcing NACK-driven recovery for every
+	         * second packet.
+	         */
+	        { "goog-remb" },
+	    };
+	    unsigned k;
+	    for (k = 0; k < PJ_ARRAY_SIZE(VIDEO_RTCP_FB); ++k) {
+	        char fb_buf[64];
+	        int n = pj_ansi_snprintf(fb_buf, sizeof(fb_buf),
+	                                 "%d %s",
+	                                 codec_info[i].pt,
+	                                 VIDEO_RTCP_FB[k].fb);
+	        if (n <= 0 || (unsigned)n >= sizeof(fb_buf))
+	            continue;
+	        attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
+	        attr->name  = pj_str("rtcp-fb");
+	        attr->value = pj_strdup3(pool, fb_buf);
+	        if (m->attr_count < PJMEDIA_MAX_SDP_ATTR)
+	            m->attr[m->attr_count++] = attr;
+	    }
+	}
+
 	/* Find maximum bitrate in this media */
 	vfd = pjmedia_format_get_video_format_detail(&codec_param.enc_fmt,
 						     PJ_TRUE);
