42
42
#include "network.h"
43
43
#include "srtp.h"
44
44
#include "tls.h"
45
-
45
+ #include <errno.h>
46
46
/**
47
47
* Maximum size limit of a Session Description Protocol (SDP),
48
48
* be it an offer or answer.
143
143
#define WHIP_RTCP_PT_START 192
144
144
#define WHIP_RTCP_PT_END 223
145
145
146
+ /**
147
+ * Consent-freshness constants
148
+ */
149
+ #define WHIP_CONSENT_DEF_INTERVAL 15000 /* ms – RFC 7675 default */
150
+ #define WHIP_CONSENT_MAX_FAILURES 3
151
+
146
152
/**
147
153
* In the case of ICE-LITE, these fields are not used; instead, they are defined
148
154
* as constant values.
156
162
/* STUN Attribute, comprehension-required range (0x0000-0x7FFF) */
157
163
enum STUNAttr {
158
164
STUN_ATTR_USERNAME = 0x0006 , /// shared secret response/bind request
165
+ STUN_ATTR_PRIORITY = 0x0024 , /// ICE controlling/controlled
159
166
STUN_ATTR_USE_CANDIDATE = 0x0025 , /// bind request
160
167
STUN_ATTR_MESSAGE_INTEGRITY = 0x0008 , /// bind request/response
161
168
STUN_ATTR_FINGERPRINT = 0x8028 , /// rfc5389
169
+ STUN_ATTR_ICE_CONTROLLING = 0x802A , /// full agent talking to ice-lite
162
170
};
163
171
164
172
enum WHIPState {
@@ -303,6 +311,15 @@ typedef struct WHIPContext {
303
311
/* The certificate and private key used for DTLS handshake. */
304
312
char * cert_file ;
305
313
char * key_file ;
314
+
315
+ /* Consent-freshness state */
316
+ int consent_interval ;
317
+ int64_t last_consent_tx ;
318
+ int consent_failures ;
319
+
320
+ /* ICE-lite support */
321
+ int ice_lite_remote ;
322
+ uint64_t ice_tie_breaker ; /* random 64-bit, for ICE-CONTROLLING */
306
323
} WHIPContext ;
307
324
308
325
/**
@@ -412,6 +429,15 @@ static av_cold int initialize(AVFormatContext *s)
412
429
seed = av_get_random_seed ();
413
430
av_lfg_init (& whip -> rnd , seed );
414
431
432
+ /* 64-bit tie-breaker for ICE-CONTROLLING (RFC 8445 6.1.1) */
433
+ whip -> ice_tie_breaker = ((uint64_t )av_lfg_get (& whip -> rnd ) << 32 ) | (uint64_t )av_lfg_get (& whip -> rnd );
434
+
435
+ /* Initialise consent-freshness timers */
436
+ if (whip -> consent_interval <= 0 )
437
+ whip -> consent_interval = WHIP_CONSENT_DEF_INTERVAL ;
438
+ whip -> last_consent_tx = av_gettime ();
439
+ whip -> consent_failures = 0 ;
440
+
415
441
if (whip -> pkt_size < ideal_pkt_size )
416
442
av_log (whip , AV_LOG_WARNING , "WHIP: pkt_size=%d(<%d) is too small, may cause packet loss\n" ,
417
443
whip -> pkt_size , ideal_pkt_size );
@@ -894,6 +920,8 @@ static int parse_answer(AVFormatContext *s)
894
920
goto end ;
895
921
}
896
922
}
923
+ } else if (av_strstart (line , "a=ice-lite" , NULL )) {
924
+ whip -> ice_lite_remote = 1 ;
897
925
}
898
926
}
899
927
@@ -985,6 +1013,22 @@ static int ice_create_request(AVFormatContext *s, uint8_t *buf, int buf_size, in
985
1013
avio_wb16 (pb , STUN_ATTR_USE_CANDIDATE ); /* attribute type use-candidate */
986
1014
avio_wb16 (pb , 0 ); /* size of use-candidate */
987
1015
1016
+ /**
1017
+ * For ICE-lite peers we are *always* the controlling agent (RFC 8445 6.1.3.1).
1018
+ * Add PRIORITY + ICE-CONTROLLING attributes.
1019
+ */
1020
+ if (whip -> ice_lite_remote ) {
1021
+ /* we are controlling, use host-candidate priority 126 << 24 | 65535 << 8 | 255 = 2130706431 */
1022
+ avio_wb16 (pb , STUN_ATTR_PRIORITY );
1023
+ avio_wb16 (pb , 4 );
1024
+ avio_wb32 (pb , 2130706431 );
1025
+
1026
+ avio_wb16 (pb , STUN_ATTR_ICE_CONTROLLING );
1027
+ avio_wb16 (pb , 8 );
1028
+ avio_wb32 (pb , (uint32_t )(whip -> ice_tie_breaker >> 32 ));
1029
+ avio_wb32 (pb , (uint32_t )(whip -> ice_tie_breaker & 0xffffffff ));
1030
+ }
1031
+
988
1032
/* Build and update message integrity */
989
1033
avio_wb16 (pb , STUN_ATTR_MESSAGE_INTEGRITY ); /* attribute type message integrity */
990
1034
avio_wb16 (pb , 20 ); /* size of message integrity */
@@ -1775,14 +1819,27 @@ static int whip_write_packet(AVFormatContext *s, AVPacket *pkt)
1775
1819
AVStream * st = s -> streams [pkt -> stream_index ];
1776
1820
AVFormatContext * rtp_ctx = st -> priv_data ;
1777
1821
1778
- /* TODO: Send binding request every 1s as WebRTC heartbeat. */
1822
+ /* Periodic consent-freshness STUN Binding Request */
1823
+ int64_t now = av_gettime ();
1824
+ if (now - whip -> last_consent_tx >= (int64_t )whip -> consent_interval * 1000 ) {
1825
+ int req_sz ;
1826
+ if (ice_create_request (s , whip -> buf , sizeof (whip -> buf ), & req_sz ) >= 0 && ffurl_write (whip -> udp , whip -> buf , req_sz ) == req_sz ) {
1827
+ whip -> consent_failures ++ ;
1828
+ whip -> last_consent_tx = now ;
1829
+ av_log (whip , AV_LOG_VERBOSE , "WHIP: consent-freshness request %d sent\n" , whip -> consent_failures );
1830
+ }
1831
+ }
1779
1832
1780
1833
/**
1781
1834
* Receive packets from the server such as ICE binding requests, DTLS messages,
1782
1835
* and RTCP like PLI requests, then respond to them.
1783
1836
*/
1784
1837
ret = ffurl_read (whip -> udp , whip -> buf , sizeof (whip -> buf ));
1785
1838
if (ret > 0 ) {
1839
+ if (ice_is_binding_response (whip -> buf , ret )) {
1840
+ whip -> consent_failures = 0 ;
1841
+ av_log (whip , AV_LOG_VERBOSE , "WHIP: consent-freshness response received, counter reset\n" );
1842
+ }
1786
1843
if (is_dtls_packet (whip -> buf , ret )) {
1787
1844
if ((ret = ffurl_write (whip -> dtls_uc , whip -> buf , ret )) < 0 ) {
1788
1845
av_log (whip , AV_LOG_ERROR , "WHIP: Failed to handle DTLS message\n" );
@@ -1793,6 +1850,12 @@ static int whip_write_packet(AVFormatContext *s, AVPacket *pkt)
1793
1850
av_log (whip , AV_LOG_ERROR , "WHIP: Failed to read from UDP socket\n" );
1794
1851
goto end ;
1795
1852
}
1853
+ /* Check consent freshness consecutive failures */
1854
+ if (whip -> consent_failures >= WHIP_CONSENT_MAX_FAILURES ) {
1855
+ av_log (whip , AV_LOG_ERROR , "WHIP: No consent-freshness response after %d attempts, closing\n" , WHIP_CONSENT_MAX_FAILURES );
1856
+ ret = AVERROR (EHOSTUNREACH );
1857
+ goto end ;
1858
+ }
1796
1859
1797
1860
if (whip -> h264_annexb_insert_sps_pps && st -> codecpar -> codec_id == AV_CODEC_ID_H264 ) {
1798
1861
if ((ret = h264_annexb_insert_sps_pps (s , pkt )) < 0 ) {
@@ -1891,7 +1954,8 @@ static const AVOption options[] = {
1891
1954
{ "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 , DEC },
1892
1955
{ "authorization" , "The optional Bearer token for WHIP Authorization" , OFFSET (authorization ), AV_OPT_TYPE_STRING , { .str = NULL }, 0 , 0 , DEC },
1893
1956
{ "cert_file" , "The optional certificate file path for DTLS" , OFFSET (cert_file ), AV_OPT_TYPE_STRING , { .str = NULL }, 0 , 0 , DEC },
1894
- { "key_file" , "The optional private key file path for DTLS" , OFFSET (key_file ), AV_OPT_TYPE_STRING , { .str = NULL }, 0 , 0 , DEC },
1957
+ { "key_file" , "The optional private key file path for DTLS" , OFFSET (key_file ), AV_OPT_TYPE_STRING , { .str = NULL }, 0 , 0 , DEC },
1958
+ { "consent_interval" , "STUN consent refresh interval in ms (RFC 7675)" , OFFSET (consent_interval ), AV_OPT_TYPE_INT , { .i64 = WHIP_CONSENT_DEF_INTERVAL }, 5000 , 30000 , DEC },
1895
1959
{ NULL },
1896
1960
};
1897
1961
0 commit comments