@@ -263,10 +263,27 @@ defmodule ExWebRTC.PeerConnection.Configuration do
263
263
|> Keyword . put ( :audio_extensions , Enum . map ( audio_extensions , fn { _ , ext } -> ext end ) )
264
264
|> Keyword . put ( :video_extensions , Enum . map ( video_extensions , fn { _ , ext } -> ext end ) )
265
265
|> then ( & struct ( __MODULE__ , & 1 ) )
266
+ |> ensure_unique_payload_types ( )
266
267
|> populate_feedbacks ( feedbacks )
267
268
|> add_features ( )
268
269
end
269
270
271
+ defp ensure_unique_payload_types ( config ) do
272
+ audio_pt = Enum . map ( config . audio_codecs , fn codec -> codec . payload_type end )
273
+
274
+ if length ( audio_pt ) != length ( Enum . uniq ( audio_pt ) ) do
275
+ raise "Payload types in audio codecs are not unique."
276
+ end
277
+
278
+ video_pt = Enum . map ( config . video_codecs , fn codec -> codec . payload_type end )
279
+
280
+ if length ( video_pt ) != length ( Enum . uniq ( video_pt ) ) do
281
+ raise "Payload types in video codecs are not unique."
282
+ end
283
+
284
+ config
285
+ end
286
+
270
287
defp add_features ( config ) do
271
288
% __MODULE__ { features: features } = config
272
289
@@ -436,21 +453,37 @@ defmodule ExWebRTC.PeerConnection.Configuration do
436
453
defp do_update_extensions ( extensions , sdp_extensions , free_ids ) do
437
454
# we replace extension ids in config to ids from the SDP
438
455
# in case we have an extension in config but not in SDP, we replace
439
- # its id to some free (not present in SDP) id, so it doesn't conflict
456
+ # its id only when it's occupied to some free (not present in SDP) id, so it doesn't conflict
440
457
Enum . map_reduce ( extensions , free_ids , fn ext , free_ids ->
441
- sdp_extensions
442
- |> Enum . find ( & ( & 1 . uri == ext . uri ) )
443
- |> case do
444
- nil ->
458
+ case find_in_sdp_rtp_extensions ( sdp_extensions , ext ) do
459
+ { nil , false } ->
460
+ { ext , free_ids }
461
+
462
+ { nil , true } ->
445
463
[ id | rest ] = free_ids
446
464
{ % Extmap { ext | id: id } , rest }
447
465
448
- other ->
466
+ { other , _id_used } ->
449
467
{ % Extmap { ext | id: other . id } , free_ids }
450
468
end
451
469
end )
452
470
end
453
471
472
+ # Searches for rtp extension in sdp rtp extensions.
473
+ # If ext is not found, id_used determines whether ext's id
474
+ # is already present in sdp_extensions.
475
+ # Otherwise, id_used can have any value.
476
+ defp find_in_sdp_rtp_extensions ( sdp_extensions , ext , id_used \\ false )
477
+ defp find_in_sdp_rtp_extensions ( [ ] , _ext , id_used ) , do: { nil , id_used }
478
+
479
+ defp find_in_sdp_rtp_extensions ( [ sdp_ext | sdp_extensions ] , ext , id_used ) do
480
+ if sdp_ext . uri == ext . uri do
481
+ { sdp_ext , id_used }
482
+ else
483
+ find_in_sdp_rtp_extensions ( sdp_extensions , ext , id_used || sdp_ext . id == ext . id )
484
+ end
485
+ end
486
+
454
487
defp update_codecs ( config , sdp ) do
455
488
% __MODULE__ { audio_codecs: audio_codecs , video_codecs: video_codecs } = config
456
489
sdp_codecs = SDPUtils . get_rtp_codec_parameters ( sdp )
@@ -463,29 +496,27 @@ defmodule ExWebRTC.PeerConnection.Configuration do
463
496
end
464
497
465
498
defp do_update_codecs ( codecs , sdp_codecs , free_pts ) do
466
- # we replace codec payload types in config to payload types from SDP
467
- # both normal codecs and rtx (we also update apt FMTP attribute in rtxs)
468
- # other codecs that are present in config but not in SDP
469
- # are also updated with values from a pool of free payload types (not present in SDP)
470
- # to make sure they don't conflict
471
- { sdp_rtxs , sdp_codecs } = Enum . split_with ( sdp_codecs , & rtx? / 1 )
499
+ # We replace codec payload types in config to payload types from SDP
500
+ # both for normal codecs and rtx (we also update apt FMTP attribute in rtxs).
501
+ # Other codecs that are present in config but not in SDP, and their
502
+ # payload type is already present in SDP, are also updated with values
503
+ # from a pool of free payload types (not present in SDP) to make sure they don't conflict
472
504
{ rtxs , codecs } = Enum . split_with ( codecs , & rtx? / 1 )
473
505
474
506
{ codecs , { free_pts , mapping } } =
475
507
Enum . map_reduce ( codecs , { free_pts , % { } } , fn codec , { free_pts , mapping } ->
476
- sdp_codecs
477
- |> Enum . find (
478
- & ( String . downcase ( & 1 . mime_type ) == String . downcase ( codec . mime_type ) and
479
- & 1 . clock_rate == codec . clock_rate and
480
- & 1 . channels == codec . channels and fmtp_equal_soft? ( codec , & 1 ) )
481
- )
482
- |> case do
483
- nil ->
508
+ case find_in_sdp_codecs ( sdp_codecs , codec ) do
509
+ # there is no such codec and its payload type is not used
510
+ { nil , false } ->
511
+ { codec , { free_pts , Map . put ( mapping , codec . payload_type , codec . payload_type ) } }
512
+
513
+ # there is no such codec, but its payload type is used
514
+ { nil , true } ->
484
515
[ pt | rest ] = free_pts
485
516
new_codec = do_update_codec ( codec , pt )
486
517
{ new_codec , { rest , Map . put ( mapping , codec . payload_type , pt ) } }
487
518
488
- other ->
519
+ { other , _pt_used } ->
489
520
new_codec = do_update_codec ( codec , other . payload_type )
490
521
{ new_codec , { free_pts , Map . put ( mapping , codec . payload_type , other . payload_type ) } }
491
522
end
@@ -497,15 +528,18 @@ defmodule ExWebRTC.PeerConnection.Configuration do
497
528
% RTPCodecParameters { rtx | sdp_fmtp_line: % FMTP { fmtp | apt: Map . fetch! ( mapping , apt ) } }
498
529
end )
499
530
|> Enum . map_reduce ( free_pts , fn rtx , free_pts ->
500
- sdp_rtxs
501
- |> Enum . find ( & ( & 1 . sdp_fmtp_line . apt == rtx . sdp_fmtp_line . apt ) )
502
- |> case do
503
- nil ->
531
+ case find_in_sdp_codecs ( sdp_codecs , rtx ) do
532
+ # there is no such codec and its payload type is not used
533
+ { nil , false } ->
534
+ { rtx , free_pts }
535
+
536
+ # thre is no such codec, but its payload type is used
537
+ { nil , true } ->
504
538
[ pt | rest ] = free_pts
505
539
rtx = do_update_codec ( rtx , pt )
506
540
{ rtx , rest }
507
541
508
- other ->
542
+ { other , _pt_used } ->
509
543
rtx = do_update_codec ( rtx , other . payload_type )
510
544
{ rtx , free_pts }
511
545
end
@@ -514,6 +548,38 @@ defmodule ExWebRTC.PeerConnection.Configuration do
514
548
{ codecs ++ rtxs , free_pts }
515
549
end
516
550
551
+ # Searches for codec in sdp_codecs.
552
+ # If codec is not found, pt_used determines whether
553
+ # codec's payload type is already present in sdp_codecs.
554
+ # Otherwise, pt_used can have any value.
555
+ defp find_in_sdp_codecs ( sdp_codecs , codec , pt_used \\ false )
556
+
557
+ defp find_in_sdp_codecs ( [ ] , _codec , pt_used ) , do: { nil , pt_used }
558
+
559
+ defp find_in_sdp_codecs ( [ sdp_codec | sdp_codecs ] , codec , pt_used ) do
560
+ if String . ends_with? ( codec . mime_type , "/rtx" ) do
561
+ if sdp_codec . sdp_fmtp_line != nil && sdp_codec . sdp_fmtp_line . apt == codec . sdp_fmtp_line . apt do
562
+ { sdp_codec , pt_used }
563
+ else
564
+ find_in_sdp_codecs (
565
+ sdp_codecs ,
566
+ codec ,
567
+ pt_used || sdp_codec . payload_type == codec . payload_type
568
+ )
569
+ end
570
+ else
571
+ if codec_equal_soft? ( sdp_codec , codec ) do
572
+ { sdp_codec , pt_used }
573
+ else
574
+ find_in_sdp_codecs (
575
+ sdp_codecs ,
576
+ codec ,
577
+ pt_used || sdp_codec . payload_type == codec . payload_type
578
+ )
579
+ end
580
+ end
581
+ end
582
+
517
583
defp do_update_codec ( codec , new_pt ) do
518
584
% RTPCodecParameters { rtcp_fbs: fbs , sdp_fmtp_line: fmtp } = codec
519
585
new_fbs = Enum . map ( fbs , & % RTCPFeedback { & 1 | pt: new_pt } )
@@ -549,6 +615,7 @@ defmodule ExWebRTC.PeerConnection.Configuration do
549
615
end )
550
616
end
551
617
618
+ # soft functions does not compare payload types
552
619
@ doc false
553
620
@ spec codec_equal? ( RTPCodecParameters . t ( ) , RTPCodecParameters . t ( ) ) :: boolean ( )
554
621
def codec_equal? ( c1 , c2 ) do
@@ -558,6 +625,14 @@ defmodule ExWebRTC.PeerConnection.Configuration do
558
625
c1 . channels == c2 . channels and fmtp_equal? ( c1 , c2 )
559
626
end
560
627
628
+ @ doc false
629
+ @ spec codec_equal_soft? ( RTPCodecParameters . t ( ) , RTPCodecParameters . t ( ) ) :: boolean ( )
630
+ def codec_equal_soft? ( c1 , c2 ) do
631
+ String . downcase ( c1 . mime_type ) == String . downcase ( c2 . mime_type ) and
632
+ c1 . clock_rate == c2 . clock_rate and
633
+ c1 . channels == c2 . channels and fmtp_equal_soft? ( c1 , c2 )
634
+ end
635
+
561
636
defp fmtp_equal? ( % { sdp_fmtp_line: nil } , _c2 ) , do: true
562
637
defp fmtp_equal? ( _c1 , % { sdp_fmtp_line: nil } ) , do: true
563
638
defp fmtp_equal? ( c1 , c2 ) , do: c1 . sdp_fmtp_line == c2 . sdp_fmtp_line
0 commit comments