Skip to content

Commit

Permalink
Add PeerConneciton.set_packet_loss
Browse files Browse the repository at this point in the history
  • Loading branch information
mickel8 committed Jan 6, 2025
1 parent 2ef1582 commit 01868f5
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 8 deletions.
29 changes: 26 additions & 3 deletions lib/ex_webrtc/dtls_transport.ex
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ defmodule ExWebRTC.DTLSTransport do
GenServer.cast(dtls_transport, {:send_data, data})
end

@spec set_packet_loss(dtls_transport(), 0..100) :: :ok
def set_packet_loss(dtls_transport, packet_loss) do
GenServer.cast(dtls_transport, {:set_packet_loss, packet_loss})
end

@spec stop(dtls_transport()) :: :ok
def stop(dtls_transport) do
GenServer.stop(dtls_transport)
Expand Down Expand Up @@ -127,7 +132,8 @@ defmodule ExWebRTC.DTLSTransport do
peer_fingerprint: nil,
dtls_state: :new,
dtls: nil,
mode: nil
mode: nil,
packet_loss: 0
}

notify(state.owner, {:state_change, :new})
Expand Down Expand Up @@ -236,8 +242,19 @@ defmodule ExWebRTC.DTLSTransport do
@impl true
def handle_cast({:send_rtp, data}, %{dtls_state: :connected, ice_connected: true} = state) do
case ExLibSRTP.protect(state.out_srtp, data) do
{:ok, protected} -> state.ice_transport.send_data(state.ice_pid, protected)
{:error, reason} -> Logger.warning("Unable to protect RTP: #{inspect(reason)}")
{:ok, protected} ->
case state.packet_loss do
0 ->
state.ice_transport.send_data(state.ice_pid, protected)

_ ->
if Enum.random(1..100) > state.packet_loss do

Check warning on line 251 in lib/ex_webrtc/dtls_transport.ex

View workflow job for this annotation

GitHub Actions / CI on OTP 27 / Elixir 1.17

Function body is nested too deep (max depth is 2, was 3).
state.ice_transport.send_data(state.ice_pid, protected)
end
end

{:error, reason} ->
Logger.warning("Unable to protect RTP: #{inspect(reason)}")
end

{:noreply, state}
Expand Down Expand Up @@ -269,6 +286,12 @@ defmodule ExWebRTC.DTLSTransport do
{:noreply, state}
end

@impl true
def handle_cast({:set_packet_loss, value}, state) do
state = %{state | packet_loss: value}
{:noreply, state}
end

@impl true
def handle_info(:dtls_timeout, %{buffered_local_packets: buffered_local_packets} = state) do
case ExDTLS.handle_timeout(state.dtls) do
Expand Down
16 changes: 16 additions & 0 deletions lib/ex_webrtc/peer_connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@ defmodule ExWebRTC.PeerConnection do
GenServer.cast(peer_connection, {:send_data, channel_ref, data})
end

@doc """
Sets very simple packet loss.
Can be used for experimental purposes.
"""
@spec set_packet_loss(peer_connection(), 0..100) :: :ok
def set_packet_loss(peer_connection, value) when value in 0..100 do
GenServer.cast(peer_connection, {:set_packet_loss, value})
end

#### MDN-API ####

@doc """
Expand Down Expand Up @@ -1162,6 +1172,12 @@ defmodule ExWebRTC.PeerConnection do
{:noreply, %{state | sctp_transport: sctp_transport}}
end

@impl true
def handle_cast({:set_packet_loss, packet_loss}, state) do
DTLSTransport.set_packet_loss(state.dtls_transport, packet_loss)
{:noreply, state}
end

@impl true
def handle_info({:ex_ice, _from, {:connection_state_change, new_ice_state}}, state) do
state = %{state | ice_state: new_ice_state}
Expand Down
46 changes: 41 additions & 5 deletions test/ex_webrtc/dtls_transport_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ defmodule ExWebRTC.DTLSTransportTest do
|> ExDTLS.get_cert_fingerprint()
|> Utils.hex_dump()

@rtp_header <<1::1, 0::1, 0::1, 0::1, 0::4, 0::1, 96::7, 1::16, 1::32, 1::32>>
@rtp_payload <<0>>
@rtp_packet <<@rtp_header::binary, @rtp_payload::binary>>

defmodule MockICETransport do
@behaviour ExWebRTC.ICETransport

Expand Down Expand Up @@ -175,6 +179,10 @@ defmodule ExWebRTC.DTLSTransportTest do
assert :ok = check_handshake(dtls, ice_transport, ice_pid, remote_dtls)
assert_receive {:dtls_transport, ^dtls, {:state_change, :connecting}}
assert_receive {:dtls_transport, ^dtls, {:state_change, :connected}}

# assert we can send rtp packets
assert :ok = DTLSTransport.send_rtp(dtls, @rtp_packet)
assert_receive {:mock_ice, <<@rtp_header::binary, _payload::binary>>}
end

test "finishes handshake in passive mode", %{
Expand All @@ -200,6 +208,39 @@ defmodule ExWebRTC.DTLSTransportTest do
assert :ok == check_handshake(dtls, ice_transport, ice_pid, remote_dtls)
assert_receive {:dtls_transport, ^dtls, {:state_change, :connecting}}
assert_receive {:dtls_transport, ^dtls, {:state_change, :connected}}

# assert we can send rtp packets
assert :ok = DTLSTransport.send_rtp(dtls, @rtp_packet)
assert_receive {:mock_ice, <<@rtp_header::binary, _payload::binary>>}
end

test "drops packets when packet loss is set", %{
dtls: dtls,
ice_transport: ice_transport,
ice_pid: ice_pid
} do
:ok = DTLSTransport.start_dtls(dtls, :active, @fingerprint)
remote_dtls = ExDTLS.init(mode: :server, dtls_srtp: true)

:ok = DTLSTransport.set_ice_connected(dtls)

assert :ok = check_handshake(dtls, ice_transport, ice_pid, remote_dtls)
assert_receive {:dtls_transport, ^dtls, {:state_change, :connecting}}
assert_receive {:dtls_transport, ^dtls, {:state_change, :connected}}

# assert we can send data
DTLSTransport.send_rtp(dtls, @rtp_packet)
assert_receive {:mock_ice, <<@rtp_header::binary, _payload::binary>>}

# now set packet-loss
DTLSTransport.set_packet_loss(dtls, 100)
DTLSTransport.send_rtp(dtls, @rtp_packet)
refute_receive {:mock_ice, _rtp_packet}
end

test "stop/1", %{dtls: dtls} do
assert :ok == DTLSTransport.stop(dtls)
assert false == Process.alive?(dtls)
end

defp check_handshake(dtls, ice_transport, ice_pid, remote_dtls) do
Expand All @@ -218,9 +259,4 @@ defmodule ExWebRTC.DTLSTransportTest do
:ok
end
end

test "stop/1", %{dtls: dtls} do
assert :ok == DTLSTransport.stop(dtls)
assert false == Process.alive?(dtls)
end
end

0 comments on commit 01868f5

Please sign in to comment.