@@ -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,28 @@ 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
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
471
504
{ sdp_rtxs , sdp_codecs } = Enum . split_with ( sdp_codecs , & rtx? / 1 )
472
505
{ rtxs , codecs } = Enum . split_with ( codecs , & rtx? / 1 )
473
506
474
507
{ codecs , { free_pts , mapping } } =
475
508
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 ->
509
+ case find_in_sdp_codecs ( sdp_codecs , codec ) do
510
+ # there is no such codec and its payload type is not used
511
+ { nil , false } ->
512
+ { codec , { free_pts , Map . put ( mapping , codec . payload_type , codec . payload_type ) } }
513
+
514
+ # there is no such codec, but its payload type is used
515
+ { nil , true } ->
484
516
[ pt | rest ] = free_pts
485
517
new_codec = do_update_codec ( codec , pt )
486
518
{ new_codec , { rest , Map . put ( mapping , codec . payload_type , pt ) } }
487
519
488
- other ->
520
+ { other , _pt_used } ->
489
521
new_codec = do_update_codec ( codec , other . payload_type )
490
522
{ new_codec , { free_pts , Map . put ( mapping , codec . payload_type , other . payload_type ) } }
491
523
end
@@ -497,15 +529,18 @@ defmodule ExWebRTC.PeerConnection.Configuration do
497
529
% RTPCodecParameters { rtx | sdp_fmtp_line: % FMTP { fmtp | apt: Map . fetch! ( mapping , apt ) } }
498
530
end )
499
531
|> 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 ->
532
+ case find_in_sdp_rtx_codecs ( sdp_rtxs , rtx ) do
533
+ # there is no such codec and its payload type is not used
534
+ { nil , false } ->
535
+ { rtx , free_pts }
536
+
537
+ # thre is no such codec, but its payload type is used
538
+ { nil , true } ->
504
539
[ pt | rest ] = free_pts
505
540
rtx = do_update_codec ( rtx , pt )
506
541
{ rtx , rest }
507
542
508
- other ->
543
+ { other , _pt_used } ->
509
544
rtx = do_update_codec ( rtx , other . payload_type )
510
545
{ rtx , free_pts }
511
546
end
@@ -514,6 +549,46 @@ defmodule ExWebRTC.PeerConnection.Configuration do
514
549
{ codecs ++ rtxs , free_pts }
515
550
end
516
551
552
+ # Searches for codec in sdp_codecs.
553
+ # If codec is not found, pt_used determines whether
554
+ # codec's payload type is already present in sdp_codecs.
555
+ # Otherwise, pt_used can have any value.
556
+ defp find_in_sdp_codecs ( sdp_codecs , codec , pt_used \\ false )
557
+
558
+ defp find_in_sdp_codecs ( [ ] , _codec , pt_used ) , do: { nil , pt_used }
559
+
560
+ defp find_in_sdp_codecs ( [ sdp_codec | sdp_codecs ] , codec , pt_used ) do
561
+ if codec_equal_soft? ( sdp_codec , codec ) 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
+ end
571
+
572
+ # Searches for rtx codec in sdp_rtx_codecs.
573
+ # If rtx_codec is not found, pt_used determines whether
574
+ # rtx codec's payload type is already present in sdp rtx codecs.
575
+ # Otherwise, pt_used can have any value.
576
+ defp find_in_sdp_rtx_codecs ( sdp_rtx_codecs , rtx_codec , pt_used \\ false )
577
+
578
+ defp find_in_sdp_rtx_codecs ( [ ] , _rtx_codec , pt_used ) , do: { nil , pt_used }
579
+
580
+ defp find_in_sdp_rtx_codecs ( [ sdp_rtx_codec | sdp_rtx_codecs ] , rtx_codec , pt_used ) do
581
+ if sdp_rtx_codec . sdp_fmtp_line . apt == rtx_codec . sdp_fmtp_line . apt do
582
+ { sdp_rtx_codec , pt_used }
583
+ else
584
+ find_in_sdp_codecs (
585
+ sdp_rtx_codecs ,
586
+ rtx_codec ,
587
+ pt_used || sdp_rtx_codec . payload_type == rtx_codec . payload_type
588
+ )
589
+ end
590
+ end
591
+
517
592
defp do_update_codec ( codec , new_pt ) do
518
593
% RTPCodecParameters { rtcp_fbs: fbs , sdp_fmtp_line: fmtp } = codec
519
594
new_fbs = Enum . map ( fbs , & % RTCPFeedback { & 1 | pt: new_pt } )
@@ -549,6 +624,7 @@ defmodule ExWebRTC.PeerConnection.Configuration do
549
624
end )
550
625
end
551
626
627
+ # soft functions does not compare payload types
552
628
@ doc false
553
629
@ spec codec_equal? ( RTPCodecParameters . t ( ) , RTPCodecParameters . t ( ) ) :: boolean ( )
554
630
def codec_equal? ( c1 , c2 ) do
@@ -558,6 +634,14 @@ defmodule ExWebRTC.PeerConnection.Configuration do
558
634
c1 . channels == c2 . channels and fmtp_equal? ( c1 , c2 )
559
635
end
560
636
637
+ @ doc false
638
+ @ spec codec_equal_soft? ( RTPCodecParameters . t ( ) , RTPCodecParameters . t ( ) ) :: boolean ( )
639
+ def codec_equal_soft? ( c1 , c2 ) do
640
+ String . downcase ( c1 . mime_type ) == String . downcase ( c2 . mime_type ) and
641
+ c1 . clock_rate == c2 . clock_rate and
642
+ c1 . channels == c2 . channels and fmtp_equal_soft? ( c1 , c2 )
643
+ end
644
+
561
645
defp fmtp_equal? ( % { sdp_fmtp_line: nil } , _c2 ) , do: true
562
646
defp fmtp_equal? ( _c1 , % { sdp_fmtp_line: nil } ) , do: true
563
647
defp fmtp_equal? ( c1 , c2 ) , do: c1 . sdp_fmtp_line == c2 . sdp_fmtp_line
0 commit comments