PR #20482 opened by Jack Lau (JackLau) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20482 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20482.patch
Refer to RFC 9725 4.2, "Once a session is set up, consent freshness as per [RFC7675] SHALL be used to detect non-graceful disconnection by full ICE implementations and DTLS teardown for session termination by either side" Refer to RFC 7675, send STUN Binding request every 5s, expire consent after 30s without response, terminate session when the consent expired. TODO: Random consent check interval (4-6s) will be added later. Co-authored-by: Sergio Garcia Murillo <sergio.garcia.muri...@gmail.com> Signed-off-by: Jack Lau <jacklau1...@qq.com> >From 456f08ce82d1a1af3e03d9296f48e9b88a133a57 Mon Sep 17 00:00:00 2001 From: Jack Lau <jacklau1...@qq.com> Date: Wed, 10 Sep 2025 15:53:25 +0800 Subject: [PATCH 1/2] avformat/whip: add WHIP_US_PER_MS macro to replace 1000 Signed-off-by: Jack Lau <jacklau1...@qq.com> --- libavformat/whip.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libavformat/whip.c b/libavformat/whip.c index 20db03a239..ef5890fc7d 100644 --- a/libavformat/whip.c +++ b/libavformat/whip.c @@ -66,6 +66,8 @@ */ #define DTLS_SRTP_CHECKSUM_LEN 16 +#define WHIP_US_PER_MS 1000 + /** * When sending ICE or DTLS messages, responses are received via UDP. However, the peer * may not be ready and return EAGAIN, in which case we should wait for a short duration @@ -1251,7 +1253,7 @@ next_packet: break; now = av_gettime_relative(); - if (now - starttime >= whip->handshake_timeout * 1000) { + if (now - starttime >= whip->handshake_timeout * WHIP_US_PER_MS) { av_log(whip, AV_LOG_ERROR, "DTLS handshake timeout=%dms, cost=%.2fms, elapsed=%.2fms, state=%d\n", whip->handshake_timeout, ELAPSED(starttime, now), ELAPSED(whip->whip_starttime, now), whip->state); ret = AVERROR(ETIMEDOUT); @@ -1264,7 +1266,7 @@ next_packet: if (ret > 0) break; if (ret == AVERROR(EAGAIN)) { - av_usleep(5 * 1000); + av_usleep(5 * WHIP_US_PER_MS); continue; } av_log(whip, AV_LOG_ERROR, "Failed to read message\n"); -- 2.49.1 >From f84805f59cb1d515a8492d64b5c15c02bc39263b Mon Sep 17 00:00:00 2001 From: Jack Lau <jacklau1...@qq.com> Date: Wed, 10 Sep 2025 15:26:22 +0800 Subject: [PATCH 2/2] avformat/whip: add ICE consent freshness support Refer to RFC 9725 4.2, "Once a session is set up, consent freshness as per [RFC7675] SHALL be used to detect non-graceful disconnection by full ICE implementations and DTLS teardown for session termination by either side" Refer to RFC 7675, send STUN Binding request every 5s, expire consent after 30s without response, terminate session when the consent expired. TODO: Random consent check interval (4-6s) will be added later. Co-authored-by: Sergio Garcia Murillo <sergio.garcia.muri...@gmail.com> Signed-off-by: Jack Lau <jacklau1...@qq.com> --- libavformat/whip.c | 49 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/libavformat/whip.c b/libavformat/whip.c index ef5890fc7d..e10c3cacd1 100644 --- a/libavformat/whip.c +++ b/libavformat/whip.c @@ -159,6 +159,17 @@ #define WHIP_SDP_SESSION_ID "4489045141692799359" #define WHIP_SDP_CREATOR_IP "127.0.0.1" +/** + * Refer to RFC 7675 5.1, + * + * To prevent expiry of consent, a STUN binding request can be sent periodically. + * Implementations SHOULD set a default interval of 5 seconds(5000ms). + * + * Consent expires after 30 seconds(30000ms). + */ +#define WHIP_ICE_CONSENT_CHECK_INTERVAL 5000 +#define WHIP_ICE_CONSENT_EXPIRED_TIMER 30000 + /* Calculate the elapsed time from starttime to endtime in milliseconds. */ #define ELAPSED(starttime, endtime) ((float)(endtime - starttime) / 1000) @@ -269,6 +280,8 @@ typedef struct WHIPContext { int64_t whip_ice_time; int64_t whip_dtls_time; int64_t whip_srtp_time; + int64_t whip_last_consent_tx_time; + int64_t whip_last_consent_rx_time; /* The certificate and private key content used for DTLS handshake */ char cert_buf[MAX_CERTIFICATE_SIZE]; @@ -1320,6 +1333,8 @@ 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) { + /* Start consent timer when ICE selected */ + whip->whip_last_consent_tx_time = whip->whip_last_consent_rx_time = av_gettime_relative(); whip->state = WHIP_STATE_DTLS_CONNECTING; ret = ffurl_handshake(whip->dtls_uc); if (ret < 0) { @@ -1787,8 +1802,26 @@ 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; - - /* TODO: Send binding request every 1s as WebRTC heartbeat. */ + int64_t now = av_gettime_relative(); + /** + * Refer to RFC 7675 + * Periodically send Consent Freshness STUN Binding Request + */ + if (now - whip->whip_last_consent_tx_time > WHIP_ICE_CONSENT_CHECK_INTERVAL * WHIP_US_PER_MS) { + int size; + ret = ice_create_request(s, whip->buf, sizeof(whip->buf), &size); + if (ret < 0) { + av_log(whip, AV_LOG_ERROR, "Failed to create STUN binding request, size=%d\n", size); + goto end; + } + ret = ffurl_write(whip->udp, whip->buf, size); + if (ret < 0) { + av_log(whip, AV_LOG_ERROR, "Failed to send STUN binding request, size=%d\n", size); + goto end; + } + whip->whip_last_consent_tx_time = now; + av_log(whip, AV_LOG_DEBUG, "Consent Freshness check sent\n"); + } /** * Receive packets from the server such as ICE binding requests, DTLS messages, @@ -1805,6 +1838,10 @@ static int whip_write_packet(AVFormatContext *s, AVPacket *pkt) av_log(whip, AV_LOG_ERROR, "Receive EOF from UDP socket\n"); goto end; } + if (ice_is_binding_response(whip->buf, ret)) { + whip->whip_last_consent_rx_time = av_gettime_relative(); + av_log(whip, AV_LOG_DEBUG, "Consent Freshness check received\n"); + } if (is_dtls_packet(whip->buf, ret)) { if ((ret = ffurl_write(whip->dtls_uc, whip->buf, ret)) < 0) { av_log(whip, AV_LOG_ERROR, "Failed to handle DTLS message\n"); @@ -1812,6 +1849,14 @@ static int whip_write_packet(AVFormatContext *s, AVPacket *pkt) } } write_packet: + now = av_gettime_relative(); + if (now - whip->whip_last_consent_rx_time > WHIP_ICE_CONSENT_EXPIRED_TIMER * WHIP_US_PER_MS) { + av_log(whip, AV_LOG_ERROR, + "Consent Freshness expired after %.2fms (limted %dms), terminate session\n", + ELAPSED(now, whip->whip_last_consent_rx_time), WHIP_ICE_CONSENT_EXPIRED_TIMER); + ret = AVERROR(ETIMEDOUT); + goto end; + } 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"); -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-le...@ffmpeg.org