Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support
@ 2025-07-21 11:30 Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 01/11] avformat/whip: add whip_flags ignore_ipv6 to skip IPv6 ICE candidates Jack Lau
                   ` (10 more replies)
  0 siblings, 11 replies; 14+ messages in thread
From: Jack Lau @ 2025-07-21 11:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jack Lau

This version add DTLS active support, cleanup the ICE and DTLS code

Jack Lau (10):
  avformat/whip: add whip_flags ignore_ipv6 to skip IPv6 ICE candidates
  avformat/whip: fix typos
  avformat/whip: fix H264 profile_iop bit map for SDP
  avformat/whip: implement NACK and RTX suppport
  avformat/whip: reindent whip options
  avformat/whip: add support for active dtls role
  avformat/whip: remove DTLSState enum
  avformat/whip: check the peer whether is ice lite
  avformat/whip: remove WHIP_STATE_DTLS_CONNECTING
  avformat/whip: simplify and modularize the ICE and DTLS

winlin (1):
  WHIP: X509 cert serial number should be positive.

 libavformat/tls.h          |  15 --
 libavformat/tls_openssl.c  |  10 +-
 libavformat/tls_schannel.c |   7 -
 libavformat/whip.c         | 485 ++++++++++++++++++++++++-------------
 4 files changed, 323 insertions(+), 194 deletions(-)

-- 
2.49.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [FFmpeg-devel] [PATCH v4 01/11] avformat/whip: add whip_flags ignore_ipv6 to skip IPv6 ICE candidates
  2025-07-21 11:30 [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support Jack Lau
@ 2025-07-21 11:30 ` Jack Lau
  2025-07-21 13:53   ` Steven Liu
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 02/11] avformat/whip: fix typos Jack Lau
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 14+ messages in thread
From: Jack Lau @ 2025-07-21 11:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jack Lau

mark this ignore_ipv6 flag could ignore any IPv6 ICE candidate,
preventing “No route to host” errors on devices without IPv6 connectivity.

Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
 libavformat/whip.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/libavformat/whip.c b/libavformat/whip.c
index fd6de8503f..caac8d5680 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -193,9 +193,14 @@ enum WHIPState {
     WHIP_STATE_FAILED,
 };
 
+typedef enum WHIPFlags {
+    WHIP_FLAG_IGNORE_IPV6  = (1 << 0) // Ignore ipv6 candidate
+} WHIPFlags;
+
 typedef struct WHIPContext {
     AVClass *av_class;
 
+    uint32_t flags;        // enum WHIPFlags
     /* The state of the RTC connection. */
     enum WHIPState state;
     /* The callback return value for DTLS. */
@@ -884,6 +889,9 @@ static int parse_answer(AVFormatContext *s)
             if (ptr && av_stristr(ptr, "host")) {
                 char protocol[17], host[129];
                 int priority, port;
+#if HAVE_STRUCT_SOCKADDR_IN6
+                struct in6_addr addr6;
+#endif
                 ret = sscanf(ptr, "%16s %d %128s %d typ host", protocol, &priority, host, &port);
                 if (ret != 4) {
                     av_log(whip, AV_LOG_ERROR, "Failed %d to parse line %d %s from %s\n",
@@ -891,7 +899,12 @@ static int parse_answer(AVFormatContext *s)
                     ret = AVERROR(EIO);
                     goto end;
                 }
-
+#if HAVE_STRUCT_SOCKADDR_IN6
+                if (whip->flags & WHIP_FLAG_IGNORE_IPV6 && inet_pton(AF_INET6, host, &addr6) == 1) {
+                    av_log(whip, AV_LOG_DEBUG, "Ignoring IPv6 ICE candidates %s, line %d %s \n", host, i, line);
+                    continue;
+                }
+#endif
                 if (av_strcasecmp(protocol, "udp")) {
                     av_log(whip, AV_LOG_ERROR, "Protocol %s is not supported by RTC, choose udp, line %d %s of %s\n",
                         protocol, i, line, whip->sdp_answer);
@@ -1901,6 +1914,8 @@ static const AVOption options[] = {
     { "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 },
+    { "whip_flags",         "Set flags affecting WHIP connection behavior",             OFFSET(flags),         AV_OPT_TYPE_FLAGS,  { .i64 = 0 },                           0, UINT_MAX, ENC, .unit = "flags" },
+    { "ignore_ipv6",        "(Optional) Ignore any IPv6 ICE candidate",                 0,                     AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_IGNORE_IPV6 },       0, UINT_MAX, ENC, .unit = "flags" },
     { NULL },
 };
 
-- 
2.49.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [FFmpeg-devel] [PATCH v4 02/11] avformat/whip: fix typos
  2025-07-21 11:30 [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 01/11] avformat/whip: add whip_flags ignore_ipv6 to skip IPv6 ICE candidates Jack Lau
@ 2025-07-21 11:30 ` Jack Lau
  2025-07-21 13:52   ` Steven Liu
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 03/11] avformat/whip: fix H264 profile_iop bit map for SDP Jack Lau
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 14+ messages in thread
From: Jack Lau @ 2025-07-21 11:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jack Lau

Remove redundant "WHIP: " prefix in log context
since it already add whip context.

Fix grammers in whip options descriptions

Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
 libavformat/whip.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libavformat/whip.c b/libavformat/whip.c
index caac8d5680..3ec4c76b5f 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -1911,11 +1911,11 @@ static int whip_check_bitstream(AVFormatContext *s, AVStream *st, const AVPacket
 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 },
-    { "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 },
+    { "authorization",      "Optional Bearer token for WHIP Authorization",         OFFSET(authorization),      AV_OPT_TYPE_STRING, { .str = NULL },     0,       0, ENC },
+    { "cert_file",          "Optional certificate file path for DTLS",              OFFSET(cert_file),          AV_OPT_TYPE_STRING, { .str = NULL },     0,       0, ENC },
+    { "key_file",           "Optional private key file path for DTLS",              OFFSET(key_file),      AV_OPT_TYPE_STRING, { .str = NULL },     0,       0, ENC },
     { "whip_flags",         "Set flags affecting WHIP connection behavior",             OFFSET(flags),         AV_OPT_TYPE_FLAGS,  { .i64 = 0 },                           0, UINT_MAX, ENC, .unit = "flags" },
-    { "ignore_ipv6",        "(Optional) Ignore any IPv6 ICE candidate",                 0,                     AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_IGNORE_IPV6 },       0, UINT_MAX, ENC, .unit = "flags" },
+    { "ignore_ipv6",        "Ignore any IPv6 ICE candidate",                 0,                     AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_IGNORE_IPV6 },       0, UINT_MAX, ENC, .unit = "flags" },
     { NULL },
 };
 
-- 
2.49.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [FFmpeg-devel] [PATCH v4 03/11] avformat/whip: fix H264 profile_iop bit map for SDP
  2025-07-21 11:30 [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 01/11] avformat/whip: add whip_flags ignore_ipv6 to skip IPv6 ICE candidates Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 02/11] avformat/whip: fix typos Jack Lau
@ 2025-07-21 11:30 ` Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 04/11] WHIP: X509 cert serial number should be positive Jack Lau
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jack Lau @ 2025-07-21 11:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jack Lau

AVCodecParameters::profile only contains constraint_set1_flag
(AV_PROFILE_H264_CONSTRAINED 1<<9).
So add H264 constraints flag fully parse refer to hlsenc
write_codec_attr

Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
 libavformat/whip.c | 47 ++++++++++++++++------------------------------
 1 file changed, 16 insertions(+), 31 deletions(-)

