32
32
#include "libavutil/avstring.h"
33
33
#include "url.h"
34
34
#include "libavutil/random_seed.h"
35
+ #include "avio_internal.h"
36
+ #include "libavutil/hmac.h"
37
+ #include "libavutil/crc.h"
35
38
36
39
#define MAX_SDP_SIZE 8192
40
+ #define MAX_UDP_SIZE 1500
37
41
38
42
typedef struct RTCContext {
39
43
AVClass * av_class ;
@@ -70,6 +74,9 @@ typedef struct RTCContext {
70
74
int ice_port ;
71
75
/* The SDP answer received from the WebRTC server. */
72
76
char * sdp_answer ;
77
+
78
+ /* The UDP transport is used for delivering ICE, DTLS and SRTP packets. */
79
+ URLContext * udp_uc ;
73
80
} RTCContext ;
74
81
75
82
/**
@@ -97,12 +104,12 @@ static int check_codec(AVFormatContext *s)
97
104
if (par -> codec_id != AV_CODEC_ID_H264 ) {
98
105
av_log (s , AV_LOG_ERROR , "Unsupported video codec %s by RTC, choose h264\n" ,
99
106
desc ? desc -> name : "unknown" );
100
- return AVERROR ( EINVAL ) ;
107
+ return AVERROR_PATCHWELCOME ;
101
108
}
102
109
if ((par -> profile & ~FF_PROFILE_H264_CONSTRAINED ) != FF_PROFILE_H264_BASELINE ) {
103
110
av_log (s , AV_LOG_ERROR , "Profile %d of stream %d is not baseline, currently unsupported by RTC\n" ,
104
111
par -> profile , i );
105
- return AVERROR ( EINVAL ) ;
112
+ return AVERROR_PATCHWELCOME ;
106
113
}
107
114
break ;
108
115
case AVMEDIA_TYPE_AUDIO :
@@ -115,24 +122,24 @@ static int check_codec(AVFormatContext *s)
115
122
if (par -> codec_id != AV_CODEC_ID_OPUS ) {
116
123
av_log (s , AV_LOG_ERROR , "Unsupported audio codec %s by RTC, choose opus\n" ,
117
124
desc ? desc -> name : "unknown" );
118
- return AVERROR ( EINVAL ) ;
125
+ return AVERROR_PATCHWELCOME ;
119
126
}
120
127
121
128
if (par -> ch_layout .nb_channels != 2 ) {
122
129
av_log (s , AV_LOG_ERROR , "Unsupported audio channels %d by RTC, choose stereo\n" ,
123
130
par -> ch_layout .nb_channels );
124
- return AVERROR ( EINVAL ) ;
131
+ return AVERROR_PATCHWELCOME ;
125
132
}
126
133
127
134
if (par -> sample_rate != 48000 ) {
128
135
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 ;
130
137
}
131
138
break ;
132
139
default :
133
140
av_log (s , AV_LOG_ERROR , "Codec type '%s' for stream %d is not supported by RTC\n" ,
134
141
av_get_media_type_string (par -> codec_type ), i );
135
- return AVERROR ( EINVAL ) ;
142
+ return AVERROR_PATCHWELCOME ;
136
143
}
137
144
}
138
145
@@ -194,7 +201,7 @@ static int generate_sdp_offer(AVFormatContext *s)
194
201
char * tmp = av_mallocz (MAX_SDP_SIZE );
195
202
if (!tmp ) {
196
203
av_log (s , AV_LOG_ERROR , "Failed to alloc answer: %s" , s -> url );
197
- return AVERROR (EINVAL );
204
+ return AVERROR (ENOMEM );
198
205
}
199
206
200
207
if (rtc -> sdp_offer ) {
@@ -274,7 +281,7 @@ static int generate_sdp_offer(AVFormatContext *s)
274
281
rtc -> video_ssrc );
275
282
if (ret >= MAX_SDP_SIZE ) {
276
283
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 );
278
285
goto end ;
279
286
}
280
287
@@ -338,7 +345,7 @@ static int exchange_sdp(AVFormatContext *s)
338
345
char * tmp = av_mallocz (MAX_SDP_SIZE );
339
346
if (!tmp ) {
340
347
av_log (s , AV_LOG_ERROR , "Failed to alloc answer: %s" , s -> url );
341
- return AVERROR (EINVAL );
348
+ return AVERROR (ENOMEM );
342
349
}
343
350
344
351
ret = ffurl_alloc (& whip_uc , s -> url , AVIO_FLAG_READ_WRITE , & s -> interrupt_callback );
@@ -376,7 +383,7 @@ static int exchange_sdp(AVFormatContext *s)
376
383
ret = av_strlcatf (tmp , MAX_SDP_SIZE , "%.*s" , ret , buf );
377
384
if (ret >= MAX_SDP_SIZE ) {
378
385
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 );
380
387
goto end ;
381
388
}
382
389
}
@@ -426,14 +433,14 @@ static int parse_answer(AVFormatContext *s)
426
433
if (ret != 4 ) {
427
434
av_log (s , AV_LOG_ERROR , "Failed %d to parse line %d %s from %s" ,
428
435
ret , i , line , rtc -> sdp_answer );
429
- ret = AVERROR (EINVAL );
436
+ ret = AVERROR (EIO );
430
437
goto end ;
431
438
}
432
439
433
440
if (av_strcasecmp (protocol , "udp" )) {
434
441
av_log (s , AV_LOG_ERROR , "Protocol %s is not supported by RTC, choose udp, line %d %s of %s" ,
435
442
protocol , i , line , rtc -> sdp_answer );
436
- ret = AVERROR (EINVAL );
443
+ ret = AVERROR (EIO );
437
444
goto end ;
438
445
}
439
446
@@ -453,6 +460,115 @@ static int parse_answer(AVFormatContext *s)
453
460
return ret ;
454
461
}
455
462
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
+
456
572
static av_cold int rtc_init (AVFormatContext * s )
457
573
{
458
574
int ret ;
@@ -469,6 +585,9 @@ static av_cold int rtc_init(AVFormatContext *s)
469
585
if ((ret = parse_answer (s )) < 0 )
470
586
return ret ;
471
587
588
+ if ((ret = ice_handshake (s )) < 0 )
589
+ return ret ;
590
+
472
591
return 0 ;
473
592
}
474
593
@@ -496,6 +615,7 @@ static av_cold void rtc_deinit(AVFormatContext *s)
496
615
av_freep (& rtc -> ice_pwd_remote );
497
616
av_freep (& rtc -> ice_protocol );
498
617
av_freep (& rtc -> ice_host );
618
+ ffurl_closep (& rtc -> udp_uc );
499
619
}
500
620
501
621
static const AVOption options [] = {
0 commit comments