This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit d7820156f92a490ecff45b9e92bfe6ca2c621585
Author:     Jack Lau <[email protected]>
AuthorDate: Tue Jan 20 20:30:18 2026 +0800
Commit:     Jack Lau <[email protected]>
CommitDate: Fri Feb 27 12:42:05 2026 +0000

    avformat/whip: add RTX support
    
    See https://datatracker.ietf.org/doc/html/rfc4588
    
    Parse sequence number from NACKs, then create RTX
    packet and send it.
    
    Signed-off-by: Jack Lau <[email protected]>
    
    avformat/whip: set NACK logs as DEBUG
    
    Signed-off-by: Jack Lau <[email protected]>
---
 libavformat/whip.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 85 insertions(+), 1 deletion(-)

diff --git a/libavformat/whip.c b/libavformat/whip.c
index bc3f5a9c6d..e728464660 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -264,6 +264,8 @@ typedef struct WHIPContext {
 
     uint16_t audio_first_seq;
     uint16_t video_first_seq;
+
+    uint16_t video_rtx_seq;
     /* The PT(Payload Type) of stream, generated by the muxer. */
     uint8_t audio_payload_type;
     uint8_t video_payload_type;
@@ -1880,9 +1882,70 @@ end:
     return ret;
 }
 
+/**
+ * See https://datatracker.ietf.org/doc/html/rfc4588#section-4
+ * Create RTX packet and send it out.
+ */
+static void handle_rtx_packet(AVFormatContext *s, uint16_t seq)
+{
+    int ret = -1;
+    WHIPContext *whip = s->priv_data;
+    uint8_t *ori_buf, rtx_buf[MAX_UDP_BUFFER_SIZE] = { 0 };
+    int ori_size, rtx_size, cipher_size;
+    uint16_t ori_seq;
+    const RtpHistoryItem *it = rtp_history_find(whip, seq);
+    uint16_t latest_seq = whip->hist[(whip->hist_head - 1 + whip->hist_sz) % 
whip->hist_sz].seq;
+
+    if (!it) {
+        av_log(whip, AV_LOG_DEBUG,
+               "RTP history packet seq=%"PRIu16" not found, latest 
seq=%"PRIu16"\n",
+               seq, latest_seq);
+        return;
+    }
+    av_log(whip, AV_LOG_DEBUG,
+           "Found RTP history packet for RTX, seq=%"PRIu16", latest 
seq=%"PRIu16"\n",
+           seq, latest_seq);
+
+    ori_buf = it->buf;
+    ori_size = it->size;
+
+    /* RTX packet format: header + original seq (2 bytes) + payload */
+    if (ori_size + 2 > sizeof(rtx_buf)) {
+        av_log(whip, AV_LOG_WARNING, "RTX packet is too large, size=%d\n", 
ori_size);
+        goto end;
+    }
+
+    memcpy(rtx_buf, ori_buf, ori_size);
+    ori_seq = AV_RB16(rtx_buf + 2);
+
+    /* rewrite RTX packet header */
+    rtx_buf[1] = (rtx_buf[1] & 0x80) | whip->video_rtx_payload_type; /* keep M 
bit */
+    AV_WB16(rtx_buf + 2, whip->video_rtx_seq++);
+    AV_WB32(rtx_buf + 8, whip->video_rtx_ssrc);
+
+    /* shift payload 2 bytes to write the original seq number */
+    memmove(rtx_buf + 12 + 2, rtx_buf + 12, ori_size - 12);
+    AV_WB16(rtx_buf + 12, ori_seq);
+
+    rtx_size = ori_size + 2;
+    cipher_size = ff_srtp_encrypt(&whip->srtp_video_rtx_send,
+                                  rtx_buf, rtx_size,
+                                  whip->buf, sizeof(whip->buf));
+    if (cipher_size <= 0) {
+        av_log(whip, AV_LOG_WARNING,
+               "Failed to encrypt RTX packet, size=%d, cipher_size=%d\n",
+               rtx_size, cipher_size);
+        goto end;
+    }
+    ret = ffurl_write(whip->udp, whip->buf, cipher_size);
+end:
+    if (ret < 0)
+        av_log(whip, AV_LOG_WARNING, "Failed to send RTX packet, skip this 
one\n");
+}
+
 static void handle_nack_rtx(AVFormatContext *s, int size)
 {
-    int ret;
+    int ret, i = 0;
     WHIPContext *whip = s->priv_data;
     uint8_t *buf = NULL;
     int rtcp_len, srtcp_len, header_len = 12/*RFC 4585 6.1*/;
@@ -1910,6 +1973,27 @@ static void handle_nack_rtx(AVFormatContext *s, int size)
         av_log(whip, AV_LOG_WARNING, "NACK packet decrypt failed: %d\n", ret);
         goto error;
     }
+    while (header_len + i + 4 <= rtcp_len) {
+        /**
+         * See https://datatracker.ietf.org/doc/html/rfc4585#section-6.1
+         * Handle multi NACKs in bundled packet.
+         */
+        uint16_t pid = AV_RB16(&buf[12 + i]);
+        uint16_t blp = AV_RB16(&buf[14 + i]);
+
+        handle_rtx_packet(s, pid);
+        /* retransmit pid + any bit set in blp */
+        for (int bit = 0; bit < 16; bit++) {
+            uint16_t seq = pid + bit + 1;
+            if (!blp)
+                break;
+            if (!(blp & (1 << bit)))
+                continue;
+
+            handle_rtx_packet(s, seq);
+        }
+        i += 4;
+    }
     goto end;
 error:
     av_log(whip, AV_LOG_WARNING, "Failed to handle NACK and RTX, Skip...\n");

_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to