diff --git a/libavformat/whip.c b/libavformat/whip.c
index 3ec4c76b5f..4de8eb2601 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -210,6 +210,7 @@ typedef struct WHIPContext {
     /* Parameters for the input audio and video codecs. */
     AVCodecParameters *audio_par;
     AVCodecParameters *video_par;
+    uint8_t constraint_set_flags;
 
     /**
      * The h264_mp4toannexb Bitstream Filter (BSF) bypasses the AnnexB packet;
@@ -450,45 +451,30 @@ static av_cold int initialize(AVFormatContext *s)
 static int parse_profile_level(AVFormatContext *s, AVCodecParameters *par)
 {
     int ret = 0;
-    const uint8_t *r = par->extradata, *r1, *end = par->extradata + par->extradata_size;
-    H264SPS seq, *const sps = &seq;
-    uint32_t state;
+    const uint8_t *r = par->extradata;
     WHIPContext *whip = s->priv_data;
 
     if (par->codec_id != AV_CODEC_ID_H264)
         return ret;
 
-    if (par->profile != AV_PROFILE_UNKNOWN && par->level != AV_LEVEL_UNKNOWN)
-        return ret;
-
     if (!par->extradata || par->extradata_size <= 0) {
         av_log(whip, AV_LOG_ERROR, "Unable to parse profile from empty extradata=%p, size=%d\n",
             par->extradata, par->extradata_size);
         return AVERROR(EINVAL);
     }
 
-    while (1) {
-        r = avpriv_find_start_code(r, end, &state);
-        if (r >= end)
-            break;
-
-        r1 = ff_nal_find_startcode(r, end);
-        if ((state & 0x1f) == H264_NAL_SPS) {
-            ret = ff_avc_decode_sps(sps, r, r1 - r);
-            if (ret < 0) {
-                av_log(whip, AV_LOG_ERROR, "Failed to decode SPS, state=%x, size=%d\n",
-                    state, (int)(r1 - r));
-                return ret;
-            }
-
-            av_log(whip, AV_LOG_VERBOSE, "Parse profile=%d, level=%d from SPS\n",
-                sps->profile_idc, sps->level_idc);
-            par->profile = sps->profile_idc;
-            par->level = sps->level_idc;
-        }
+    if (AV_RB32(r) == 0x00000001 && (r[4] & 0x1F) == 7)
+        r = &r[5];
+    else if (AV_RB24(r) == 0x000001 && (r[3] & 0x1F) == 7)
+        r = &r[4];
+    else if (r[0] == 0x01)  // avcC
+        r = &r[1];
+    else
+        return AVERROR(EINVAL);
 
-        r = r1;
-    }
+    if (par->profile == AV_PROFILE_UNKNOWN) par->profile = r[0];
+    whip->constraint_set_flags = r[1];
+    if (par->level == AV_LEVEL_UNKNOWN) par->level = r[2];
 
     return ret;
 }
@@ -599,7 +585,7 @@ static int parse_codec(AVFormatContext *s)
  */
 static int generate_sdp_offer(AVFormatContext *s)
 {
-    int ret = 0, profile, level, profile_iop;
+    int ret = 0, profile, level;
     const char *acodec_name = NULL, *vcodec_name = NULL;
     AVBPrint bp;
     WHIPContext *whip = s->priv_data;
@@ -667,11 +653,10 @@ static int generate_sdp_offer(AVFormatContext *s)
     }
 
     if (whip->video_par) {
-        profile_iop = profile = whip->video_par->profile;
+        profile = whip->video_par->profile;
         level = whip->video_par->level;
         if (whip->video_par->codec_id == AV_CODEC_ID_H264) {
             vcodec_name = "H264";
-            profile_iop &= AV_PROFILE_H264_CONSTRAINED;
             profile &= (~AV_PROFILE_H264_CONSTRAINED);
         }
 
@@ -699,7 +684,7 @@ static int generate_sdp_offer(AVFormatContext *s)
             vcodec_name,
             whip->video_payload_type,
             profile,
-            profile_iop,
+            whip->constraint_set_flags,
             level,
             whip->video_ssrc,
             whip->video_ssrc);
-- 
2.49.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [FFmpeg-devel] [PATCH v4 04/11] WHIP: X509 cert serial number should be positive.
  2025-07-21 11:30 [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support Jack Lau
                   ` (2 preceding siblings ...)
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 03/11] avformat/whip: fix H264 profile_iop bit map for SDP Jack Lau
@ 2025-07-21 11:30 ` Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 05/11] avformat/whip: implement NACK and RTX suppport Jack Lau
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jack Lau @ 2025-07-21 11:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: winlin, Jack Lau

From: winlin <winlinvip@gmail.com>

See RFC5280 4.1.2.2

Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
 libavformat/tls_openssl.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c
index a6b358d8f9..778595c721 100644
--- a/libavformat/tls_openssl.c
+++ b/libavformat/tls_openssl.c
@@ -328,7 +328,8 @@ static int openssl_gen_certificate(EVP_PKEY *pkey, X509 **cert, char **fingerpri
         goto enomem_end;
     }
 
-    serial = (int)av_get_random_seed();
+    // According to RFC5280 4.1.2.2, The serial number MUST be a positive integer
+    serial = (int)(av_get_random_seed() & 0x7FFFFFFF);
     if (ASN1_INTEGER_set(X509_get_serialNumber(*cert), serial) != 1) {
         av_log(NULL, AV_LOG_ERROR, "TLS: Failed to set serial, %s\n", ERR_error_string(ERR_get_error(), NULL));
         goto einval_end;
-- 
2.49.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [FFmpeg-devel] [PATCH v4 05/11] avformat/whip: implement NACK and RTX suppport
  2025-07-21 11:30 [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support Jack Lau
                   ` (3 preceding siblings ...)
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 04/11] WHIP: X509 cert serial number should be positive Jack Lau
@ 2025-07-21 11:30 ` Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 06/11] avformat/whip: reindent whip options Jack Lau
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jack Lau @ 2025-07-21 11:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Sergio Garcia Murillo, Jack Lau

RTP retransmission described in RFC4588 (RTX) is an effective packet
loss recovery technique for real-time applications with relaxed delay bounds.

This patch provides a minimal implementation for RTX and RTCP NACK (RFC3940)
and its associated SDP signaling and negotiation.

Co-authored-by: Sergio Garcia Murillo <sergio.garcia.murillo@gmail.com>
Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
 libavformat/whip.c | 206 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 202 insertions(+), 4 deletions(-)

diff --git a/libavformat/whip.c b/libavformat/whip.c
index 4de8eb2601..5907865d22 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -114,6 +114,7 @@
 /* Referring to Chrome's definition of RTP payload types. */
 #define WHIP_RTP_PAYLOAD_TYPE_H264 106
 #define WHIP_RTP_PAYLOAD_TYPE_OPUS 111
+#define WHIP_RTP_PAYLOAD_TYPE_RTX  105
 
 /**
  * The STUN message header, which is 20 bytes long, comprises the
@@ -150,6 +151,11 @@
 #define WHIP_SDP_SESSION_ID "4489045141692799359"
 #define WHIP_SDP_CREATOR_IP "127.0.0.1"
 
+/**
+ * Retransmission / NACK support
+*/
+#define HISTORY_SIZE_DEFAULT 512
+
 /* Calculate the elapsed time from starttime to endtime in milliseconds. */
 #define ELAPSED(starttime, endtime) ((int)(endtime - starttime) / 1000)
 
@@ -194,9 +200,16 @@ enum WHIPState {
 };
 
 typedef enum WHIPFlags {
-    WHIP_FLAG_IGNORE_IPV6  = (1 << 0) // Ignore ipv6 candidate
+    WHIP_FLAG_IGNORE_IPV6  = (1 << 0), // Ignore ipv6 candidate
+    WHIP_FLAG_DISABLE_RTX     = (1 << 1)  // Enable NACK and RTX
 } WHIPFlags;
 
+typedef struct RtpHistoryItem {
+        uint16_t seq; // original RTP seq
+        int size; // length in bytes
+        uint8_t* buf; // malloc-ed copy
+} RtpHistoryItem;
+
 typedef struct WHIPContext {
     AVClass *av_class;
 
@@ -285,6 +298,7 @@ typedef struct WHIPContext {
     /* The SRTP send context, to encrypt outgoing packets. */
     SRTPContext srtp_audio_send;
     SRTPContext srtp_video_send;
+    SRTPContext srtp_video_rtx_send;
     SRTPContext srtp_rtcp_send;
     /* The SRTP receive context, to decrypt incoming packets. */
     SRTPContext srtp_recv;
@@ -309,6 +323,14 @@ typedef struct WHIPContext {
     /* The certificate and private key used for DTLS handshake. */
     char* cert_file;
     char* key_file;
+
+    /* RTX and NACK */
+    uint8_t rtx_payload_type;
+    uint32_t video_rtx_ssrc;
+    uint16_t rtx_seq;
+    int  history_size;
+    RtpHistoryItem *history;  /* ring buffer  */
+    int hist_head;
 } WHIPContext;
 
 /**
@@ -611,6 +633,16 @@ static int generate_sdp_offer(AVFormatContext *s)
     whip->audio_payload_type = WHIP_RTP_PAYLOAD_TYPE_OPUS;
     whip->video_payload_type = WHIP_RTP_PAYLOAD_TYPE_H264;
 
+    /* RTX and NACK init */
+    whip->rtx_payload_type = WHIP_RTP_PAYLOAD_TYPE_RTX;
+    whip->video_rtx_ssrc = av_lfg_get(&whip->rnd);
+    whip->rtx_seq = 0;
+    whip->hist_head = 0;
+    whip->history_size = FFMAX(64, whip->history_size);
+    whip->history = av_calloc(whip->history_size, sizeof(*whip->history));
+    if (!whip->history)
+            return AVERROR(ENOMEM);
+
     av_bprintf(&bp, ""
         "v=0\r\n"
         "o=FFmpeg %s 2 IN IP4 %s\r\n"
@@ -661,7 +693,7 @@ static int generate_sdp_offer(AVFormatContext *s)
         }
 
         av_bprintf(&bp, ""
-            "m=video 9 UDP/TLS/RTP/SAVPF %u\r\n"
+            "m=video 9 UDP/TLS/RTP/SAVPF %u %u\r\n"
             "c=IN IP4 0.0.0.0\r\n"
             "a=ice-ufrag:%s\r\n"
             "a=ice-pwd:%s\r\n"
@@ -674,9 +706,16 @@ static int generate_sdp_offer(AVFormatContext *s)
             "a=rtcp-rsize\r\n"
             "a=rtpmap:%u %s/90000\r\n"
             "a=fmtp:%u level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=%02x%02x%02x\r\n"
+            "a=rtcp-fb:%u nack\r\n"
+            "a=rtpmap:%u rtx/90000\r\n"
+            "a=fmtp:%u apt=%u\r\n"
+            "a=ssrc-group:FID %u %u\r\n"
+            "a=ssrc:%u cname:FFmpeg\r\n"
+            "a=ssrc:%u msid:FFmpeg video\r\n"
             "a=ssrc:%u cname:FFmpeg\r\n"
             "a=ssrc:%u msid:FFmpeg video\r\n",
             whip->video_payload_type,
+            whip->rtx_payload_type,
             whip->ice_ufrag_local,
             whip->ice_pwd_local,
             whip->dtls_fingerprint,
@@ -686,8 +725,16 @@ static int generate_sdp_offer(AVFormatContext *s)
             profile,
             whip->constraint_set_flags,
             level,
+            whip->video_payload_type,
+            whip->rtx_payload_type,
+            whip->rtx_payload_type,
+            whip->video_payload_type,
+            whip->video_ssrc,
+            whip->video_rtx_ssrc,
             whip->video_ssrc,
-            whip->video_ssrc);
+            whip->video_ssrc,
+            whip->video_rtx_ssrc,
+            whip->video_rtx_ssrc);
     }
 
     if (!av_bprint_is_complete(&bp)) {
@@ -1400,6 +1447,12 @@ static int setup_srtp(AVFormatContext *s)
         goto end;
     }
 
+    ret = ff_srtp_set_crypto(&whip->srtp_video_rtx_send, suite, buf);
+    if (ret < 0) {
+        av_log(whip, AV_LOG_ERROR, "Failed to set crypto for video rtx send\n");
+        goto end;
+    }
+
     ret = ff_srtp_set_crypto(&whip->srtp_rtcp_send, suite, buf);
     if (ret < 0) {
         av_log(whip, AV_LOG_ERROR, "Failed to set crypto for rtcp send\n");
@@ -1429,6 +1482,38 @@ end:
     return ret;
 }
 
+
+/**
+ * RTX history helpers
+ */
+ static int rtp_history_store(WHIPContext *whip, const uint8_t *buf, int size)
+{
+    int pos = whip->hist_head % whip->history_size;
+    RtpHistoryItem *it = &whip->history[pos];
+    /* free older entry */
+    av_free(it->buf);
+    it->buf = av_malloc(size);
+    if (!it->buf)
+        return AVERROR(ENOMEM);
+
+    memcpy(it->buf, buf, size);
+    it->size = size;
+    it->seq = AV_RB16(buf + 2);
+
+    whip->hist_head = ++pos;
+    return 0;
+}
+
+static const RtpHistoryItem *rtp_history_find(const WHIPContext *whip, uint16_t seq)
+{
+    for (int i = 0; i < whip->history_size; i++) {
+        const RtpHistoryItem *it = &whip->history[i];
+        if (it->buf && it->seq == seq)
+            return it;
+    }
+    return NULL;
+}
+
 /**
  * Callback triggered by the RTP muxer when it creates and sends out an RTP packet.
  *
@@ -1465,6 +1550,12 @@ static int on_rtp_write_packet(void *opaque, const uint8_t *buf, int buf_size)
         return 0;
     }
 
+    /* Store only ORIGINAL video packets (non-RTX, non-RTCP) */
+    if (!is_rtcp && 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);
@@ -1473,6 +1564,48 @@ static int on_rtp_write_packet(void *opaque, const uint8_t *buf, int buf_size)
 
     return ret;
 }
+/**
+ * See https://datatracker.ietf.org/doc/html/rfc4588
+ * Build and send a single RTX packet
+ */
+static int send_rtx_packet(AVFormatContext *s, const uint8_t *orig_pkt_buf, int orig_size)
+{
+    int ret;
+    WHIPContext *whip = s->priv_data;
+    int new_size, cipher_size;
+    if (whip->flags & WHIP_FLAG_DISABLE_RTX)
+        return 0;
+
+    /* allocate new buffer: header + 2 + payload */
+    if (orig_size + 2 > sizeof(whip->buf))
+        return 0;
+
+    memcpy(whip->buf, orig_pkt_buf, orig_size);
+
+    uint8_t *hdr = whip->buf;
+    uint16_t orig_seq = AV_RB16(hdr + 2);
+
+    /* rewrite header */
+    hdr[1] = (hdr[1] & 0x80) | whip->rtx_payload_type; /* keep M bit */
+    AV_WB16(hdr + 2, whip->rtx_seq++);
+    AV_WB32(hdr + 8, whip->video_rtx_ssrc);
+
+    /* shift payload 2 bytes */
+    memmove(hdr + 12 + 2, hdr + 12, orig_size - 12);
+    AV_WB16(hdr + 12, orig_seq);
+
+    new_size = orig_size + 2;
+
+    /* Encrypt by SRTP and send out. */
+    cipher_size = ff_srtp_encrypt(&whip->srtp_video_rtx_send, whip->buf, new_size, whip->buf, sizeof(whip->buf));
+    if (cipher_size <= 0 || cipher_size < new_size) {
+        av_log(whip, AV_LOG_WARNING, "Failed to encrypt packet=%dB, cipher=%dB\n", new_size, cipher_size);
+        return 0;
+    }
+    ret = ffurl_write(whip->udp, whip->buf, cipher_size);
+    if (ret <= 0) av_log(whip, AV_LOG_ERROR, "Failed to send RTX packet\n");
+    return ret;
+}
 
 /**
  * Creates dedicated RTP muxers for each stream in the AVFormatContext to build RTP
@@ -1781,6 +1914,7 @@ static int whip_write_packet(AVFormatContext *s, AVPacket *pkt)
     WHIPContext *whip = s->priv_data;
     AVStream *st = s->streams[pkt->stream_index];
     AVFormatContext *rtp_ctx = st->priv_data;
+    uint8_t *buf = NULL;
 
     /* TODO: Send binding request every 1s as WebRTC heartbeat. */
 
@@ -1796,11 +1930,72 @@ static int whip_write_packet(AVFormatContext *s, AVPacket *pkt)
                 goto end;
             }
         }
