PR #20703 opened by Jack Lau (JackLau)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20703
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20703.patch

This patch aims to enable rtp history store for RTX

TODO:
handle the rtx send to make rtx really work


>From 90b863a2f54964009cff4dcf367e38267a0ad01b Mon Sep 17 00:00:00 2001
From: Jack Lau <[email protected]>
Date: Fri, 3 Oct 2025 10:33:55 +0800
Subject: [PATCH 1/2] avformat/whip: add rtp history store and find method

This patch aims to enable rtp history store for RTX

TODO:
handle the rtx send to make rtx really work

Signed-off-by: Jack Lau <[email protected]>
---
 doc/muxers.texi    |  4 +++
 libavformat/whip.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 70 insertions(+)

diff --git a/doc/muxers.texi b/doc/muxers.texi
index 9889bd2ff6..91dc90008c 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -3959,6 +3959,10 @@ Default value is 1200.
 Set the buffer size, in bytes, of underlying protocol.
 Default value is -1(auto). The UDP auto selects a reasonable value.
 
+@item rtp_history @var{integer}
+Set the number of RTP history items to store.
+Default value is 512.
+
 @item authorization @var{string}
 The optional Bearer token for WHIP Authorization.
 
diff --git a/libavformat/whip.c b/libavformat/whip.c
index e809075643..0f00a6652b 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -168,6 +168,19 @@
 #define WHIP_ICE_CONSENT_CHECK_INTERVAL 5000
 #define WHIP_ICE_CONSENT_EXPIRED_TIMER 30000
 
+/**
+ * RTP history packet size.
+ * Target: hold 1000ms of RTP traffic.
+ *
+ * Formula:
+ * bandwidth_bps = (RTP payload bytes) * (RTP history size) * 8
+ *
+ * Assumes average RTP payload is 1184 bytes (MTU - SRTP_CHECKSUM_LEN).
+ */
+#define WHIP_RTP_HISTORY_MIN 64 /* around 0.61 Mbps */
+#define WHIP_RTP_HISTORY_DEFAULT 512 /* around 4.85 Mbps */
+#define WHIP_RTP_HISTORY_MAX 2048 /* around 19.40 Mbps */
+
 /* Calculate the elapsed time from starttime to endtime in milliseconds. */
 #define ELAPSED(starttime, endtime) ((float)(endtime - starttime) / 1000)
 
@@ -211,6 +224,12 @@ enum WHIPState {
     WHIP_STATE_FAILED,
 };
 
