Skip to content

Commit

Permalink
Add get_stats
Browse files Browse the repository at this point in the history
  • Loading branch information
mickel8 committed Jan 22, 2024
1 parent 7f85ac4 commit 420b9e7
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 4 deletions.
9 changes: 9 additions & 0 deletions lib/ex_webrtc/app.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule ExWebRTC.App do

Check warning on line 1 in lib/ex_webrtc/app.ex

View workflow job for this annotation

GitHub Actions / Lint (OTP 26 / Elixir 1.15)

Modules should have a @moduledoc tag.
use Application

def start(_type, _args) do
IO.inspect(:loading_ex_webrtc_app)

Check warning on line 5 in lib/ex_webrtc/app.ex

View workflow job for this annotation

GitHub Actions / Lint (OTP 26 / Elixir 1.15)

There should be no calls to IO.inspect/1.
children = [{Registry, keys: :unique, name: ExWebRTC.Registry}]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
15 changes: 15 additions & 0 deletions lib/ex_webrtc/dtls_transport.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ defmodule ExWebRTC.DTLSTransport do
GenServer.call(dtls_transport, :set_ice_connected)
end

@spec get_local_cert_info(dtls_transport()) :: map()
def get_local_cert_info(dtls_transport) do
GenServer.call(dtls_transport, :get_local_cert_info)
end

@spec get_fingerprint(dtls_transport()) :: binary()
def get_fingerprint(dtls_transport) do
GenServer.call(dtls_transport, :get_fingerprint)
Expand Down Expand Up @@ -87,6 +92,7 @@ defmodule ExWebRTC.DTLSTransport do
ice_connected: false,
buffered_packets: nil,
cert: cert,
base64_cert: Base.encode64(cert),
pkey: pkey,
fingerprint: fingerprint,
in_srtp: ExLibSRTP.new(),
Expand Down Expand Up @@ -133,6 +139,15 @@ defmodule ExWebRTC.DTLSTransport do
end
end

@impl true
def handle_call(:get_local_cert_info, _from, state) do
%{
fingerprint: state.fingerprint,
fingerprint_algorithm: :sha_256,
base64_certificate: state.base64_cert
}
end

@impl true
def handle_call(:get_fingerprint, _from, state) do
{:reply, state.fingerprint, state}
Expand Down
3 changes: 3 additions & 0 deletions lib/ex_webrtc/ice_transport.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ defmodule ExWebRTC.ICETransport do
@callback restart(pid()) :: :ok
@callback send_data(pid(), binary()) :: :ok
@callback set_remote_credentials(pid(), ufrag :: binary(), pwd :: binary()) :: :ok
@callback get_stats(pid()) :: map()
@callback stop(pid()) :: :ok
end

Expand Down Expand Up @@ -43,5 +44,7 @@ defmodule ExWebRTC.DefaultICETransport do
@impl true
defdelegate set_remote_credentials(pid, ufrag, pwd), to: ICEAgent
@impl true
defdelegate get_stats(pid), to: ICEAgent

Check warning on line 47 in lib/ex_webrtc/ice_transport.ex

View workflow job for this annotation

GitHub Actions / Test (OTP 26 / Elixir 1.15)

ExICE.ICEAgent.get_stats/1 is undefined or private
@impl true
defdelegate stop(pid), to: ICEAgent
end
46 changes: 46 additions & 0 deletions lib/ex_webrtc/peer_connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ defmodule ExWebRTC.PeerConnection do
@type connection_state() :: :closed | :failed | :disconnected | :new | :connecting | :connected

#### API ####
@spec get_all_peer_connections() :: [pid()]
def get_all_peer_connections() do
Registry.select(ExWebRTC.Registry, [{{:_, :"$1", :_}, [], [:"$1"]}])
end

@spec start_link(Configuration.options()) :: GenServer.on_start()
def start_link(options \\ []) do
configuration = Configuration.from_options!(options)
Expand Down Expand Up @@ -157,6 +162,11 @@ defmodule ExWebRTC.PeerConnection do
GenServer.call(peer_connection, {:remove_track, sender_id})
end

@spec get_stats(peer_connection()) :: %{String.t() => term()}
def get_stats(peer_connection) do
GenServer.call(peer_connection, :get_stats)
end

@spec send_rtp(peer_connection(), String.t(), ExRTP.Packet.t()) :: :ok
def send_rtp(peer_connection, track_id, packet) do
GenServer.cast(peer_connection, {:send_rtp, track_id, packet})
Expand All @@ -171,6 +181,7 @@ defmodule ExWebRTC.PeerConnection do