+        /**
+         * Handle RTCP NACK
+         * Refer to RFC 4585, Section 6.2.1
+         * The Generic NACK message is identified by PT=RTPFB and FMT=1.
+         * TODO: disable retransmisstion when "-tune zerolatency"
+         */
+        if (media_is_rtcp(whip->buf, ret)) {
+            int ptr = 0;
+            uint8_t pt = whip->buf[ptr + 1];
+            uint8_t fmt = (whip->buf[ptr] & 0x1f);
+            if (ptr + 4 <= ret && pt == 205 && fmt == 1) {
+                /**
+                 * Refer to RFC 3550, Section 6.4.1.
+                 * The length of this RTCP packet in 32-bit words minus one,
+                 * including the header and any padding.
+                 */
+                int rtcp_len = (AV_RB16(&whip->buf[ptr + 2]) + 1) * 4;
+                /* SRTCP index(4 bytes) + HMAC (SRTP_AES128_CM_SHA1_80 10bytes) */
+                int srtcp_len = rtcp_len + 4 + 10;
+                if (srtcp_len == ret && rtcp_len >= 12) {
+                    int i = 0;
+                    buf = av_malloc(srtcp_len);
+                    if (!buf) return AVERROR(ENOMEM);
+                    memcpy(buf, whip->buf, srtcp_len);
+                    int ret = ff_srtp_decrypt(&whip->srtp_recv, buf, &srtcp_len);
+                    if (ret < 0) {
+                        av_log(whip, AV_LOG_ERROR, "NACK packet(SRTCP) decrypt failed: %d, Can't send RTX packet\n", ret);
+                        goto write_packet;
+                    }
+                    while (12 + i < 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[ptr + 12 + i]);
+                        uint16_t blp = AV_RB16(&buf[ptr + 14 + i]);
+
+                        /* retransmit pid + any bit set in blp */
+                        for (int bit = -1; bit < 16; bit++) {
+                            uint16_t seq = (bit < 0) ? pid : pid + bit + 1;
+                            if (bit >= 0 && !(blp & (1 << bit)))
+                                continue;
+
+                            const RtpHistoryItem *it = rtp_history_find(whip, seq);
+                            if (it) {
+                                av_log(whip, AV_LOG_VERBOSE,
+                                    "NACK, packet found: size: %d, seq=%d, rtx size=%d, lateset stored packet seq:%d\n",
+                                    it->size, seq, ret, whip->history[whip->hist_head-1].seq);
+                                send_rtx_packet(s, it->buf, it->size);
+                            } else {
+                                av_log(whip, AV_LOG_VERBOSE,
+                                    "NACK, packet not found, seq=%d, latest stored packet seq: %d, latest rtx seq: %d\n",
+                                    seq, whip->history[whip->hist_head-1].seq, whip->rtx_seq);
+                            }
+                        }
+                        i = i + 4;
+                    }
+                    av_freep(&buf);
+                }
+            }
+        }
     } else if (ret != AVERROR(EAGAIN)) {
         av_log(whip, AV_LOG_ERROR, "Failed to read from UDP socket\n");
         goto end;
     }
