33
33
#include "url.h"
34
34
#include "libavutil/random_seed.h"
35
35
36
+ #define MAX_SDP_SIZE 8192
37
+
36
38
typedef struct RTCContext {
37
39
AVClass * av_class ;
38
40
@@ -58,16 +60,16 @@ typedef struct RTCContext {
58
60
/* The ICE username and pwd from remote server. */
59
61
char * ice_ufrag_remote ;
60
62
char * ice_pwd_remote ;
61
- /* The ICE candidate protocol, priority, host and port. */
63
+ /**
64
+ * The ICE candidate protocol, priority, host and port. Note that only
65
+ * support one candidate for now. We will choose the first udp candidate.
66
+ * We will support multiple candidates in the future.
67
+ */
62
68
char * ice_protocol ;
63
- int ice_priority ;
64
69
char * ice_host ;
65
70
int ice_port ;
66
71
/* The SDP answer received from the WebRTC server. */
67
72
char * sdp_answer ;
68
-
69
- /* The HTTP URL context is the transport layer for the WHIP protocol. */
70
- URLContext * whip_uc ;
71
73
} RTCContext ;
72
74
73
75
/**
@@ -186,9 +188,15 @@ static int check_codec(AVFormatContext *s)
186
188
*/
187
189
static int generate_sdp_offer (AVFormatContext * s )
188
190
{
189
- int profile_iop ;
191
+ int ret , profile_iop ;
190
192
RTCContext * rtc = s -> priv_data ;
191
193
194
+ char * tmp = av_mallocz (MAX_SDP_SIZE );
195
+ if (!tmp ) {
196
+ av_log (s , AV_LOG_ERROR , "Failed to alloc answer: %s" , s -> url );
197
+ return AVERROR (EINVAL );
198
+ }
199
+
192
200
if (rtc -> sdp_offer ) {
193
201
av_log (s , AV_LOG_ERROR , "SDP offer is already set\n" );
194
202
return AVERROR (EINVAL );
@@ -207,7 +215,8 @@ static int generate_sdp_offer(AVFormatContext *s)
207
215
rtc -> video_payload_type = 106 ;
208
216
209
217
profile_iop = rtc -> video_par -> profile & FF_PROFILE_H264_CONSTRAINED ? 0xe0 : 0x00 ;
210
- rtc -> sdp_offer = av_asprintf (
218
+
219
+ ret = av_strlcatf (tmp , MAX_SDP_SIZE ,
211
220
"v=0\r\n"
212
221
"o=FFmpeg 4489045141692799359 2 IN IP4 127.0.0.1\r\n"
213
222
"s=FFmpegPublishSession\r\n"
@@ -262,11 +271,19 @@ static int generate_sdp_offer(AVFormatContext *s)
262
271
profile_iop ,
263
272
rtc -> video_par -> level ,
264
273
rtc -> video_ssrc ,
265
- rtc -> video_ssrc
266
- );
274
+ rtc -> video_ssrc );
275
+ if (ret >= MAX_SDP_SIZE ) {
276
+ av_log (s , AV_LOG_ERROR , "Offer %d exceed max %d, %s" , ret , MAX_SDP_SIZE , tmp );
277
+ ret = AVERROR (EINVAL );
278
+ goto end ;
279
+ }
280
+
281
+ rtc -> sdp_offer = av_strdup (tmp );
267
282
av_log (s , AV_LOG_VERBOSE , "Generated offer: %s" , rtc -> sdp_offer );
268
283
269
- return 0 ;
284
+ end :
285
+ av_free (tmp );
286
+ return ret ;
270
287
}
271
288
272
289
/**
@@ -313,31 +330,38 @@ static int generate_sdp_offer(AVFormatContext *s)
313
330
static int exchange_sdp (AVFormatContext * s )
314
331
{
315
332
int ret ;
316
- char headers [MAX_URL_SIZE ], buf [MAX_URL_SIZE ];
317
- char * p ;
333
+ char buf [MAX_URL_SIZE ];
318
334
RTCContext * rtc = s -> priv_data ;
335
+ /* The URL context is an HTTP transport layer for the WHIP protocol. */
336
+ URLContext * whip_uc = NULL ;
337
+
338
+ char * tmp = av_mallocz (MAX_SDP_SIZE );
339
+ if (!tmp ) {
340
+ av_log (s , AV_LOG_ERROR , "Failed to alloc answer: %s" , s -> url );
341
+ return AVERROR (EINVAL );
342
+ }
319
343
320
- ret = ffurl_alloc (& rtc -> whip_uc , s -> url , AVIO_FLAG_READ_WRITE , & s -> interrupt_callback );
344
+ ret = ffurl_alloc (& whip_uc , s -> url , AVIO_FLAG_READ_WRITE , & s -> interrupt_callback );
321
345
if (ret < 0 ) {
322
346
av_log (s , AV_LOG_ERROR , "Failed to alloc HTTP context: %s" , s -> url );
323
- return ret ;
347
+ goto end ;
324
348
}
325
349
326
- snprintf (headers , sizeof (headers ),
350
+ snprintf (buf , sizeof (buf ),
327
351
"Cache-Control: no-cache\r\n"
328
352
"Content-Type: application/sdp\r\n" );
329
- av_opt_set (rtc -> whip_uc -> priv_data , "headers" , headers , 0 );
330
- av_opt_set (rtc -> whip_uc -> priv_data , "chunked_post" , "0" , 0 );
331
- av_opt_set_bin (rtc -> whip_uc -> priv_data , "post_data" , rtc -> sdp_offer , (int )strlen (rtc -> sdp_offer ), 0 );
353
+ av_opt_set (whip_uc -> priv_data , "headers" , buf , 0 );
354
+ av_opt_set (whip_uc -> priv_data , "chunked_post" , "0" , 0 );
355
+ av_opt_set_bin (whip_uc -> priv_data , "post_data" , rtc -> sdp_offer , (int )strlen (rtc -> sdp_offer ), 0 );
332
356
333
- ret = ffurl_connect (rtc -> whip_uc , NULL );
357
+ ret = ffurl_connect (whip_uc , NULL );
334
358
if (ret < 0 ) {
335
359
av_log (s , AV_LOG_ERROR , "Failed to request url=%s, offer: %s" , s -> url , rtc -> sdp_offer );
336
- return ret ;
360
+ goto end ;
337
361
}
338
362
339
- for (;; ) {
340
- ret = ffurl_read (rtc -> whip_uc , buf , sizeof (buf ));
363
+ while ( 1 ) {
364
+ ret = ffurl_read (whip_uc , buf , sizeof (buf ));
341
365
if (ret == AVERROR_EOF ) {
342
366
/* Reset the error because we read all response as answer util EOF. */
343
367
ret = 0 ;
@@ -346,15 +370,23 @@ static int exchange_sdp(AVFormatContext *s)
346
370
if (ret <= 0 ) {
347
371
av_log (s , AV_LOG_ERROR , "Failed to read response from url=%s, offer is %s, answer is %s" ,
348
372
s -> url , rtc -> sdp_offer , rtc -> sdp_answer );
349
- return ret ;
373
+ goto end ;
350
374
}
351
375
352
- p = rtc -> sdp_answer ;
353
- rtc -> sdp_answer = av_asprintf ("%s%.*s" , p ? p : "" , ret , buf );
354
- av_free (p );
376
+ ret = av_strlcatf (tmp , MAX_SDP_SIZE , "%.*s" , ret , buf );
377
+ if (ret >= MAX_SDP_SIZE ) {
378
+ av_log (s , AV_LOG_ERROR , "Answer %d exceed max size %d, %s" , ret , MAX_SDP_SIZE , tmp );
379
+ ret = AVERROR (EINVAL );
380
+ goto end ;
381
+ }
355
382
}
383
+
384
+ rtc -> sdp_answer = av_strdup (tmp );
356
385
av_log (s , AV_LOG_VERBOSE , "Got answer: %s" , rtc -> sdp_answer );
357
386
387
+ end :
388
+ ffurl_closep (& whip_uc );
389
+ av_free (tmp );
358
390
return ret ;
359
391
}
360
392
@@ -372,9 +404,7 @@ static int parse_answer(AVFormatContext *s)
372
404
int i ;
373
405
RTCContext * rtc = s -> priv_data ;
374
406
375
- pb = avio_alloc_context (
376
- (unsigned char * )rtc -> sdp_answer , (int )strlen (rtc -> sdp_answer ),
377
- AVIO_FLAG_READ , NULL , NULL , NULL , NULL );
407
+ pb = avio_alloc_context (rtc -> sdp_answer , strlen (rtc -> sdp_answer ), AVIO_FLAG_READ , NULL , NULL , NULL , NULL );
378
408
if (!pb ) {
379
409
av_log (s , AV_LOG_ERROR , "Failed to alloc AVIOContext for answer: %s" , rtc -> sdp_answer );
380
410
ret = AVERROR (ENOMEM );
@@ -383,13 +413,11 @@ static int parse_answer(AVFormatContext *s)
383
413
384
414
for (i = 0 ; !avio_feof (pb ); i ++ ) {
385
415
ff_get_chomp_line (pb , line , sizeof (line ));
386
- if (av_strstart (line , "a=ice-ufrag:" , & ptr )) {
387
- av_freep (& rtc -> ice_ufrag_remote );
416
+ if (av_strstart (line , "a=ice-ufrag:" , & ptr ) && !rtc -> ice_ufrag_remote ) {
388
417
rtc -> ice_ufrag_remote = av_strdup (ptr );
389
- } else if (av_strstart (line , "a=ice-pwd:" , & ptr )) {
390
- av_freep (& rtc -> ice_pwd_remote );
418
+ } else if (av_strstart (line , "a=ice-pwd:" , & ptr ) && !rtc -> ice_pwd_remote ) {
391
419
rtc -> ice_pwd_remote = av_strdup (ptr );
392
- } else if (av_strstart (line , "a=candidate:" , & ptr )) {
420
+ } else if (av_strstart (line , "a=candidate:" , & ptr ) && ! rtc -> ice_protocol ) {
393
421
ptr = av_stristr (ptr , "udp" );
394
422
if (ptr && av_stristr (ptr , "host" )) {
395
423
char protocol [17 ], host [129 ];
@@ -403,27 +431,29 @@ static int parse_answer(AVFormatContext *s)
403
431
}
404
432
405
433
if (av_strcasecmp (protocol , "udp" )) {
406
- av_log (s , AV_LOG_ERROR , "Protocol %s is not supported by RTC, choose udp" , protocol );
434
+ av_log (s , AV_LOG_ERROR , "Protocol %s is not supported by RTC, choose udp, line %d %s of %s" ,
435
+ protocol , i , line , rtc -> sdp_answer );
407
436
ret = AVERROR (EINVAL );
408
437
goto end ;
409
438
}
410
439
411
- av_freep (& rtc -> ice_protocol );
412
440
rtc -> ice_protocol = av_strdup (protocol );
413
- av_freep (& rtc -> ice_host );
414
441
rtc -> ice_host = av_strdup (host );
415
- rtc -> ice_priority = priority ;
416
442
rtc -> ice_port = port ;
417
443
}
418
444
}
419
445
}
420
446
447
+ av_log (s , AV_LOG_VERBOSE , "SDP offer=%luB, answer=%luB, ufrag=%s, pwd=%luB, transport=%s://%s:%d\n" ,
448
+ strlen (rtc -> sdp_offer ), strlen (rtc -> sdp_answer ), rtc -> ice_ufrag_remote , strlen (rtc -> ice_pwd_remote ),
449
+ rtc -> ice_protocol , rtc -> ice_host , rtc -> ice_port );
450
+
421
451
end :
422
452
avio_context_free (& pb );
423
453
return ret ;
424
454
}
425
455
426
- static int rtc_init (AVFormatContext * s )
456
+ static av_cold int rtc_init (AVFormatContext * s )
427
457
{
428
458
int ret ;
429
459
@@ -457,12 +487,11 @@ static int rtc_write_trailer(AVFormatContext *s)
457
487
return 0 ;
458
488
}
459
489
460
- static void rtc_deinit (AVFormatContext * s )
490
+ static av_cold void rtc_deinit (AVFormatContext * s )
461
491
{
462
492
RTCContext * rtc = s -> priv_data ;
463
493
av_freep (& rtc -> sdp_offer );
464
494
av_freep (& rtc -> sdp_answer );
465
- ffurl_closep (& rtc -> whip_uc );
466
495
av_freep (& rtc -> ice_ufrag_remote );
467
496
av_freep (& rtc -> ice_pwd_remote );
468
497
av_freep (& rtc -> ice_protocol );
0 commit comments