From e662577a5eb3df1708c8858f3e7227d4d737f544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=9Aled=C5=BA?= Date: Fri, 7 Mar 2025 15:53:30 +0100 Subject: [PATCH 1/4] Dynamically determine ICE agent's role --- lib/ex_webrtc/ice_transport.ex | 10 +++++-- lib/ex_webrtc/peer_connection.ex | 26 +++++++++++++++++-- .../peer_connection/configuration.ex | 1 + lib/ex_webrtc/sdp_utils.ex | 3 +++ mix.exs | 2 +- mix.lock | 2 +- test/ex_webrtc/dtls_transport_test.exs | 26 ++++++++++++++++--- test/ex_webrtc/peer_connection_test.exs | 2 +- 8 files changed, 62 insertions(+), 10 deletions(-) diff --git a/lib/ex_webrtc/ice_transport.ex b/lib/ex_webrtc/ice_transport.ex index 9c53e758..190bc40a 100644 --- a/lib/ex_webrtc/ice_transport.ex +++ b/lib/ex_webrtc/ice_transport.ex @@ -5,7 +5,7 @@ defmodule ExWebRTC.ICETransport do @type t() :: module() @type state() :: :checking | :connected | :completed | :failed - @callback start_link(ExICE.ICEAgent.role(), Keyword.t()) :: {:ok, pid()} + @callback start_link(Keyword.t()) :: {:ok, pid()} @callback on_data(pid(), pid()) :: :ok @callback add_remote_candidate(pid(), candidate :: String.t()) :: :ok @callback end_of_candidates(pid()) :: :ok @@ -13,8 +13,10 @@ defmodule ExWebRTC.ICETransport do @callback get_local_credentials(pid()) :: {:ok, ufrag :: binary(), pwd :: binary()} @callback get_local_candidates(pid()) :: [binary()] @callback get_remote_candidates(pid()) :: [binary()] + @callback get_role(pid()) :: ExICE.ICEAgent.role() | nil @callback restart(pid()) :: :ok @callback send_data(pid(), binary()) :: :ok + @callback set_role(pid(), ExICE.ICEAgent.role()) :: :ok @callback set_remote_credentials(pid(), ufrag :: binary(), pwd :: binary()) :: :ok @callback get_stats(pid()) :: map() @callback stop(pid()) :: :ok @@ -28,7 +30,7 @@ defmodule ExWebRTC.DefaultICETransport do alias ExICE.ICEAgent @impl true - defdelegate start_link(role, opts), to: ICEAgent + defdelegate start_link(opts), to: ICEAgent @impl true defdelegate on_data(pid, dst_pid), to: ICEAgent @impl true @@ -38,6 +40,8 @@ defmodule ExWebRTC.DefaultICETransport do @impl true defdelegate gather_candidates(pid), to: ICEAgent @impl true + defdelegate get_role(pid), to: ICEAgent + @impl true defdelegate get_local_credentials(pid), to: ICEAgent @impl true defdelegate get_local_candidates(pid), to: ICEAgent @@ -48,6 +52,8 @@ defmodule ExWebRTC.DefaultICETransport do @impl true defdelegate send_data(pid, data), to: ICEAgent @impl true + defdelegate set_role(pid, role), to: ICEAgent + @impl true defdelegate set_remote_credentials(pid, ufrag, pwd), to: ICEAgent @impl true defdelegate get_stats(pid), to: ICEAgent diff --git a/lib/ex_webrtc/peer_connection.ex b/lib/ex_webrtc/peer_connection.ex index 05895759..e04eb238 100644 --- a/lib/ex_webrtc/peer_connection.ex +++ b/lib/ex_webrtc/peer_connection.ex @@ -577,7 +577,7 @@ defmodule ExWebRTC.PeerConnection do on_data: nil ] - {:ok, ice_pid} = DefaultICETransport.start_link(:controlled, ice_config) + {:ok, ice_pid} = DefaultICETransport.start_link(ice_config) {:ok, dtls_transport} = DTLSTransport.start_link(DefaultICETransport, ice_pid) # route data to the DTLSTransport :ok = DefaultICETransport.on_data(ice_pid, dtls_transport) @@ -1188,7 +1188,7 @@ defmodule ExWebRTC.PeerConnection do timestamp: timestamp, ice_state: ice_stats.state, ice_gathering_state: state.ice_gathering_state, - ice_role: ice_stats.role, + ice_role: ice_stats.role || :unknown, ice_local_ufrag: ice_stats.local_ufrag, dtls_state: state.dtls_state, bytes_sent: ice_stats.bytes_sent, @@ -1361,6 +1361,8 @@ defmodule ExWebRTC.PeerConnection do def handle_info({:dtls_transport, _pid, {:state_change, new_dtls_state}}, state) do next_conn_state = next_conn_state(state.ice_state, new_dtls_state) + notify(state.owner, {:dtls_state_change, new_dtls_state}) + state = %{state | dtls_state: new_dtls_state} |> update_conn_state(next_conn_state) @@ -1717,6 +1719,13 @@ defmodule ExWebRTC.PeerConnection do with {:ok, next_sig_state} <- next_signaling_state(state.signaling_state, :local, type), :ok <- check_altered(type, raw_sdp, state), {:ok, sdp} <- parse_sdp(raw_sdp) do + # See: https://www.w3.org/TR/webrtc/#ref-for-dfn-icerole-1 + # Also, this has to be before gathering candidates. + # Note: If we add support for ice-lite, this code needs to be adjusted. + if state.ice_transport.get_role(state.ice_pid) == nil and type == :offer do + :ok = state.ice_transport.set_role(state.ice_pid, :controlling) + end + if state.ice_gathering_state == :new do state.ice_transport.gather_candidates(state.ice_pid) end @@ -1755,10 +1764,23 @@ defmodule ExWebRTC.PeerConnection do {:ok, sdp} <- parse_sdp(raw_sdp), :ok <- SDPUtils.ensure_valid(sdp), {:ok, ice_creds} <- SDPUtils.get_ice_credentials(sdp), + ice_lite <- SDPUtils.get_ice_lite(sdp), {:ok, {:fingerprint, {:sha256, peer_fingerprint}}} <- SDPUtils.get_cert_fingerprint(sdp), {:ok, dtls_role} <- SDPUtils.get_dtls_role(sdp) do config = Configuration.update(state.config, sdp) + # See: https://www.w3.org/TR/webrtc/#ref-for-dfn-icerole-1 + # Note: If we add support for ice-lite, this code needs to be adjusted. + if state.ice_transport.get_role(state.ice_pid) == nil do + cond do + type == :offer and ice_lite == true -> + :ok = state.ice_transport.set_role(state.ice_pid, :controlling) + + type == :offer and ice_lite == false -> + :ok = state.ice_transport.set_role(state.ice_pid, :controlled) + end + end + twcc_id = (config.video_extensions ++ config.audio_extensions) |> Enum.find(&(&1.uri == @twcc_uri)) diff --git a/lib/ex_webrtc/peer_connection/configuration.ex b/lib/ex_webrtc/peer_connection/configuration.ex index 5c8ca0ec..02619cee 100644 --- a/lib/ex_webrtc/peer_connection/configuration.ex +++ b/lib/ex_webrtc/peer_connection/configuration.ex @@ -199,6 +199,7 @@ defmodule ExWebRTC.PeerConnection.Configuration do ice_servers: [], ice_transport_policy: :all, ice_port_range: [0], + ice_role: :controlled, audio_codecs: @default_audio_codecs, video_codecs: @default_video_codecs, features: @default_features diff --git a/lib/ex_webrtc/sdp_utils.ex b/lib/ex_webrtc/sdp_utils.ex index b16985eb..fe2baec4 100644 --- a/lib/ex_webrtc/sdp_utils.ex +++ b/lib/ex_webrtc/sdp_utils.ex @@ -139,6 +139,9 @@ defmodule ExWebRTC.SDPUtils do |> Enum.map(fn msid -> msid.id end) end + @spec get_ice_lite(ExSDP.t()) :: boolean() + def get_ice_lite(sdp), do: ExSDP.get_attribute(sdp, "ice-lite") != nil + @spec get_ice_credentials(ExSDP.t()) :: {:ok, {binary(), binary()} | nil} | {:error, diff --git a/mix.exs b/mix.exs index e297f8e9..5e45b312 100644 --- a/mix.exs +++ b/mix.exs @@ -57,7 +57,7 @@ defmodule ExWebRTC.MixProject do defp deps do [ {:ex_sdp, "~> 1.0"}, - {:ex_ice, "~> 0.10.0"}, + {:ex_ice, github: "elixir-webrtc/ex_ice"}, {:ex_dtls, "~> 0.16.0"}, {:ex_libsrtp, "~> 0.7.1"}, {:ex_rtp, "~> 0.4.0"}, diff --git a/mix.lock b/mix.lock index 5cd5485f..705acf7c 100644 --- a/mix.lock +++ b/mix.lock @@ -12,7 +12,7 @@ "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "ex_doc": {:hex, :ex_doc, "0.37.0", "970f92b39e62c460aa8a367508e938f5e4da6e2ff3eaed3f8530b25870f45471", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "b0ee7f17373948e0cf471e59c3a0ee42f3bd1171c67d91eb3626456ef9c6202c"}, "ex_dtls": {:hex, :ex_dtls, "0.16.0", "3ae38025ccc77f6db573e2e391602fa9bbc02253c137d8d2d59469a66cbe806b", [:mix], [{:bundlex, "~> 1.5.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2a4e30d74c6ddf95cc5b796423293c06a0da295454c3823819808ff031b4b361"}, - "ex_ice": {:hex, :ex_ice, "0.10.0", "cbfe28b01fea0dcb70b4c289db21e712bf22d26f63f32d6a67326d178dbdcaf5", [:mix], [{:elixir_uuid, "~> 1.0", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:ex_stun, "~> 0.2.0", [hex: :ex_stun, repo: "hexpm", optional: false]}, {:ex_turn, "~> 0.2.0", [hex: :ex_turn, repo: "hexpm", optional: false]}], "hexpm", "1490f979d7eb47115a50ebb0bae7c06d13d22932aaf43e42088474582f2af705"}, + "ex_ice": {:git, "https://github.com/elixir-webrtc/ex_ice.git", "b58aceee2641a35c3edfadaaef6fa9d7c48258d8", []}, "ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"}, "ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"}, "ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"}, diff --git a/test/ex_webrtc/dtls_transport_test.exs b/test/ex_webrtc/dtls_transport_test.exs index 00540dd2..25f93944 100644 --- a/test/ex_webrtc/dtls_transport_test.exs +++ b/test/ex_webrtc/dtls_transport_test.exs @@ -23,7 +23,7 @@ defmodule ExWebRTC.DTLSTransportTest do use GenServer @impl true - def start_link(_mode, config), do: GenServer.start_link(__MODULE__, config) + def start_link(config), do: GenServer.start_link(__MODULE__, config) @impl true def on_data(ice_pid, dst_pid), do: GenServer.call(ice_pid, {:on_data, dst_pid}) @@ -40,6 +40,11 @@ defmodule ExWebRTC.DTLSTransportTest do @impl true def gather_candidates(ice_pid), do: ice_pid + @impl true + def get_role(ice_pid) do + GenServer.call(ice_pid, :get_role) + end + @impl true def get_local_credentials(_state), do: {:ok, "testufrag", "testpwd"} @@ -55,6 +60,11 @@ defmodule ExWebRTC.DTLSTransportTest do @impl true def set_remote_credentials(ice_pid, _ufrag, _pwd), do: ice_pid + @impl true + def set_role(ice_pid, role) do + GenServer.cast(ice_pid, {:set_role, role}) + end + @impl true def get_stats(_ice_pid), do: %{} @@ -65,13 +75,23 @@ defmodule ExWebRTC.DTLSTransportTest do @impl true def init(tester: tester), - do: {:ok, %{on_data_dst: nil, tester: tester}} + do: {:ok, %{role: nil, on_data_dst: nil, tester: tester}} @impl true def handle_call({:on_data, dst_pid}, _from, state) do {:reply, :ok, %{state | on_data_dst: dst_pid}} end + @impl true + def handle_call(:get_role, _from, state) do + {:reply, state.role, state} + end + + @impl true + def handle_cast({:set_role, role}, state) do + {:noreply, %{state | role: role}} + end + @impl true def handle_cast({:send_data, data}, state) do send(state.tester, {:mock_ice, data}) @@ -86,7 +106,7 @@ defmodule ExWebRTC.DTLSTransportTest do end setup do - {:ok, ice_pid} = MockICETransport.start_link(:controlled, tester: self()) + {:ok, ice_pid} = MockICETransport.start_link(tester: self()) assert {:ok, dtls} = DTLSTransport.start_link(MockICETransport, ice_pid) MockICETransport.on_data(ice_pid, dtls) assert_receive {:dtls_transport, ^dtls, {:state_change, :new}} diff --git a/test/ex_webrtc/peer_connection_test.exs b/test/ex_webrtc/peer_connection_test.exs index 77eb0862..d8885c97 100644 --- a/test/ex_webrtc/peer_connection_test.exs +++ b/test/ex_webrtc/peer_connection_test.exs @@ -970,7 +970,7 @@ defmodule ExWebRTC.PeerConnectionTest do assert is_binary(stats.local_certificate.fingerprint) assert is_binary(stats.local_certificate.base64_certificate) - assert stats.transport.ice_role in [:controlling, :controlled] + assert stats.transport.ice_role == :unknown assert is_binary(stats.transport.ice_local_ufrag) groups = Enum.group_by(Map.values(stats), & &1.type) From e5118dba765b26208ab12ee5884ac7110a4646cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=9Aled=C5=BA?= Date: Mon, 10 Mar 2025 22:23:26 +0100 Subject: [PATCH 2/4] Add get_dtls_transport_state --- lib/ex_webrtc/peer_connection.ex | 32 +++++++++++++++++++++++-- test/ex_webrtc/peer_connection_test.exs | 5 ++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/ex_webrtc/peer_connection.ex b/lib/ex_webrtc/peer_connection.ex index e04eb238..6ac6c0e6 100644 --- a/lib/ex_webrtc/peer_connection.ex +++ b/lib/ex_webrtc/peer_connection.ex @@ -53,6 +53,13 @@ defmodule ExWebRTC.PeerConnection do @type ice_connection_state() :: :new | :checking | :connected | :completed | :failed | :disconnected | :closed + @typedoc """ + Possible DTLS transport state. + + For the exact meaning, refer to the [RTCDtlsTransport: state property](https://developer.mozilla.org/en-US/docs/Web/API/RTCDtlsTransport/state) + """ + @type dtls_transport_state() :: :new | :connecting | :connected | :failed + @typedoc """ Possible signaling states. @@ -65,6 +72,11 @@ defmodule ExWebRTC.PeerConnection do Most of the messages match the [RTCPeerConnection events](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection#events), except for: + * `:dtls_transport_state_change` - traditional WebRTC implementation does not emit such event. + Instead, developer can read DTLS transport state by iterating over RTP receiver/senders, and checking their + DTLS transports states. See https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/transport. + However, because Elixir WebRTC creates a single DTLS transport for all receivers and senders, there is one generic + notification informing about DTLS transport state. * `:track_muted`, `:track_ended` - these match the [MediaStreamTrack events](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack#events). * `:data` - data received from DataChannel identified by its `ref`. * `:rtp` and `:rtcp` - these contain packets received by the PeerConnection. The third element of `:rtp` tuple is a simulcast RID and is set to `nil` if simulcast @@ -79,6 +91,7 @@ defmodule ExWebRTC.PeerConnection do | {:ice_candidate, ICECandidate.t()} | {:ice_connection_state_change, ice_connection_state()} | {:ice_gathering_state_change, ice_gathering_state()} + | {:dtls_transport_state_change, dtls_transport_state()} | :negotiation_needed | {:signaling_state_change, signaling_state()} | {:data_channel_state_change, DataChannel.ref(), DataChannel.ready_state()} @@ -292,6 +305,16 @@ defmodule ExWebRTC.PeerConnection do GenServer.call(peer_connection, :get_ice_gathering_state) end + @doc """ + Returns the DTLS transport state. + + For more information, refer to the [RTCDtlsTransport: state property](https://developer.mozilla.org/en-US/docs/Web/API/RTCDtlsTransport/state). + """ + @spec get_dtls_transport_state(peer_connection()) :: dtls_transport_state() + def get_dtls_transport_state(peer_connection) do + GenServer.call(peer_connection, :get_dtls_transport_state) + end + @doc """ Returns the signaling state. @@ -675,6 +698,11 @@ defmodule ExWebRTC.PeerConnection do {:reply, state.ice_gathering_state, state} end + @impl true + def handle_call(:get_dtls_transport_state, _from, state) do + {:reply, state.dtls_state, state} + end + @impl true def handle_call(:get_signaling_state, _from, state) do {:reply, state.signaling_state, state} @@ -1359,9 +1387,9 @@ defmodule ExWebRTC.PeerConnection do @impl true def handle_info({:dtls_transport, _pid, {:state_change, new_dtls_state}}, state) do - next_conn_state = next_conn_state(state.ice_state, new_dtls_state) + notify(state.owner, {:dtls_transport_state_change, new_dtls_state}) - notify(state.owner, {:dtls_state_change, new_dtls_state}) + next_conn_state = next_conn_state(state.ice_state, new_dtls_state) state = %{state | dtls_state: new_dtls_state} diff --git a/test/ex_webrtc/peer_connection_test.exs b/test/ex_webrtc/peer_connection_test.exs index d8885c97..1f3c149f 100644 --- a/test/ex_webrtc/peer_connection_test.exs +++ b/test/ex_webrtc/peer_connection_test.exs @@ -298,6 +298,11 @@ defmodule ExWebRTC.PeerConnectionTest do :ok = PeerConnection.close(pc2) end + test "get_dtls_transport_state/1" do + {:ok, pc} = PeerConnection.start_link() + assert PeerConnection.get_dtls_transport_state(pc) == :new + end + describe "get_local_description/1" do test "includes ICE candidates" do {:ok, pc} = PeerConnection.start() From 1f3e6aeca283d57d72184fd2d10335553dc2cb44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=9Aled=C5=BA?= Date: Mon, 10 Mar 2025 22:34:10 +0100 Subject: [PATCH 3/4] Make credo happy --- lib/ex_webrtc/peer_connection.ex | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/ex_webrtc/peer_connection.ex b/lib/ex_webrtc/peer_connection.ex index 6ac6c0e6..780f1b00 100644 --- a/lib/ex_webrtc/peer_connection.ex +++ b/lib/ex_webrtc/peer_connection.ex @@ -1746,12 +1746,13 @@ defmodule ExWebRTC.PeerConnection do defp apply_local_description(%SessionDescription{type: type, sdp: raw_sdp}, state) do with {:ok, next_sig_state} <- next_signaling_state(state.signaling_state, :local, type), :ok <- check_altered(type, raw_sdp, state), - {:ok, sdp} <- parse_sdp(raw_sdp) do + {:ok, sdp} <- parse_sdp(raw_sdp), + ice_lite <- SDPUtils.get_ice_lite(sdp) do # See: https://www.w3.org/TR/webrtc/#ref-for-dfn-icerole-1 # Also, this has to be before gathering candidates. # Note: If we add support for ice-lite, this code needs to be adjusted. - if state.ice_transport.get_role(state.ice_pid) == nil and type == :offer do - :ok = state.ice_transport.set_role(state.ice_pid, :controlling) + if state.ice_transport.get_role(state.ice_pid) == nil do + set_ice_role(state, :local, type, ice_lite) end if state.ice_gathering_state == :new do @@ -1800,13 +1801,7 @@ defmodule ExWebRTC.PeerConnection do # See: https://www.w3.org/TR/webrtc/#ref-for-dfn-icerole-1 # Note: If we add support for ice-lite, this code needs to be adjusted. if state.ice_transport.get_role(state.ice_pid) == nil do - cond do - type == :offer and ice_lite == true -> - :ok = state.ice_transport.set_role(state.ice_pid, :controlling) - - type == :offer and ice_lite == false -> - :ok = state.ice_transport.set_role(state.ice_pid, :controlled) - end + set_ice_role(state, :remote, type, ice_lite) end twcc_id = @@ -1972,6 +1967,18 @@ defmodule ExWebRTC.PeerConnection do %{state | pending_remote_desc: {type, sdp}} end + defp set_ice_role(state, :local, :offer, _ice_lite) do + :ok = state.ice_transport.set_role(state.ice_pid, :controlling) + end + + defp set_ice_role(state, :remote, :offer, true) do + :ok = state.ice_transport.set_role(state.ice_pid, :controlling) + end + + defp set_ice_role(state, :remote, :offer, false) do + :ok = state.ice_transport.set_role(state.ice_pid, :controlled) + end + defp parse_sdp(raw_sdp) do case ExSDP.parse(raw_sdp) do {:ok, _sdp} = res -> res From fc689773ce85808f297ffb36a3e31f88e16b0d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=9Aled=C5=BA?= Date: Mon, 10 Mar 2025 22:49:20 +0100 Subject: [PATCH 4/4] Minor docs improvements --- lib/ex_webrtc/peer_connection.ex | 19 ++++++++++--------- .../peer_connection/configuration.ex | 1 - 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/ex_webrtc/peer_connection.ex b/lib/ex_webrtc/peer_connection.ex index 780f1b00..e4563fec 100644 --- a/lib/ex_webrtc/peer_connection.ex +++ b/lib/ex_webrtc/peer_connection.ex @@ -54,7 +54,7 @@ defmodule ExWebRTC.PeerConnection do :new | :checking | :connected | :completed | :failed | :disconnected | :closed @typedoc """ - Possible DTLS transport state. + Possible DTLS transport states. For the exact meaning, refer to the [RTCDtlsTransport: state property](https://developer.mozilla.org/en-US/docs/Web/API/RTCDtlsTransport/state) """ @@ -73,10 +73,10 @@ defmodule ExWebRTC.PeerConnection do Most of the messages match the [RTCPeerConnection events](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection#events), except for: * `:dtls_transport_state_change` - traditional WebRTC implementation does not emit such event. - Instead, developer can read DTLS transport state by iterating over RTP receiver/senders, and checking their + Instead, developer can read DTLS transport state by iterating over RTP receivers/senders, and checking their DTLS transports states. See https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/transport. However, because Elixir WebRTC creates a single DTLS transport for all receivers and senders, there is one generic - notification informing about DTLS transport state. + notification for convenience and parity with other events informing about ice/signaling/connection state changes. * `:track_muted`, `:track_ended` - these match the [MediaStreamTrack events](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack#events). * `:data` - data received from DataChannel identified by its `ref`. * `:rtp` and `:rtcp` - these contain packets received by the PeerConnection. The third element of `:rtp` tuple is a simulcast RID and is set to `nil` if simulcast @@ -1748,9 +1748,7 @@ defmodule ExWebRTC.PeerConnection do :ok <- check_altered(type, raw_sdp, state), {:ok, sdp} <- parse_sdp(raw_sdp), ice_lite <- SDPUtils.get_ice_lite(sdp) do - # See: https://www.w3.org/TR/webrtc/#ref-for-dfn-icerole-1 - # Also, this has to be before gathering candidates. - # Note: If we add support for ice-lite, this code needs to be adjusted. + # This has to be called before gathering candidates. if state.ice_transport.get_role(state.ice_pid) == nil do set_ice_role(state, :local, type, ice_lite) end @@ -1798,8 +1796,6 @@ defmodule ExWebRTC.PeerConnection do {:ok, dtls_role} <- SDPUtils.get_dtls_role(sdp) do config = Configuration.update(state.config, sdp) - # See: https://www.w3.org/TR/webrtc/#ref-for-dfn-icerole-1 - # Note: If we add support for ice-lite, this code needs to be adjusted. if state.ice_transport.get_role(state.ice_pid) == nil do set_ice_role(state, :remote, type, ice_lite) end @@ -1967,10 +1963,15 @@ defmodule ExWebRTC.PeerConnection do %{state | pending_remote_desc: {type, sdp}} end - defp set_ice_role(state, :local, :offer, _ice_lite) do + # See: https://www.w3.org/TR/webrtc/#ref-for-dfn-icerole-1 + defp set_ice_role(state, :local, :offer, false) do :ok = state.ice_transport.set_role(state.ice_pid, :controlling) end + defp set_ice_role(state, :local, :offer, true) do + :ok = state.ice_transport.set_role(state.ice_pid, :controlled) + end + defp set_ice_role(state, :remote, :offer, true) do :ok = state.ice_transport.set_role(state.ice_pid, :controlling) end diff --git a/lib/ex_webrtc/peer_connection/configuration.ex b/lib/ex_webrtc/peer_connection/configuration.ex index 02619cee..5c8ca0ec 100644 --- a/lib/ex_webrtc/peer_connection/configuration.ex +++ b/lib/ex_webrtc/peer_connection/configuration.ex @@ -199,7 +199,6 @@ defmodule ExWebRTC.PeerConnection.Configuration do ice_servers: [], ice_transport_policy: :all, ice_port_range: [0], - ice_role: :controlled, audio_codecs: @default_audio_codecs, video_codecs: @default_video_codecs, features: @default_features