-
+write_packet:
     if (whip->h264_annexb_insert_sps_pps && st->codecpar->codec_id == AV_CODEC_ID_H264) {
         if ((ret = h264_annexb_insert_sps_pps(s, pkt)) < 0) {
             av_log(whip, AV_LOG_ERROR, "Failed to insert SPS/PPS before IDR\n");
@@ -1819,6 +2014,7 @@ static int whip_write_packet(AVFormatContext *s, AVPacket *pkt)
     }
 
 end:
+    if (buf) av_freep(&buf);
     if (ret < 0 && whip->state < WHIP_STATE_FAILED)
         whip->state = WHIP_STATE_FAILED;
     if (ret >= 0 && whip->state >= WHIP_STATE_FAILED && whip->dtls_ret < 0)
@@ -1901,6 +2097,8 @@ static const AVOption options[] = {
     { "key_file",           "Optional private key file path for DTLS",              OFFSET(key_file),      AV_OPT_TYPE_STRING, { .str = NULL },     0,       0, ENC },
     { "whip_flags",         "Set flags affecting WHIP connection behavior",             OFFSET(flags),         AV_OPT_TYPE_FLAGS,  { .i64 = 0 },                           0, UINT_MAX, ENC, .unit = "flags" },
     { "ignore_ipv6",        "Ignore any IPv6 ICE candidate",                 0,                     AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_IGNORE_IPV6 },       0, UINT_MAX, ENC, .unit = "flags" },
+    { "disable_rtx", "Disable RFC 4588 RTX", 0, AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_DISABLE_RTX }, 0, UINT_MAX, ENC, .unit = "flags" },
+    { "rtx_history_size", "Packet history size", OFFSET(history_size), AV_OPT_TYPE_INT, { .i64 = HISTORY_SIZE_DEFAULT }, 64, 2048, ENC },
     { NULL },
 };
 
-- 
2.49.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [FFmpeg-devel] [PATCH v4 06/11] avformat/whip: reindent whip options
  2025-07-21 11:30 [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support Jack Lau
                   ` (4 preceding siblings ...)
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 05/11] avformat/whip: implement NACK and RTX suppport Jack Lau
@ 2025-07-21 11:30 ` Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 07/11] avformat/whip: add support for active dtls role Jack Lau
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jack Lau @ 2025-07-21 11:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jack Lau

Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
 libavformat/whip.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/libavformat/whip.c b/libavformat/whip.c
index 5907865d22..094d3a0a4c 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -2090,13 +2090,13 @@ static int whip_check_bitstream(AVFormatContext *s, AVStream *st, const AVPacket
 #define OFFSET(x) offsetof(WHIPContext, x)
 #define ENC AV_OPT_FLAG_ENCODING_PARAM
 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 },
-    { "authorization",      "Optional Bearer token for WHIP Authorization",         OFFSET(authorization),      AV_OPT_TYPE_STRING, { .str = NULL },     0,       0, ENC },
-    { "cert_file",          "Optional certificate file path for DTLS",              OFFSET(cert_file),          AV_OPT_TYPE_STRING, { .str = NULL },     0,       0, ENC },
-    { "key_file",           "Optional private key file path for DTLS",              OFFSET(key_file),      AV_OPT_TYPE_STRING, { .str = NULL },     0,       0, ENC },
-    { "whip_flags",         "Set flags affecting WHIP connection behavior",             OFFSET(flags),         AV_OPT_TYPE_FLAGS,  { .i64 = 0 },                           0, UINT_MAX, ENC, .unit = "flags" },
-    { "ignore_ipv6",        "Ignore any IPv6 ICE candidate",                 0,                     AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_IGNORE_IPV6 },       0, UINT_MAX, ENC, .unit = "flags" },
+    { "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 },
+    { "authorization", "Optional Bearer token for WHIP Authorization", OFFSET(authorization), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, ENC },
+    { "cert_file", "Optional certificate file path for DTLS", OFFSET(cert_file), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, ENC },
+    { "key_file", "Optional private key file path for DTLS", OFFSET(key_file), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, ENC },
+    { "whip_flags", "Set flags affecting WHIP connection behavior", OFFSET(flags), AV_OPT_TYPE_FLAGS,  { .i64 = 0 }, 0, 0, ENC, .unit = "flags" },
+    { "ignore_ipv6", "Ignore any IPv6 ICE candidate", 0, AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_IGNORE_IPV6 }, 0, UINT_MAX, ENC, .unit = "flags" },
     { "disable_rtx", "Disable RFC 4588 RTX", 0, AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_DISABLE_RTX }, 0, UINT_MAX, ENC, .unit = "flags" },
     { "rtx_history_size", "Packet history size", OFFSET(history_size), AV_OPT_TYPE_INT, { .i64 = HISTORY_SIZE_DEFAULT }, 64, 2048, ENC },
     { NULL },
-- 
2.49.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [FFmpeg-devel] [PATCH v4 07/11] avformat/whip: add support for active dtls role
  2025-07-21 11:30 [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support Jack Lau
                   ` (5 preceding siblings ...)
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 06/11] avformat/whip: reindent whip options Jack Lau
@ 2025-07-21 11:30 ` Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 08/11] avformat/whip: remove DTLSState enum Jack Lau
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jack Lau @ 2025-07-21 11:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jack Lau

add dtls_active flag to specify the dtls role

properly set the send key and recv key depends on DTLS role

As DTLS server, the recv key is client master key plus salt,
the send key is server master key plus salt.
As DTLS client, the recv key is server master key plus salt,
the send key is client master key plus salt.

Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
 libavformat/whip.c | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/libavformat/whip.c b/libavformat/whip.c
index 094d3a0a4c..e02ed7a8a4 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -201,7 +201,8 @@ enum WHIPState {
 
 typedef enum WHIPFlags {
     WHIP_FLAG_IGNORE_IPV6  = (1 << 0), // Ignore ipv6 candidate
-    WHIP_FLAG_DISABLE_RTX     = (1 << 1)  // Enable NACK and RTX
+    WHIP_FLAG_DISABLE_RTX     = (1 << 1),  // Enable NACK and RTX
+    WHIP_FLAG_DTLS_ACTIVE  = (1 << 2), // DTLS active role
 } WHIPFlags;
 
 typedef struct RtpHistoryItem {
@@ -611,6 +612,7 @@ static int generate_sdp_offer(AVFormatContext *s)
     const char *acodec_name = NULL, *vcodec_name = NULL;
     AVBPrint bp;
     WHIPContext *whip = s->priv_data;
+    int is_dtls_active = whip->flags & WHIP_FLAG_DTLS_ACTIVE;
 
     /* To prevent a crash during cleanup, always initialize it. */
     av_bprint_init(&bp, 1, MAX_SDP_SIZE);
@@ -664,7 +666,7 @@ static int generate_sdp_offer(AVFormatContext *s)
             "a=ice-ufrag:%s\r\n"
             "a=ice-pwd:%s\r\n"
             "a=fingerprint:sha-256 %s\r\n"
-            "a=setup:passive\r\n"
+            "a=setup:%s\r\n"
             "a=mid:0\r\n"
             "a=sendonly\r\n"
             "a=msid:FFmpeg audio\r\n"
@@ -676,6 +678,7 @@ static int generate_sdp_offer(AVFormatContext *s)
             whip->ice_ufrag_local,
             whip->ice_pwd_local,
             whip->dtls_fingerprint,
+            is_dtls_active ? "active" : "passive",
             whip->audio_payload_type,
             acodec_name,
             whip->audio_par->sample_rate,
@@ -698,7 +701,7 @@ static int generate_sdp_offer(AVFormatContext *s)
             "a=ice-ufrag:%s\r\n"
             "a=ice-pwd:%s\r\n"
             "a=fingerprint:sha-256 %s\r\n"
-            "a=setup:passive\r\n"
+            "a=setup:%s\r\n"
             "a=mid:1\r\n"
             "a=sendonly\r\n"
             "a=msid:FFmpeg video\r\n"
@@ -719,6 +722,7 @@ static int generate_sdp_offer(AVFormatContext *s)
             whip->ice_ufrag_local,
             whip->ice_pwd_local,
             whip->dtls_fingerprint,
+            is_dtls_active ? "active" : "passive",
             whip->video_payload_type,
             vcodec_name,
             whip->video_payload_type,
@@ -1270,6 +1274,7 @@ static int ice_dtls_handshake(AVFormatContext *s)
     int ret = 0, size, i;
     int64_t starttime = av_gettime(), now;
     WHIPContext *whip = s->priv_data;
+    int is_dtls_active = whip->flags & WHIP_FLAG_DTLS_ACTIVE;
     AVDictionary *opts = NULL;
     char buf[256], *cert_buf = NULL, *key_buf = NULL;
 
@@ -1319,12 +1324,14 @@ next_packet:
                 av_usleep(5 * 1000);
                 continue;
             }
+            if (is_dtls_active)
+                break;
             av_log(whip, AV_LOG_ERROR, "Failed to read message\n");
             goto end;
         }
 
         /* Got nothing, continue to process handshake. */
-        if (ret <= 0 && whip->state < WHIP_STATE_DTLS_CONNECTING)
+        if (ret <= 0 && (is_dtls_active ? whip->state < WHIP_STATE_ICE_CONNECTED : whip->state < WHIP_STATE_DTLS_CONNECTING))
             continue;
 
         /* Handle the ICE binding response. */
@@ -1348,7 +1355,7 @@ next_packet:
                 } else
                     av_dict_set(&opts, "key_pem", whip->key_buf, 0);
                 av_dict_set_int(&opts, "external_sock", 1, 0);
