3232#include "libavutil/avstring.h"
3333#include "url.h"
3434#include "libavutil/random_seed.h"
35+ #include "avio_internal.h"
36+ #include "libavutil/hmac.h"
37+ #include "libavutil/crc.h"
3538
3639#define MAX_SDP_SIZE 8192
40+ #define MAX_UDP_SIZE 1500
3741
3842typedef struct RTCContext {
3943 AVClass * av_class ;
@@ -70,6 +74,9 @@ typedef struct RTCContext {
7074 int ice_port ;
7175 /* The SDP answer received from the WebRTC server. */
7276 char * sdp_answer ;
77+
78+ /* The UDP transport is used for delivering ICE, DTLS and SRTP packets. */
79+ URLContext * udp_uc ;
7380} RTCContext ;
7481
7582/**
@@ -97,12 +104,12 @@ static int check_codec(AVFormatContext *s)
97104 if (par -> codec_id != AV_CODEC_ID_H264 ) {
98105 av_log (s , AV_LOG_ERROR , "Unsupported video codec %s by RTC, choose h264\n" ,
99106 desc ? desc -> name : "unknown" );
100- return AVERROR ( EINVAL ) ;
107+ return AVERROR_PATCHWELCOME ;
101108 }
102109 if ((par -> profile & ~FF_PROFILE_H264_CONSTRAINED ) != FF_PROFILE_H264_BASELINE ) {
103110 av_log (s , AV_LOG_ERROR , "Profile %d of stream %d is not baseline, currently unsupported by RTC\n" ,
104111 par -> profile , i );
105- return AVERROR ( EINVAL ) ;
112+ return AVERROR_PATCHWELCOME ;
106113 }
107114 break ;
108115 case AVMEDIA_TYPE_AUDIO :
@@ -115,24 +122,24 @@ static int check_codec(AVFormatContext *s)
115122 if (par -> codec_id != AV_CODEC_ID_OPUS ) {
116123 av_log (s , AV_LOG_ERROR , "Unsupported audio codec %s by RTC, choose opus\n" ,
117124 desc ? desc -> name : "unknown" );
118- return AVERROR ( EINVAL ) ;
125+ return AVERROR_PATCHWELCOME ;
119126 }
120127
121128 if (par -> ch_layout .nb_channels != 2 ) {
122129 av_log (s , AV_LOG_ERROR , "Unsupported audio channels %d by RTC, choose stereo\n" ,
123130 par -> ch_layout .nb_channels );
124- return AVERROR ( EINVAL ) ;
131+ return AVERROR_PATCHWELCOME ;
125132 }
126133
127134 if (par -> sample_rate != 48000 ) {
128135 av_log (s , AV_LOG_ERROR , "Unsupported audio sample rate %d by RTC, choose 48000\n" , par -> sample_rate );
129- return AVERROR ( EINVAL ) ;
136+ return AVERROR_PATCHWELCOME ;
130137 }
131138 break ;
132139 default :
133140 av_log (s , AV_LOG_ERROR , "Codec type '%s' for stream %d is not supported by RTC\n" ,
134141 av_get_media_type_string (par -> codec_type ), i );
135- return AVERROR ( EINVAL ) ;
142+ return AVERROR_PATCHWELCOME ;
136143 }
137144 }
138145
@@ -194,7 +201,7 @@ static int generate_sdp_offer(AVFormatContext *s)
194201 char * tmp = av_mallocz (MAX_SDP_SIZE );
195202 if (!tmp ) {
196203 av_log (s , AV_LOG_ERROR , "Failed to alloc answer: %s" , s -> url );
197- return AVERROR (EINVAL );
204+ return AVERROR (ENOMEM );
198205 }
199206
200207 if (rtc -> sdp_offer ) {
@@ -274,7 +281,7 @@ static int generate_sdp_offer(AVFormatContext *s)
274281 rtc -> video_ssrc );
275282 if (ret >= MAX_SDP_SIZE ) {
276283 av_log (s , AV_LOG_ERROR , "Offer %d exceed max %d, %s" , ret , MAX_SDP_SIZE , tmp );
277- ret = AVERROR (EINVAL );
284+ ret = AVERROR (EIO );
278285 goto end ;
279286 }
280287
@@ -338,7 +345,7 @@ static int exchange_sdp(AVFormatContext *s)
338345 char * tmp = av_mallocz (MAX_SDP_SIZE );
339346 if (!tmp ) {
340347 av_log (s , AV_LOG_ERROR , "Failed to alloc answer: %s" , s -> url );
341- return AVERROR (EINVAL );
348+ return AVERROR (ENOMEM );
342349 }
343350
344351 ret = ffurl_alloc (& whip_uc , s -> url , AVIO_FLAG_READ_WRITE , & s -> interrupt_callback );
@@ -376,7 +383,7 @@ static int exchange_sdp(AVFormatContext *s)
376383 ret = av_strlcatf (tmp , MAX_SDP_SIZE , "%.*s" , ret , buf );
377384 if (ret >= MAX_SDP_SIZE ) {
378385 av_log (s , AV_LOG_ERROR , "Answer %d exceed max size %d, %s" , ret , MAX_SDP_SIZE , tmp );
379- ret = AVERROR (EINVAL );
386+ ret = AVERROR (EIO );
380387 goto end ;
381388 }
382389 }
@@ -426,14 +433,14 @@ static int parse_answer(AVFormatContext *s)
426433 if (ret != 4 ) {
427434 av_log (s , AV_LOG_ERROR , "Failed %d to parse line %d %s from %s" ,
428435 ret , i , line , rtc -> sdp_answer );
429- ret = AVERROR (EINVAL );
436+ ret = AVERROR (EIO );
430437 goto end ;
431438 }
432439
433440 if (av_strcasecmp (protocol , "udp" )) {
434441 av_log (s , AV_LOG_ERROR , "Protocol %s is not supported by RTC, choose udp, line %d %s of %s" ,
435442 protocol , i , line , rtc -> sdp_answer );
436- ret = AVERROR (EINVAL );
443+ ret = AVERROR (EIO );
437444 goto end ;
438445 }
439446
@@ -453,6 +460,115 @@ static int parse_answer(AVFormatContext *s)
453460 return ret ;
454461}
455462
463+ /**
464+ * Open the UDP transport and complete the ICE handshake.
465+ *
466+ * @return 0 if OK, AVERROR_xxx on error
467+ */
468+ static int ice_handshake (AVFormatContext * s )
469+ {
470+ int ret , len , crc32 ;
471+ char url [256 ], buf [MAX_UDP_SIZE ];
472+ AVIOContext * pb = NULL ;
473+ AVHMAC * hmac = NULL ;
474+ RTCContext * rtc = s -> priv_data ;
475+
476+ pb = avio_alloc_context (buf , sizeof (buf ), AVIO_FLAG_WRITE , NULL , NULL , NULL , NULL );
477+ if (!pb ) {
478+ av_log (s , AV_LOG_ERROR , "Failed to alloc AVIOContext for ICE" );
479+ ret = AVERROR (ENOMEM );
480+ goto end ;
481+ }
482+
483+ hmac = av_hmac_alloc (AV_HMAC_SHA1 );
484+ if (!hmac ) {
485+ av_log (s , AV_LOG_ERROR , "Failed to alloc AVHMAC for ICE" );
486+ ret = AVERROR (ENOMEM );
487+ goto end ;
488+ }
489+
490+ /* Build UDP URL and create the UDP context as transport. */
491+ ff_url_join (url , sizeof (url ), "udp" , NULL , rtc -> ice_host , rtc -> ice_port , NULL );
492+ ret = ffurl_alloc (& rtc -> udp_uc , url , AVIO_FLAG_WRITE | AVIO_FLAG_NONBLOCK , & s -> interrupt_callback );
493+ if (ret < 0 ) {
494+ av_log (s , AV_LOG_ERROR , "Failed to open udp://%s:%d" , rtc -> ice_host , rtc -> ice_port );
495+ goto end ;
496+ }
497+
498+ av_opt_set (rtc -> udp_uc -> priv_data , "connect" , "1" , 0 );
499+ av_opt_set (rtc -> udp_uc -> priv_data , "fifo_size" , "0" , 0 );
500+
501+ ret = ffurl_connect (rtc -> udp_uc , NULL );
502+ if (ret < 0 ) {
503+ av_log (s , AV_LOG_ERROR , "Failed to connect udp://%s:%d" , rtc -> ice_host , rtc -> ice_port );
504+ goto end ;
505+ }
506+
507+ /* Set the transport as READ and WRITE after connected. */
508+ rtc -> udp_uc -> flags |= AVIO_FLAG_READ ;
509+
510+ /* Build and send the STUN binding request. */
511+ /* Write 20 bytes header */
512+ avio_wb16 (pb , 0x0001 ); /* STUN binding request */
513+ avio_wb16 (pb , 0 ); /* length */
514+ avio_wb32 (pb , 0x2112A442 ); /* magic cookie */
515+ avio_wb32 (pb , av_get_random_seed ()); /* transaction ID */
516+ avio_wb32 (pb , av_get_random_seed ()); /* transaction ID */
517+ avio_wb32 (pb , av_get_random_seed ()); /* transaction ID */
518+ /* Write the username attribute */
519+ ret = snprintf (url , sizeof (url ), "%s:%s" , rtc -> ice_ufrag_remote , rtc -> ice_ufrag_local );
520+ avio_wb16 (pb , 0x0006 ); /* attribute type username */
521+ avio_wb16 (pb , ret ); /* size of username */
522+ avio_write (pb , url , ret ); /* bytes of username */
523+ ffio_fill (pb , 0 , (4 - (ret % 4 )) % 4 ); /* padding */
524+ /* Build and update message integrity */
525+ avio_wb16 (pb , 0x0008 ); /* attribute type message integrity */
526+ avio_wb16 (pb , 20 ); /* size of message integrity */
527+ ffio_fill (pb , 0 , 20 ); /* fill with zero to directly write and skip it */
528+ len = avio_tell (pb );
529+ buf [2 ] = (len - 20 ) >> 8 ;
530+ buf [3 ] = (len - 20 ) & 0xFF ;
531+ av_hmac_init (hmac , rtc -> ice_pwd_local , strlen (rtc -> ice_pwd_local ));
532+ av_hmac_update (hmac , buf , len - 24 );
533+ av_hmac_final (hmac , buf + len - 20 , 20 );
534+ /* Write the fingerprint attribute */
535+ avio_wb16 (pb , 0x8028 ); /* attribute type fingerprint */
536+ avio_wb16 (pb , 4 ); /* size of fingerprint */
537+ ffio_fill (pb , 0 , 4 ); /* fill with zero to directly write and skip it */
538+ len = avio_tell (pb );
539+ buf [2 ] = (len - 20 ) >> 8 ;
540+ buf [3 ] = (len - 20 ) & 0xFF ;
541+ crc32 = av_crc (av_crc_get_table (AV_CRC_32_IEEE_LE ), 0xFFFFFFFF , buf , len - 8 ) ^ 0xFFFFFFFF ;
542+ avio_skip (pb , -4 );
543+ avio_wb32 (pb , crc32 ^ 0x5354554E ); /* hash message by CRC32 */
544+
545+ ret = ffurl_write (rtc -> udp_uc , buf , len );
546+ if (ret < 0 ) {
547+ av_log (s , AV_LOG_ERROR , "Failed to send STUN binding request, size=%d" , len );
548+ goto end ;
549+ }
550+
551+ /* Read the STUN binding response. */
552+ ret = ffurl_read (rtc -> udp_uc , buf , sizeof (buf ));
553+ if (ret < 0 ) {
554+ av_log (s , AV_LOG_ERROR , "Failed to read STUN binding response" );
555+ goto end ;
556+ }
557+
558+ if (ret < 2 || buf [0 ] != 0x01 || buf [1 ] != 0x01 ) {
559+ av_log (s , AV_LOG_ERROR , "Invalid STUN binding response, size=%d, type=%02X%02X" , ret , buf [0 ], buf [1 ]);
560+ ret = AVERROR (EIO );
561+ goto end ;
562+ }
563+ av_log (s , AV_LOG_VERBOSE , "ICE STUN ok, url=udp://%s:%d, username=%s:%s, request=%dB, response=%dB\n" ,
564+ rtc -> ice_host , rtc -> ice_port , rtc -> ice_ufrag_remote , rtc -> ice_ufrag_local , len , ret );
565+
566+ end :
567+ avio_context_free (& pb );
568+ av_hmac_free (hmac );
569+ return ret ;
570+ }
571+
456572static av_cold int rtc_init (AVFormatContext * s )
457573{
458574 int ret ;
@@ -469,6 +585,9 @@ static av_cold int rtc_init(AVFormatContext *s)
469585 if ((ret = parse_answer (s )) < 0 )
470586 return ret ;
471587
588+ if ((ret = ice_handshake (s )) < 0 )
589+ return ret ;
590+
472591 return 0 ;
473592}
474593
@@ -496,6 +615,7 @@ static av_cold void rtc_deinit(AVFormatContext *s)
496615 av_freep (& rtc -> ice_pwd_remote );
497616 av_freep (& rtc -> ice_protocol );
498617 av_freep (& rtc -> ice_host );
618+ ffurl_closep (& rtc -> udp_uc );
499619}
500620
501621static const AVOption options [] = {
0 commit comments