@impl true
def init({owner, config}) do
{:ok, _} = Registry.register(ExWebRTC.Registry, self(), self())
ice_config = [stun_servers: config.ice_servers, ip_filter: config.ice_ip_filter, on_data: nil]
{:ok, ice_pid} = DefaultICETransport.start_link(:controlled, ice_config)
{:ok, dtls_transport} = DTLSTransport.start_link(DefaultICETransport, ice_pid)
Expand All @@ -182,6 +193,7 @@ defmodule ExWebRTC.PeerConnection do

state = %{
owner: owner,
stats_id: Utils.generate_id(),
config: config,
current_local_desc: nil,
pending_local_desc: nil,
Expand Down Expand Up @@ -558,6 +570,40 @@ defmodule ExWebRTC.PeerConnection do
end
end

@impl true
def handle_call(:get_stats, _from, state) do
timestamp = System.os_time(:millisecond)

cert_info = DTLSTransport.get_local_cert_info(state.dtls_transport)

stats =

Check warning on line 579 in lib/ex_webrtc/peer_connection.ex

View workflow job for this annotation

GitHub Actions / Test (OTP 26 / Elixir 1.15)

variable "stats" is unused (if the variable is not meant to be used, prefix it with an underscore)
Enum.flat_map(state.transceivers, fn tr ->
inbound_rtp_stats = RTPSender.get_stats(tr.sender, timestamp)
outbound_rtp_stats = RTPReceiver.get_stats(tr.receiver, timestamp)

Check warning on line 582 in lib/ex_webrtc/peer_connection.ex

View workflow job for this annotation

GitHub Actions / Test (OTP 26 / Elixir 1.15)

RTPReceiver.get_stats/2 is undefined (module RTPReceiver is not available or is yet to be defined)
[inbound_rtp_stats, outbound_rtp_stats]
end)

stats = %{
state.stats_id => %{
id: state.stats_id,
type: :peer_connection,
timestamp: timestamp,
datachannels_opened: 0,
datachannels_closed: 0
},
:certificate => %{
id: :certificate,
type: :certificate,
timestamp: timestamp,
fingerprint: cert_info.fingerprint,
fingerprint_algorithm: cert_info.fingerprint_algorithm,
base64_certificate: cert_info.base64_certificate
}
}

{:reply, stats, state}
end

@impl true
def handle_cast({:send_rtp, track_id, packet}, state) do
# TODO: iterating over transceivers is not optimal
Expand Down
42 changes: 38 additions & 4 deletions lib/ex_webrtc/rtp_sender.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,23 @@ defmodule ExWebRTC.RTPSender do
mid: String.t() | nil,
pt: non_neg_integer() | nil,
ssrc: non_neg_integer() | nil,
last_seq_num: non_neg_integer()
last_seq_num: non_neg_integer(),
packets_sent: non_neg_integer(),
bytes_sent: non_neg_integer()
}

@enforce_keys [:id, :last_seq_num]
defstruct @enforce_keys ++ [:track, :codec, :mid, :pt, :ssrc, rtp_hdr_exts: %{}]
defstruct @enforce_keys ++
[
:track,
:codec,
:mid,
:pt,
:ssrc,
rtp_hdr_exts: %{},
packets_sent: 0,
bytes_sent: 0
]

@doc false
@spec new(
Expand All @@ -47,7 +59,9 @@ defmodule ExWebRTC.RTPSender do
pt: pt,
ssrc: ssrc,
last_seq_num: random_seq_num(),
mid: mid
mid: mid,
packets_sent: 0,
bytes_sent: 0
}
end

Expand Down Expand Up @@ -82,9 +96,29 @@ defmodule ExWebRTC.RTPSender do
|> ExRTP.Packet.add_extension(mid_ext)
|> ExRTP.Packet.encode()

sender = %{sender | last_seq_num: next_seq_num}
sender = %{
sender
| last_seq_num: next_seq_num,
packets_sent: sender.packets_sent + 1,
bytes_sent: sender.bytes_sent + byte_size(packet)
}

{packet, sender}
end

@doc false
@spec get_stats(t(), non_neg_integer()) :: map()
def get_stats(sender, timestamp) do
%{
timestamp: timestamp,
type: :outbound_rtp,
id: sender.stats_id,
ssrc: sender.ssrc,
kind: sender.track.kind,
packets_sent: sender.packets_sent,
bytes_sent: sender.bytes_sent
}
end

defp random_seq_num(), do: Enum.random(0..65_535)
end
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ defmodule ExWebRTC.MixProject do

def application do
[
mod: {ExWebRTC.App, []},
extra_applications: [:logger]
]
end
Expand Down

0 comments on commit 420b9e7

Please sign in to comment.