diff --git a/lib/ex_webrtc/peer_connection.ex b/lib/ex_webrtc/peer_connection.ex index 9b11507c..b75cd48b 100644 --- a/lib/ex_webrtc/peer_connection.ex +++ b/lib/ex_webrtc/peer_connection.ex @@ -383,7 +383,7 @@ defmodule ExWebRTC.PeerConnection do demuxer = %{ state.demuxer | extensions: SDPUtils.get_extensions(sdp), - payload_types: SDPUtils.get_payload_types(sdp) + pt_to_mid: SDPUtils.get_payload_types(sdp) } dtls = diff --git a/lib/ex_webrtc/peer_connection/demuxer.ex b/lib/ex_webrtc/peer_connection/demuxer.ex index 249886ea..3951e5c3 100644 --- a/lib/ex_webrtc/peer_connection/demuxer.ex +++ b/lib/ex_webrtc/peer_connection/demuxer.ex @@ -5,7 +5,7 @@ defmodule ExWebRTC.PeerConnection.Demuxer do alias ExRTP.Packet.Extension alias ExRTP.Packet.Extension.SourceDescription - defstruct ssrcs: %{}, extensions: %{}, payload_types: %{} + defstruct ssrc_to_mid: %{}, extensions: %{}, pt_to_mid: %{} def process_data(demuxer, data) do with {:ok, %Packet{} = packet} <- decode(data), @@ -15,14 +15,12 @@ defmodule ExWebRTC.PeerConnection.Demuxer do end # RFC 8843, 9.2 - defp match_to_mid(demuxer, packet) do - with demuxer <- update_mapping(demuxer, packet), - :error <- match_by_extension(demuxer, packet), - :error <- match_by_payload_type(demuxer, packet) do - {:error, :unmatched_stream} - else - {:ok, mid} -> {:ok, demuxer, mid} - {:ok, _demuxer, _mid} = res -> res + defp match_to_mid(demuxer, %Packet{ssrc: ssrc} = packet) do + demuxer = update_mapping(demuxer, packet) + + case Map.get(demuxer.ssrc_to_mid, ssrc) do + {last_mid, _last_sn} -> {:ok, demuxer, last_mid} + nil -> match_by_payload_type(demuxer, packet) end end @@ -40,29 +38,22 @@ defmodule ExWebRTC.PeerConnection.Demuxer do end end) - case Map.get(demuxer.ssrcs, ssrc) do + case Map.get(demuxer.ssrc_to_mid, ssrc) do {_last_mid, last_sn} when mid != nil and sn > last_sn -> - put_in(demuxer.ssrcs[ssrc], {mid, sn}) + put_in(demuxer.ssrc_to_mid[ssrc], {mid, sn}) nil when mid != nil -> - put_in(demuxer.ssrcs[ssrc], {mid, sn}) + put_in(demuxer.ssrc_to_mid[ssrc], {mid, sn}) _other -> demuxer end end - defp match_by_extension(demuxer, %Packet{ssrc: ssrc}) do - case Map.get(demuxer.ssrcs, ssrc) do - {last_mid, _last_sn} -> {:ok, last_mid} - nil -> :error - end - end - - defp match_by_payload_type(demuxer, %Packet{ssrc: ssrc, payload_type: pt}) do - case Map.get(demuxer.payload_types, pt) do + defp match_by_payload_type(demuxer, %Packet{ssrc: ssrc, payload_type: pt, sequence_number: sn}) do + case Map.get(demuxer.pt_to_mid, pt) do nil -> :error - mid -> {:ok, put_in(demuxer.ssrcs[ssrc], mid), mid} + mid -> {:ok, put_in(demuxer.ssrc_to_mid[ssrc], {mid, sn}), mid} end end diff --git a/test/configuration_test.exs b/test/configuration_test.exs index e53c15b2..e335fc2e 100644 --- a/test/configuration_test.exs +++ b/test/configuration_test.exs @@ -1,5 +1,5 @@ defmodule ExWebRTC.PeerConnection.ConfigurationTest do - use ExUnit.Case + use ExUnit.Case, async: true alias ExWebRTC.{PeerConnection, RTPCodecParameters, RTPTransceiver, SessionDescription} diff --git a/test/peer_connection/demuxer_test.exs b/test/peer_connection/demuxer_test.exs new file mode 100644 index 00000000..ab2a56b0 --- /dev/null +++ b/test/peer_connection/demuxer_test.exs @@ -0,0 +1,76 @@ +defmodule ExWebRTC.PeerConnection.DemuxerTest do + use ExUnit.Case, async: true + + alias ExRTP.Packet + alias ExRTP.Packet.Extension + alias ExWebRTC.PeerConnection.Demuxer + + @mid "1" + + @sequence_number 500 + @payload_type 111 + @ssrc 333_333 + @raw_packet %Packet{ + payload_type: @payload_type, + sequence_number: @sequence_number, + timestamp: 0, + ssrc: @ssrc, + payload: <<>> + } + + @packet Packet.encode(@raw_packet) + @packet_mid Packet.encode( + Packet.set_extension(@raw_packet, :two_byte, [%Extension{id: 15, data: @mid}]) + ) + @demuxer %Demuxer{extensions: %{15 => {Extension.SourceDescription, :mid}}} + + test "ssrc already mapped and, without extension" do + seq_num = 1 + demuxer = %Demuxer{@demuxer | ssrc_to_mid: %{@ssrc => {@mid, seq_num}}} + + assert {:ok, new_demuxer, @mid, _packet} = Demuxer.process_data(demuxer, @packet) + assert new_demuxer == %Demuxer{demuxer | ssrc_to_mid: %{@ssrc => {@mid, seq_num}}} + end + + test "ssrc already mapped, with extension with the same mid and bigger sequence number" do + demuxer = %Demuxer{@demuxer | ssrc_to_mid: %{@ssrc => {@mid, 1}}} + + assert {:ok, new_demuxer, @mid, _packet} = Demuxer.process_data(demuxer, @packet_mid) + assert new_demuxer == %Demuxer{demuxer | ssrc_to_mid: %{@ssrc => {@mid, @sequence_number}}} + end + + test "ssrc already mapped, with extension with new mid and smaller sequence number" do + seq_num = 600 + mid = "2" + demuxer = %Demuxer{@demuxer | ssrc_to_mid: %{@ssrc => {mid, seq_num}}} + + assert {:ok, new_demuxer, ^mid, _packet} = Demuxer.process_data(demuxer, @packet_mid) + assert new_demuxer == %Demuxer{demuxer | ssrc_to_mid: %{@ssrc => {mid, seq_num}}} + end + + test "ssrc already mapped, with extension with new mid and bigger sequence number" do + seq_num = 1 + mid = "2" + demuxer = %Demuxer{@demuxer | ssrc_to_mid: %{@ssrc => {mid, seq_num}}} + + assert {:ok, new_demuxer, @mid, _packet} = Demuxer.process_data(demuxer, @packet_mid) + assert new_demuxer == %Demuxer{demuxer | ssrc_to_mid: %{@ssrc => {@mid, @sequence_number}}} + end + + test "ssrc not mapped, with extension" do + assert {:ok, new_demuxer, @mid, _packet} = Demuxer.process_data(@demuxer, @packet_mid) + assert new_demuxer == %Demuxer{@demuxer | ssrc_to_mid: %{@ssrc => {@mid, @sequence_number}}} + end + + test "ssrc not mapped, without extension, with unique payload type" do + mid = "2" + demuxer = %Demuxer{@demuxer | pt_to_mid: %{@payload_type => mid}} + + assert {:ok, new_demuxer, ^mid, _packet} = Demuxer.process_data(demuxer, @packet) + assert new_demuxer == %Demuxer{demuxer | ssrc_to_mid: %{@ssrc => {mid, @sequence_number}}} + end + + test "unmatchable ssrc" do + assert :error = Demuxer.process_data(@demuxer, @packet) + end +end