+typedef struct RtpHistoryItem {
+    uint16_t seq;
+    int size;
+    uint8_t *buf;
+} RtpHistoryItem;
+
 typedef struct WHIPContext {
     AVClass *av_class;
 
@@ -329,6 +348,11 @@ typedef struct WHIPContext {
     /* The certificate and private key used for DTLS handshake. */
     char* cert_file;
     char* key_file;
+
+    int hist_sz;
+    RtpHistoryItem *hist;
+    uint8_t *hist_pool;
+    int hist_head;
 } WHIPContext;
 
 /**
@@ -418,6 +442,17 @@ static av_cold int initialize(AVFormatContext *s)
     whip->audio_first_seq = av_lfg_get(&whip->rnd) & 0x0fff;
     whip->video_first_seq = whip->audio_first_seq + 1;
 
+    whip->hist = av_calloc(whip->hist_sz, sizeof(*whip->hist));
+    if (!whip->hist)
+        return AVERROR(ENOMEM);
+
+    whip->hist_pool = av_calloc(whip->hist_sz, whip->pkt_size - 
DTLS_SRTP_CHECKSUM_LEN);
+    if (!whip->hist_pool)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < whip->hist_sz; i++)
+        whip->hist[i].buf = whip->hist_pool + i * (whip->pkt_size - 
DTLS_SRTP_CHECKSUM_LEN);
+
     if (whip->pkt_size < ideal_pkt_size)
         av_log(whip, AV_LOG_WARNING, "pkt_size=%d(<%d) is too small, may cause 
packet loss\n",
                whip->pkt_size, ideal_pkt_size);
@@ -1473,6 +1508,28 @@ end:
     return ret;
 }
 
+static int rtp_history_store(WHIPContext *whip, const uint8_t *buf, int size)
+{
+    uint16_t seq = AV_RB16(buf + 2);
+    uint32_t pos = ((uint32_t)seq - (uint32_t)whip->video_first_seq) % 
(uint32_t)whip->hist_sz;
+    RtpHistoryItem *it = &whip->hist[pos];
+    if (size > whip->pkt_size - DTLS_SRTP_CHECKSUM_LEN)
+        return AVERROR_INVALIDDATA;
+    memcpy(it->buf, buf, size);
+    it->size = size;
+    it->seq = seq;
+
+    whip->hist_head = ++pos;
+    return 0;
+}
+
+static const RtpHistoryItem *rtp_history_find(WHIPContext *whip, uint16_t seq)
+{
+    uint32_t pos = ((uint32_t)seq - (uint32_t)whip->video_first_seq) % 
(uint32_t)whip->hist_sz;
+    const RtpHistoryItem *it = &whip->hist[pos];
+    return it->seq == seq ? it : NULL;
+}
+
 /**
  * Callback triggered by the RTP muxer when it creates and sends out an RTP 
packet.
  *
@@ -1509,6 +1566,12 @@ static int on_rtp_write_packet(void *opaque, const 
uint8_t *buf, int buf_size)
         return 0;
     }
 
+    if (is_video) {
+        ret = rtp_history_store(whip, buf, buf_size);
+        if (ret < 0)
+            return ret;
+    }
+
     ret = ffurl_write(whip->udp, whip->buf, cipher_size);
     if (ret < 0) {
         av_log(whip, AV_LOG_ERROR, "Failed to write packet=%dB, ret=%d\n", 
cipher_size, ret);
@@ -1997,6 +2060,8 @@ static av_cold void whip_deinit(AVFormatContext *s)
     ff_srtp_free(&whip->srtp_recv);
     ffurl_close(whip->dtls_uc);
     ffurl_closep(&whip->udp);
+    av_freep(&whip->hist_pool);
+    av_freep(&whip->hist);
 }
 
 static int whip_check_bitstream(AVFormatContext *s, AVStream *st, const 
AVPacket *pkt)
@@ -2024,6 +2089,7 @@ static const AVOption options[] = {
     { "handshake_timeout",  "Timeout in milliseconds for ICE and DTLS 
handshake.",      OFFSET(handshake_timeout),  AV_OPT_TYPE_INT,    { .i64 = 5000 
},    -1, INT_MAX, ENC },
     { "pkt_size",           "The maximum size, in bytes, of RTP packets that 
send out", OFFSET(pkt_size),           AV_OPT_TYPE_INT,    { .i64 = 1200 },    
-1, INT_MAX, ENC },
     { "buffer_size",        "The buffer size, in bytes, of underlying 
protocol",        OFFSET(buffer_size),        AV_OPT_TYPE_INT,    { .i64 = -1 
},      -1, INT_MAX, ENC },
+    { "rtp_history",        "The number of RTP history items to store",        
         OFFSET(hist_sz),            AV_OPT_TYPE_INT,    { .i64 = 
WHIP_RTP_HISTORY_DEFAULT }, WHIP_RTP_HISTORY_MIN, WHIP_RTP_HISTORY_MAX, ENC },
     { "authorization",      "The optional Bearer token for WHIP 
Authorization",         OFFSET(authorization),      AV_OPT_TYPE_STRING, { .str 
= NULL },     0,       0, ENC },
     { "cert_file",          "The optional certificate file path for DTLS",     
         OFFSET(cert_file),          AV_OPT_TYPE_STRING, { .str = NULL },     
0,       0, ENC },
     { "key_file",           "The optional private key file path for DTLS",     
         OFFSET(key_file),      AV_OPT_TYPE_STRING, { .str = NULL },     0,     
  0, ENC },
-- 
2.49.1


>From dc5fc6315374ca8d1d113a6af4448f74fb9b55c8 Mon Sep 17 00:00:00 2001
From: Jack Lau <[email protected]>
Date: Tue, 14 Oct 2025 17:24:10 +0800
Subject: [PATCH 2/2] avformat/whip: reindent the options

Signed-off-by: Jack Lau <[email protected]>
---
 libavformat/whip.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavformat/whip.c b/libavformat/whip.c
index 0f00a6652b..8000bad3a4 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -2092,7 +2092,7 @@ static const AVOption options[] = {
     { "rtp_history",        "The number of RTP history items to store",        
         OFFSET(hist_sz),            AV_OPT_TYPE_INT,    { .i64 = 
WHIP_RTP_HISTORY_DEFAULT }, WHIP_RTP_HISTORY_MIN, WHIP_RTP_HISTORY_MAX, ENC },
     { "authorization",      "The optional Bearer token for WHIP 
Authorization",         OFFSET(authorization),      AV_OPT_TYPE_STRING, { .str 
= NULL },     0,       0, ENC },
     { "cert_file",          "The optional certificate file path for DTLS",     
         OFFSET(cert_file),          AV_OPT_TYPE_STRING, { .str = NULL },     
0,       0, ENC },
-    { "key_file",           "The optional private key file path for DTLS",     
         OFFSET(key_file),      AV_OPT_TYPE_STRING, { .str = NULL },     0,     
  0, ENC },
+    { "key_file",           "The optional private key file path for DTLS",     
         OFFSET(key_file),           AV_OPT_TYPE_STRING, { .str = NULL },     
0,       0, ENC },
     { NULL },
 };
 
-- 
2.49.1

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

Reply via email to