-                av_dict_set_int(&opts, "listen", 1, 0);
+                av_dict_set_int(&opts, "listen", is_dtls_active ? 0 : 1, 0);
                 /* If got the first binding response, start DTLS handshake. */
                 ret = ffurl_open_whitelist(&whip->dtls_uc, buf, AVIO_FLAG_READ_WRITE, &s->interrupt_callback,
                     &opts, s->protocol_whitelist, s->protocol_blacklist, NULL);
@@ -1368,7 +1375,7 @@ next_packet:
         }
 
         /* If got any DTLS messages, handle it. */
-        if (is_dtls_packet(whip->buf, ret) && whip->state >= WHIP_STATE_ICE_CONNECTED || whip->state == WHIP_STATE_DTLS_CONNECTING) {
+        if ((is_dtls_packet(whip->buf, ret) || is_dtls_active) && whip->state >= WHIP_STATE_ICE_CONNECTED || whip->state == WHIP_STATE_DTLS_CONNECTING) {
             whip->state = WHIP_STATE_DTLS_CONNECTING;
             if ((ret = ffurl_handshake(whip->dtls_uc)) < 0)
                 goto end;
@@ -1406,6 +1413,8 @@ static int setup_srtp(AVFormatContext *s)
      */
     const char* suite = "SRTP_AES128_CM_HMAC_SHA1_80";
     WHIPContext *whip = s->priv_data;
+    int is_dtls_active = whip->flags & WHIP_FLAG_DTLS_ACTIVE;
+
     ret = ff_dtls_export_materials(whip->dtls_uc, whip->dtls_srtp_materials, sizeof(whip->dtls_srtp_materials));
     if (ret < 0)
         goto end;
@@ -1420,13 +1429,11 @@ static int setup_srtp(AVFormatContext *s)
     char *client_salt = server_key + DTLS_SRTP_KEY_LEN;
     char *server_salt = client_salt + DTLS_SRTP_SALT_LEN;
 
-    /* As DTLS server, the recv key is client master key plus salt. */
-    memcpy(recv_key, client_key, DTLS_SRTP_KEY_LEN);
-    memcpy(recv_key + DTLS_SRTP_KEY_LEN, client_salt, DTLS_SRTP_SALT_LEN);
+    memcpy(is_dtls_active ? send_key : recv_key, client_key, DTLS_SRTP_KEY_LEN);
+    memcpy(is_dtls_active ? send_key + DTLS_SRTP_KEY_LEN : recv_key + DTLS_SRTP_KEY_LEN, client_salt, DTLS_SRTP_SALT_LEN);
 
-    /* As DTLS server, the send key is server master key plus salt. */
-    memcpy(send_key, server_key, DTLS_SRTP_KEY_LEN);
-    memcpy(send_key + DTLS_SRTP_KEY_LEN, server_salt, DTLS_SRTP_SALT_LEN);
+    memcpy(is_dtls_active ? recv_key : send_key, server_key, DTLS_SRTP_KEY_LEN);
+    memcpy(is_dtls_active ? recv_key + DTLS_SRTP_KEY_LEN : send_key + DTLS_SRTP_KEY_LEN, server_salt, DTLS_SRTP_SALT_LEN);
 
     /* Setup SRTP context for outgoing packets */
     if (!av_base64_encode(buf, sizeof(buf), send_key, sizeof(send_key))) {
@@ -2098,6 +2105,7 @@ static const AVOption options[] = {
     { "whip_flags", "Set flags affecting WHIP connection behavior", OFFSET(flags), AV_OPT_TYPE_FLAGS,  { .i64 = 0 }, 0, 0, ENC, .unit = "flags" },
     { "ignore_ipv6", "Ignore any IPv6 ICE candidate", 0, AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_IGNORE_IPV6 }, 0, UINT_MAX, ENC, .unit = "flags" },
     { "disable_rtx", "Disable RFC 4588 RTX", 0, AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_DISABLE_RTX }, 0, UINT_MAX, ENC, .unit = "flags" },
+    { "dtls_active", "Set dtls role as active", 0, AV_OPT_TYPE_CONST, { .i64 = WHIP_FLAG_DTLS_ACTIVE }, 0, UINT_MAX, ENC, .unit = "flags" },
     { "rtx_history_size", "Packet history size", OFFSET(history_size), AV_OPT_TYPE_INT, { .i64 = HISTORY_SIZE_DEFAULT }, 64, 2048, ENC },
     { NULL },
 };
-- 
2.49.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [FFmpeg-devel] [PATCH v4 08/11] avformat/whip: remove DTLSState enum
  2025-07-21 11:30 [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support Jack Lau
                   ` (6 preceding siblings ...)
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 07/11] avformat/whip: add support for active dtls role Jack Lau
@ 2025-07-21 11:30 ` Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 09/11] avformat/whip: check the peer whether is ice lite Jack Lau
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jack Lau @ 2025-07-21 11:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jack Lau

This patch aims to simplify the dtls handshake process
since dtls handshake use force block mode

We can just use the return code instead of DTLSState enum

Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
 libavformat/tls.h          | 15 ----------
 libavformat/tls_openssl.c  |  7 -----
 libavformat/tls_schannel.c |  7 -----
 libavformat/whip.c         | 59 ++++++++------------------------------
 4 files changed, 12 insertions(+), 76 deletions(-)

diff --git a/libavformat/tls.h b/libavformat/tls.h
index 0c02a4ab27..157c0d0256 100644
--- a/libavformat/tls.h
+++ b/libavformat/tls.h
@@ -33,17 +33,6 @@
  */
 #define MAX_CERTIFICATE_SIZE 8192
 
-enum DTLSState {
-    DTLS_STATE_NONE,
-
-    /* Whether DTLS handshake is finished. */
-    DTLS_STATE_FINISHED,
-    /* Whether DTLS session is closed. */
-    DTLS_STATE_CLOSED,
-    /* Whether DTLS handshake is failed. */
-    DTLS_STATE_FAILED,
-};
-
 typedef struct TLSShared {
     char *ca_file;
     int verify;
@@ -63,8 +52,6 @@ typedef struct TLSShared {
 
     int is_dtls;
 
-    enum DTLSState state;
-
     /* The certificate and private key content used for DTLS handshake */
     char* cert_buf;
     char* key_buf;
@@ -103,8 +90,6 @@ int ff_tls_set_external_socket(URLContext *h, URLContext *sock);
 
 int ff_dtls_export_materials(URLContext *h, char *dtls_srtp_materials, size_t materials_sz);
 
-int ff_dtls_state(URLContext *h);
-
 int ff_ssl_read_key_cert(char *key_url, char *cert_url, char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint);
 
 int ff_ssl_gen_key_cert(char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint);
diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c
index 778595c721..c3427a18e8 100644
--- a/libavformat/tls_openssl.c
+++ b/libavformat/tls_openssl.c
@@ -540,12 +540,6 @@ int ff_dtls_export_materials(URLContext *h, char *dtls_srtp_materials, size_t ma
     return 0;
 }
 
-int ff_dtls_state(URLContext *h)
-{
-    TLSContext *c = h->priv_data;
-    return c->tls_shared.state;
-}
-
 static int print_ssl_error(URLContext *h, int ret)
 {
     TLSContext *c = h->priv_data;
@@ -724,7 +718,6 @@ static int dtls_handshake(URLContext *h)
         goto end;
 
     ret = 0;
-    p->tls_shared.state = DTLS_STATE_FINISHED;
 end:
     return ret;
 }
diff --git a/libavformat/tls_schannel.c b/libavformat/tls_schannel.c
index c92870347f..50f35acdd6 100644
--- a/libavformat/tls_schannel.c
+++ b/libavformat/tls_schannel.c
@@ -681,12 +681,6 @@ int ff_dtls_export_materials(URLContext *h, char *dtls_srtp_materials, size_t ma
 #endif
 }
 
-int ff_dtls_state(URLContext *h)
-{
-    TLSContext *c = h->priv_data;
-    return c->tls_shared.state;
-}
-
 static void init_sec_buffer(SecBuffer *buffer, unsigned long type,
                             void *data, unsigned long size)
 {
@@ -1111,7 +1105,6 @@ static int tls_handshake(URLContext *h)
 #endif
 
     c->connected = 1;
-    s->state = DTLS_STATE_FINISHED;
 
 fail:
     return ret;
diff --git a/libavformat/whip.c b/libavformat/whip.c
index e02ed7a8a4..fe35ab7205 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -217,9 +217,6 @@ typedef struct WHIPContext {
     uint32_t flags;        // enum WHIPFlags
     /* The state of the RTC connection. */
     enum WHIPState state;
-    /* The callback return value for DTLS. */
-    int dtls_ret;
-    int dtls_closed;
 
     /* Parameters for the input audio and video codecs. */
     AVCodecParameters *audio_par;
@@ -377,41 +374,6 @@ static av_cold int certificate_key_init(AVFormatContext *s)
     return ret;
 }
 
-/**
- * When DTLS state change.
- */
-static int dtls_context_on_state(AVFormatContext *s, const char* type, const char* desc)
-{
-    int ret = 0;
-    WHIPContext *whip = s->priv_data;
-    int state = ff_dtls_state(whip->dtls_uc);
-
-    if (state == DTLS_STATE_CLOSED) {
-        whip->dtls_closed = 1;
-        av_log(whip, AV_LOG_VERBOSE, "DTLS session closed, type=%s, desc=%s, elapsed=%dms\n",
-            type ? type : "", desc ? desc : "", ELAPSED(whip->whip_starttime, av_gettime()));
-        goto error;
-    }
-
-    if (state == DTLS_STATE_FAILED) {
-        whip->state = WHIP_STATE_FAILED;
-        av_log(whip, AV_LOG_ERROR, "DTLS session failed, type=%s, desc=%s\n",
-            type ? type : "", desc ? desc : "");
-        whip->dtls_ret = AVERROR(EIO);
-        goto error;
-    }
-
-    if (state == DTLS_STATE_FINISHED && whip->state < WHIP_STATE_DTLS_FINISHED) {
-        whip->state = WHIP_STATE_DTLS_FINISHED;
-        whip->whip_dtls_time = av_gettime();
-        av_log(whip, AV_LOG_VERBOSE, "DTLS handshake is done, elapsed=%dms\n",
-            ELAPSED(whip->whip_starttime, av_gettime()));
-        return ret;
-    }
-error:
-    return -1;
-}
-
 static av_cold int dtls_initialize(AVFormatContext *s)
 {
     WHIPContext *whip = s->priv_data;
@@ -1377,9 +1339,18 @@ next_packet:
         /* If got any DTLS messages, handle it. */
         if ((is_dtls_packet(whip->buf, ret) || is_dtls_active) && whip->state >= WHIP_STATE_ICE_CONNECTED || whip->state == WHIP_STATE_DTLS_CONNECTING) {
             whip->state = WHIP_STATE_DTLS_CONNECTING;
-            if ((ret = ffurl_handshake(whip->dtls_uc)) < 0)
+            ret = ffurl_handshake(whip->dtls_uc);
+            if (ret < 0) {
+                whip->state = WHIP_STATE_FAILED;
+                av_log(whip, AV_LOG_VERBOSE, "DTLS session failed\n");
                 goto end;
-            dtls_context_on_state(s, NULL, NULL);
+            }
+            if (!ret) {
+                whip->state = WHIP_STATE_DTLS_FINISHED;
+                whip->whip_dtls_time = av_gettime();
+                av_log(whip, AV_LOG_VERBOSE, "DTLS handshake is done, elapsed=%dms\n",
+                    ELAPSED(whip->whip_starttime, whip->whip_dtls_time));
+            }
             goto next_packet;
         }
     }
@@ -1910,8 +1881,6 @@ static av_cold int whip_init(AVFormatContext *s)
 end:
     if (ret < 0 && whip->state < WHIP_STATE_FAILED)
         whip->state = WHIP_STATE_FAILED;
-    if (ret >= 0 && whip->state >= WHIP_STATE_FAILED && whip->dtls_ret < 0)
-        ret = whip->dtls_ret;
     return ret;
 }
 
@@ -2022,12 +1991,8 @@ write_packet:
 
 end:
     if (buf) av_freep(&buf);
-    if (ret < 0 && whip->state < WHIP_STATE_FAILED)
+    if (ret < 0)
         whip->state = WHIP_STATE_FAILED;
-    if (ret >= 0 && whip->state >= WHIP_STATE_FAILED && whip->dtls_ret < 0)
-        ret = whip->dtls_ret;
-    if (ret >= 0 && whip->dtls_closed)
-        ret = AVERROR(EIO);
     return ret;
 }
 
-- 
2.49.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [FFmpeg-devel] [PATCH v4 09/11] avformat/whip: check the peer whether is ice lite
  2025-07-21 11:30 [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support Jack Lau
                   ` (7 preceding siblings ...)
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 08/11] avformat/whip: remove DTLSState enum Jack Lau
@ 2025-07-21 11:30 ` Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 10/11] avformat/whip: remove WHIP_STATE_DTLS_CONNECTING Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 11/11] avformat/whip: simplify and modularize the ICE and DTLS Jack Lau
  10 siblings, 0 replies; 14+ messages in thread
From: Jack Lau @ 2025-07-21 11:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jack Lau

See RFC 5245 Section 4.3:
If an agent is a lite implementation, it MUST include an "a=ice-lite"
session-level attribute in its SDP.  If an agent is a full
implementation, it MUST NOT include this attribute.

Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
 libavformat/whip.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libavformat/whip.c b/libavformat/whip.c
index fe35ab7205..d37a61042f 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -248,6 +248,7 @@ typedef struct WHIPContext {
      */
     char *sdp_offer;
 
+    int is_peer_ice_lite;
     /* The ICE username and pwd from remote server. */
     char *ice_ufrag_remote;
     char *ice_pwd_remote;
@@ -870,6 +871,8 @@ static int parse_answer(AVFormatContext *s)
 
     for (i = 0; !avio_feof(pb); i++) {
         ff_get_chomp_line(pb, line, sizeof(line));
+        if (av_strstart(line, "a=ice-lite", &ptr))
+            whip->is_peer_ice_lite = 1;
         if (av_strstart(line, "a=ice-ufrag:", &ptr) && !whip->ice_ufrag_remote) {
             whip->ice_ufrag_remote = av_strdup(ptr);
             if (!whip->ice_ufrag_remote) {
-- 
2.49.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [FFmpeg-devel] [PATCH v4 10/11] avformat/whip: remove WHIP_STATE_DTLS_CONNECTING
  2025-07-21 11:30 [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support Jack Lau
                   ` (8 preceding siblings ...)
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 09/11] avformat/whip: check the peer whether is ice lite Jack Lau
@ 2025-07-21 11:30 ` Jack Lau
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 11/11] avformat/whip: simplify and modularize the ICE and DTLS Jack Lau
  10 siblings, 0 replies; 14+ messages in thread
From: Jack Lau @ 2025-07-21 11:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jack Lau

This value is only useful when dtls handshake is NONBLOCK mode,
dtls handshake just need to call ffurl_handshake once since it
force block mode.

Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
 libavformat/whip.c | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/libavformat/whip.c b/libavformat/whip.c
index d37a61042f..4c8ed26cef 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -187,8 +187,6 @@ enum WHIPState {
     WHIP_STATE_ICE_CONNECTING,
     /* The muxer has received the ICE response from the peer. */
     WHIP_STATE_ICE_CONNECTED,
-    /* The muxer starts attempting the DTLS handshake. */
-    WHIP_STATE_DTLS_CONNECTING,
     /* The muxer has finished the DTLS handshake with the peer. */
     WHIP_STATE_DTLS_FINISHED,
     /* The muxer has finished the SRTP setup. */
@@ -1281,7 +1279,7 @@ next_packet:
         }
 
         /* Read the STUN or DTLS messages from peer. */
-        for (i = 0; i < ICE_DTLS_READ_INTERVAL / 5 && whip->state < WHIP_STATE_DTLS_CONNECTING; i++) {
+        for (i = 0; i < ICE_DTLS_READ_INTERVAL / 5 && whip->state < WHIP_STATE_ICE_CONNECTED; i++) {
             ret = ffurl_read(whip->udp, whip->buf, sizeof(whip->buf));
             if (ret > 0)
                 break;
@@ -1295,14 +1293,11 @@ next_packet:
             goto end;
         }
 
-        /* Got nothing, continue to process handshake. */
-        if (ret <= 0 && (is_dtls_active ? whip->state < WHIP_STATE_ICE_CONNECTED : whip->state < WHIP_STATE_DTLS_CONNECTING))
-            continue;
-
         /* Handle the ICE binding response. */
         if (ice_is_binding_response(whip->buf, ret)) {
             if (whip->state < WHIP_STATE_ICE_CONNECTED) {
-                whip->state = WHIP_STATE_ICE_CONNECTED;
+                if (whip->is_peer_ice_lite)
+                    whip->state = WHIP_STATE_ICE_CONNECTED;
                 whip->whip_ice_time = av_gettime();
                 av_log(whip, AV_LOG_VERBOSE, "ICE STUN ok, state=%d, url=udp://%s:%d, location=%s, username=%s:%s, res=%dB, elapsed=%dms\n",
                     whip->state, whip->ice_host, whip->ice_port, whip->whip_resource_url ? whip->whip_resource_url : "",
@@ -1332,16 +1327,15 @@ next_packet:
             goto next_packet;
         }
 
-        /* When a binding request is received, it is necessary to respond immediately. */
+        /* See RFC8445, Triggered check when the peer is ice full mode */
         if (ice_is_binding_request(whip->buf, ret)) {
             if ((ret = ice_handle_binding_request(s, whip->buf, ret)) < 0)
                 goto end;
             goto next_packet;
         }
 
-        /* If got any DTLS messages, handle it. */
-        if ((is_dtls_packet(whip->buf, ret) || is_dtls_active) && whip->state >= WHIP_STATE_ICE_CONNECTED || whip->state == WHIP_STATE_DTLS_CONNECTING) {
-            whip->state = WHIP_STATE_DTLS_CONNECTING;
+        if ((is_dtls_packet(whip->buf, ret) || is_dtls_active) && whip->state >= WHIP_STATE_ICE_CONNECTED || whip->state == WHIP_STATE_ICE_CONNECTING) {
+            whip->state = WHIP_STATE_ICE_CONNECTED;
             ret = ffurl_handshake(whip->dtls_uc);
             if (ret < 0) {
                 whip->state = WHIP_STATE_FAILED;
-- 
2.49.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [FFmpeg-devel] [PATCH v4 11/11] avformat/whip: simplify and modularize the ICE and DTLS
  2025-07-21 11:30 [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support Jack Lau
                   ` (9 preceding siblings ...)
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 10/11] avformat/whip: remove WHIP_STATE_DTLS_CONNECTING Jack Lau
@ 2025-07-21 11:30 ` Jack Lau
  10 siblings, 0 replies; 14+ messages in thread
From: Jack Lau @ 2025-07-21 11:30 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jack Lau

We try to perform dtls handshake when the ICE is totally done.

Refer to RFC8445,
When peer's ICE is lite, the peer won't trigged check so FFmpeg just send
STUN request and receive response, then ICE is done.
When peer's ICE is full, the peer will send STUN request after reponse
FFmpeg's request to ensure candidate pair become valid in both directions.
Then the peer does nomination, and ICE is done.

Signed-off-by: Jack Lau <jacklau1222@qq.com>
---
 libavformat/whip.c | 135 +++++++++++++++++++++------------------------
 1 file changed, 62 insertions(+), 73 deletions(-)

diff --git a/libavformat/whip.c b/libavformat/whip.c
index 4c8ed26cef..cfcb8e8888 100644
--- a/libavformat/whip.c
+++ b/libavformat/whip.c
@@ -373,19 +373,6 @@ static av_cold int certificate_key_init(AVFormatContext *s)
     return ret;
 }
 
-static av_cold int dtls_initialize(AVFormatContext *s)
-{
-    WHIPContext *whip = s->priv_data;
-    /* reuse the udp created by whip */
-    ff_tls_set_external_socket(whip->dtls_uc, whip->udp);
-
-    /* Make the socket non-blocking */
-    ff_socket_nonblock(ffurl_get_file_handle(whip->dtls_uc), 1);
-    whip->dtls_uc->flags |= AVIO_FLAG_NONBLOCK;
-
-    return 0;
-}
-
 /**
  * Initialize and check the options for the WebRTC muxer.
  */
@@ -1232,14 +1219,12 @@ end:
     return ret;
 }
 
-static int ice_dtls_handshake(AVFormatContext *s)
+static int ice_handshake(AVFormatContext *s)
 {
     int ret = 0, size, i;
     int64_t starttime = av_gettime(), now;
     WHIPContext *whip = s->priv_data;
     int is_dtls_active = whip->flags & WHIP_FLAG_DTLS_ACTIVE;
-    AVDictionary *opts = NULL;
-    char buf[256], *cert_buf = NULL, *key_buf = NULL;
 
     if (whip->state < WHIP_STATE_UDP_CONNECTED || !whip->udp) {
         av_log(whip, AV_LOG_ERROR, "UDP not connected, state=%d, udp=%p\n", whip->state, whip->udp);
@@ -1261,25 +1246,20 @@ static int ice_dtls_handshake(AVFormatContext *s)
                 goto end;
             }
 
-            if (whip->state < WHIP_STATE_ICE_CONNECTING)
-                whip->state = WHIP_STATE_ICE_CONNECTING;
+            whip->state = WHIP_STATE_ICE_CONNECTING;
         }
 
 next_packet:
-        if (whip->state >= WHIP_STATE_DTLS_FINISHED)
-            /* DTLS handshake is done, exit the loop. */
-            break;
-
         now = av_gettime();
         if (now - starttime >= whip->handshake_timeout * 1000) {
-            av_log(whip, AV_LOG_ERROR, "DTLS handshake timeout=%dms, cost=%dms, elapsed=%dms, state=%d\n",
+            av_log(whip, AV_LOG_ERROR, "ICE handshake timeout=%dms, cost=%dms, elapsed=%dms, state=%d\n",
                 whip->handshake_timeout, ELAPSED(starttime, now), ELAPSED(whip->whip_starttime, now), whip->state);
             ret = AVERROR(ETIMEDOUT);
             goto end;
         }
 
-        /* Read the STUN or DTLS messages from peer. */
-        for (i = 0; i < ICE_DTLS_READ_INTERVAL / 5 && whip->state < WHIP_STATE_ICE_CONNECTED; i++) {
+        /* Read the STUN or DTLS client hello from peer. */
+        for (i = 0; i < ICE_DTLS_READ_INTERVAL / 5; i++) {
             ret = ffurl_read(whip->udp, whip->buf, sizeof(whip->buf));
             if (ret > 0)
                 break;
@@ -1295,35 +1275,8 @@ next_packet:
 
         /* Handle the ICE binding response. */
         if (ice_is_binding_response(whip->buf, ret)) {
-            if (whip->state < WHIP_STATE_ICE_CONNECTED) {
-                if (whip->is_peer_ice_lite)
-                    whip->state = WHIP_STATE_ICE_CONNECTED;
-                whip->whip_ice_time = av_gettime();
-                av_log(whip, AV_LOG_VERBOSE, "ICE STUN ok, state=%d, url=udp://%s:%d, location=%s, username=%s:%s, res=%dB, elapsed=%dms\n",
-                    whip->state, whip->ice_host, whip->ice_port, whip->whip_resource_url ? whip->whip_resource_url : "",
-                    whip->ice_ufrag_remote, whip->ice_ufrag_local, ret, ELAPSED(whip->whip_starttime, av_gettime()));
-
-                ff_url_join(buf, sizeof(buf), "dtls", NULL, whip->ice_host, whip->ice_port, NULL);
-                av_dict_set_int(&opts, "mtu", whip->pkt_size, 0);
-                if (whip->cert_file) {
-                    av_dict_set(&opts, "cert_file", whip->cert_file, 0);
-                } else
-                    av_dict_set(&opts, "cert_pem", whip->cert_buf, 0);
-
-                if (whip->key_file) {
-                    av_dict_set(&opts, "key_file", whip->key_file, 0);
-                } else
-                    av_dict_set(&opts, "key_pem", whip->key_buf, 0);
-                av_dict_set_int(&opts, "external_sock", 1, 0);
-                av_dict_set_int(&opts, "listen", is_dtls_active ? 0 : 1, 0);
-                /* If got the first binding response, start DTLS handshake. */
-                ret = ffurl_open_whitelist(&whip->dtls_uc, buf, AVIO_FLAG_READ_WRITE, &s->interrupt_callback,
-                    &opts, s->protocol_whitelist, s->protocol_blacklist, NULL);
-                av_dict_free(&opts);
-                if (ret < 0)
-                    goto end;
-                dtls_initialize(s);
-            }
+            if (whip->is_peer_ice_lite)
+                whip->state = WHIP_STATE_ICE_CONNECTED;
             goto next_packet;
         }
 
@@ -1334,29 +1287,62 @@ next_packet:
             goto next_packet;
         }
 
-        if ((is_dtls_packet(whip->buf, ret) || is_dtls_active) && whip->state >= WHIP_STATE_ICE_CONNECTED || whip->state == WHIP_STATE_ICE_CONNECTING) {
+        if (is_dtls_packet(whip->buf, ret) || whip->flags & WHIP_FLAG_DTLS_ACTIVE) {
             whip->state = WHIP_STATE_ICE_CONNECTED;
-            ret = ffurl_handshake(whip->dtls_uc);
-            if (ret < 0) {
-                whip->state = WHIP_STATE_FAILED;
-                av_log(whip, AV_LOG_VERBOSE, "DTLS session failed\n");
-                goto end;
-            }
-            if (!ret) {
-                whip->state = WHIP_STATE_DTLS_FINISHED;
-                whip->whip_dtls_time = av_gettime();
-                av_log(whip, AV_LOG_VERBOSE, "DTLS handshake is done, elapsed=%dms\n",
-                    ELAPSED(whip->whip_starttime, whip->whip_dtls_time));
-            }
-            goto next_packet;
+            ret = 0;
+            whip->whip_ice_time = av_gettime();
+            av_log(whip, AV_LOG_VERBOSE, "ICE STUN ok, state=%d, url=udp://%s:%d, location=%s, username=%s:%s, res=%dB, elapsed=%dms\n",
+                whip->state, whip->ice_host, whip->ice_port, whip->whip_resource_url ? whip->whip_resource_url : "",
+                whip->ice_ufrag_remote, whip->ice_ufrag_local, ret, ELAPSED(whip->whip_starttime, av_gettime()));
+            break;
         }
     }
+end:
+    return ret;
+}
 
+static int dtls_handshake(AVFormatContext *s)
+{
+    int ret = 0;
+    WHIPContext *whip = s->priv_data;
+    AVDictionary *opts = NULL;
+    char buf[256];
+
+    ff_url_join(buf, sizeof(buf), "dtls", NULL, whip->ice_host, whip->ice_port, NULL);
+    av_dict_set_int(&opts, "mtu", whip->pkt_size, 0);
+    if (whip->cert_file) {
+        av_dict_set(&opts, "cert_file", whip->cert_file, 0);
+    } else
+        av_dict_set(&opts, "cert_pem", whip->cert_buf, 0);
+
+    if (whip->key_file) {
+        av_dict_set(&opts, "key_file", whip->key_file, 0);
+    } else
+        av_dict_set(&opts, "key_pem", whip->key_buf, 0);
+    av_dict_set_int(&opts, "external_sock", 1, 0);
+    av_dict_set_int(&opts, "listen", whip->flags & WHIP_FLAG_DTLS_ACTIVE ? 0 : 1, 0);
+    /* If got the first binding response, start DTLS handshake. */
+    ret = ffurl_open_whitelist(&whip->dtls_uc, buf, AVIO_FLAG_READ_WRITE, &s->interrupt_callback,
+        &opts, s->protocol_whitelist, s->protocol_blacklist, NULL);
+    av_dict_free(&opts);
+    if (ret < 0)
+        goto end;
+        
+    /* reuse the udp created by whip */
+    ff_tls_set_external_socket(whip->dtls_uc, whip->udp);
+    
+    ret = ffurl_handshake(whip->dtls_uc);
+    if (ret < 0) {
+        whip->state = WHIP_STATE_FAILED;
+        av_log(whip, AV_LOG_VERBOSE, "DTLS session failed\n");
+    }
+    if (!ret) {
+        whip->state = WHIP_STATE_DTLS_FINISHED;
+        whip->whip_dtls_time = av_gettime();
+        av_log(whip, AV_LOG_VERBOSE, "DTLS handshake is done, elapsed=%dms\n",
+            ELAPSED(whip->whip_starttime, whip->whip_dtls_time));
+    }
 end:
-    if (cert_buf)
-        av_free(cert_buf);
-    if (key_buf)
-        av_free(key_buf);
     return ret;
 }
 
@@ -1866,7 +1852,10 @@ static av_cold int whip_init(AVFormatContext *s)
     if ((ret = udp_connect(s)) < 0)
         goto end;
 
-    if ((ret = ice_dtls_handshake(s)) < 0)
+    if ((ret = ice_handshake(s)) < 0)
+        goto end;
+    
+    if ((ret = dtls_handshake(s)) < 0)
         goto end;
 
     if ((ret = setup_srtp(s)) < 0)
-- 
2.49.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [FFmpeg-devel] [PATCH v4 02/11] avformat/whip: fix typos
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 02/11] avformat/whip: fix typos Jack Lau
@ 2025-07-21 13:52   ` Steven Liu
  0 siblings, 0 replies; 14+ messages in thread
From: Steven Liu @ 2025-07-21 13:52 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Jack Lau

Jack Lau <jacklau1222gm@gmail.com> 于2025年7月21日周一 19:31写道:
Hi Jack,
>
> Remove redundant "WHIP: " prefix in log context
> since it already add whip context.
>
> Fix grammers in whip options descriptions
>
> Signed-off-by: Jack Lau <jacklau1222@qq.com>
> ---
>  libavformat/whip.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/libavformat/whip.c b/libavformat/whip.c
> index caac8d5680..3ec4c76b5f 100644
> --- a/libavformat/whip.c
> +++ b/libavformat/whip.c
> @@ -1911,11 +1911,11 @@ static int whip_check_bitstream(AVFormatContext *s, AVStream *st, const AVPacket
>  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 },
> -    { "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 },
> +    { "authorization",      "Optional Bearer token for WHIP Authorization",         OFFSET(authorization),      AV_OPT_TYPE_STRING, { .str = NULL },     0,       0, ENC },
> +    { "cert_file",          "Optional certificate file path for DTLS",              OFFSET(cert_file),          AV_OPT_TYPE_STRING, { .str = NULL },     0,       0, ENC },
> +    { "key_file",           "Optional private key file path for DTLS",              OFFSET(key_file),      AV_OPT_TYPE_STRING, { .str = NULL },     0,       0, ENC },
>      { "whip_flags",         "Set flags affecting WHIP connection behavior",             OFFSET(flags),         AV_OPT_TYPE_FLAGS,  { .i64 = 0 },                           0, UINT_MAX, ENC, .unit = "flags" },
> -    { "ignore_ipv6",        "(Optional) Ignore any IPv6 ICE candidate",                 0,                     AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_IGNORE_IPV6 },       0, UINT_MAX, ENC, .unit = "flags" },
> +    { "ignore_ipv6",        "Ignore any IPv6 ICE candidate",                 0,                     AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_IGNORE_IPV6 },       0, UINT_MAX, ENC, .unit = "flags" },
Can this line modify in the first patch?, i saw you add this option in
the first patch of this patchset.
>      { NULL },
>  };
>
> --
> 2.49.0
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

Thanks
Steven
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [FFmpeg-devel] [PATCH v4 01/11] avformat/whip: add whip_flags ignore_ipv6 to skip IPv6 ICE candidates
  2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 01/11] avformat/whip: add whip_flags ignore_ipv6 to skip IPv6 ICE candidates Jack Lau
@ 2025-07-21 13:53   ` Steven Liu
  0 siblings, 0 replies; 14+ messages in thread
From: Steven Liu @ 2025-07-21 13:53 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Jack Lau

Jack Lau <jacklau1222gm@gmail.com> 于2025年7月21日周一 19:30写道:
>
> mark this ignore_ipv6 flag could ignore any IPv6 ICE candidate,
> preventing “No route to host” errors on devices without IPv6 connectivity.
>
> Signed-off-by: Jack Lau <jacklau1222@qq.com>
> ---
>  libavformat/whip.c | 17 ++++++++++++++++-
>  1 file changed, 16 insertions(+), 1 deletion(-)
>
> diff --git a/libavformat/whip.c b/libavformat/whip.c
> index fd6de8503f..caac8d5680 100644
> --- a/libavformat/whip.c
> +++ b/libavformat/whip.c
> @@ -193,9 +193,14 @@ enum WHIPState {
>      WHIP_STATE_FAILED,
>  };
>
> +typedef enum WHIPFlags {
> +    WHIP_FLAG_IGNORE_IPV6  = (1 << 0) // Ignore ipv6 candidate
> +} WHIPFlags;
> +
>  typedef struct WHIPContext {
>      AVClass *av_class;
>
> +    uint32_t flags;        // enum WHIPFlags
>      /* The state of the RTC connection. */
>      enum WHIPState state;
>      /* The callback return value for DTLS. */
> @@ -884,6 +889,9 @@ static int parse_answer(AVFormatContext *s)
>              if (ptr && av_stristr(ptr, "host")) {
>                  char protocol[17], host[129];
>                  int priority, port;
> +#if HAVE_STRUCT_SOCKADDR_IN6
> +                struct in6_addr addr6;
> +#endif
>                  ret = sscanf(ptr, "%16s %d %128s %d typ host", protocol, &priority, host, &port);
>                  if (ret != 4) {
>                      av_log(whip, AV_LOG_ERROR, "Failed %d to parse line %d %s from %s\n",
> @@ -891,7 +899,12 @@ static int parse_answer(AVFormatContext *s)
>                      ret = AVERROR(EIO);
>                      goto end;
>                  }
> -
> +#if HAVE_STRUCT_SOCKADDR_IN6
> +                if (whip->flags & WHIP_FLAG_IGNORE_IPV6 && inet_pton(AF_INET6, host, &addr6) == 1) {
> +                    av_log(whip, AV_LOG_DEBUG, "Ignoring IPv6 ICE candidates %s, line %d %s \n", host, i, line);
> +                    continue;
> +                }
> +#endif
>                  if (av_strcasecmp(protocol, "udp")) {
>                      av_log(whip, AV_LOG_ERROR, "Protocol %s is not supported by RTC, choose udp, line %d %s of %s\n",
>                          protocol, i, line, whip->sdp_answer);
> @@ -1901,6 +1914,8 @@ static const AVOption options[] = {
>      { "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 },
> +    { "whip_flags",         "Set flags affecting WHIP connection behavior",             OFFSET(flags),         AV_OPT_TYPE_FLAGS,  { .i64 = 0 },                           0, UINT_MAX, ENC, .unit = "flags" },
> +    { "ignore_ipv6",        "(Optional) Ignore any IPv6 ICE candidate",                 0,                     AV_OPT_TYPE_CONST,  { .i64 = WHIP_FLAG_IGNORE_IPV6 },       0, UINT_MAX, ENC, .unit = "flags" },

 Add this options into documents?
>      { NULL },
>  };
>
> --
> 2.49.0
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2025-07-21 13:54 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-07-21 11:30 [FFmpeg-devel] [PATCH v4 00/11] avformat/whip: Add NACK, RTX, DTLS active support Jack Lau
2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 01/11] avformat/whip: add whip_flags ignore_ipv6 to skip IPv6 ICE candidates Jack Lau
2025-07-21 13:53   ` Steven Liu
2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 02/11] avformat/whip: fix typos Jack Lau
2025-07-21 13:52   ` Steven Liu
2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 03/11] avformat/whip: fix H264 profile_iop bit map for SDP Jack Lau
2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 04/11] WHIP: X509 cert serial number should be positive Jack Lau
2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 05/11] avformat/whip: implement NACK and RTX suppport Jack Lau
2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 06/11] avformat/whip: reindent whip options Jack Lau
2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 07/11] avformat/whip: add support for active dtls role Jack Lau
2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 08/11] avformat/whip: remove DTLSState enum Jack Lau
2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 09/11] avformat/whip: check the peer whether is ice lite Jack Lau
2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 10/11] avformat/whip: remove WHIP_STATE_DTLS_CONNECTING Jack Lau
2025-07-21 11:30 ` [FFmpeg-devel] [PATCH v4 11/11] avformat/whip: simplify and modularize the ICE and DTLS Jack Lau

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git