Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
mickel8 committed Nov 7, 2023
1 parent e3f734e commit 24b8944
Show file tree
Hide file tree
Showing 6 changed files with 433 additions and 144 deletions.
51 changes: 21 additions & 30 deletions lib/ex_webrtc/peer_connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,10 @@ defmodule ExWebRTC.PeerConnection do
GenServer.call(peer_connection, :get_transceivers)
end

@spec add_transceiver(
peer_connection(),
RTPTransceiver.kind() | MediaStreamTrack.t(),
transceiver_options()
) :: {:ok, RTPTransceiver.t()} | {:error, :TODO}
def add_transceiver(peer_connection, track_or_kind, options \\ []) do
GenServer.call(peer_connection, {:add_transceiver, track_or_kind, options})
@spec add_transceiver(peer_connection(), RTPTransceiver.kind(), transceiver_options()) ::
{:ok, RTPTransceiver.t()} | {:error, :TODO}
def add_transceiver(peer_connection, kind, options \\ []) do
GenServer.call(peer_connection, {:add_transceiver, kind, options})
end

#### CALLBACKS ####
Expand Down Expand Up @@ -145,7 +142,7 @@ defmodule ExWebRTC.PeerConnection do
# we support trickle ICE only
|> ExSDP.add_attribute({:ice_options, "trickle"})

config =
opts =
[
ice_ufrag: ice_ufrag,
ice_pwd: ice_pwd,
Expand All @@ -155,10 +152,7 @@ defmodule ExWebRTC.PeerConnection do
rtcp: true
]

mlines =
Enum.map(transceivers, fn transceiver ->
SDPUtils.to_offer_mline(transceiver, config)
end)
mlines = Enum.map(transceivers, &RTPTransceiver.to_mline(&1, opts))

mids =
Enum.map(mlines, fn mline ->
Expand Down Expand Up @@ -200,7 +194,7 @@ defmodule ExWebRTC.PeerConnection do
# we only support trickle ICE, so non-trickle offers should be rejected earlier
|> ExSDP.add_attribute({:ice_options, "trickle"})

config =
opts =
[
ice_ufrag: ice_ufrag,
ice_pwd: ice_pwd,
Expand All @@ -214,7 +208,7 @@ defmodule ExWebRTC.PeerConnection do
Enum.map(remote_offer.media, fn mline ->
{:mid, mid} = ExSDP.Media.get_attribute(mline, :mid)
{_ix, transceiver} = RTPTransceiver.find_by_mid(state.transceivers, mid)
SDPUtils.get_answer_mline(mline, transceiver, config)
RTPTransceiver.to_mline(transceiver, opts)
end)

mids =
Expand Down Expand Up @@ -299,21 +293,17 @@ defmodule ExWebRTC.PeerConnection do
end

@impl true
def handle_call({:add_transceiver, :audio, options}, _from, state) do
# TODO: proper implementation, change the :audio above to track_or_kind
def handle_call({:add_transceiver, kind, options}, _from, state)
when kind in [:audio, :video] do
direction = Keyword.get(options, :direction, :sendrcv)

# hardcoded audio codec
codecs = [
%ExWebRTC.RTPCodecParameters{
payload_type: 111,
mime_type: "audio/opus",
clock_rate: 48_000,
channels: 2
}
]

transceiver = %RTPTransceiver{mid: nil, direction: direction, kind: :audio, codecs: codecs}
codecs =
case kind do
:audio -> state.config.audio_codecs
:video -> state.config.video_codecs
end

transceiver = %RTPTransceiver{mid: nil, direction: direction, kind: kind, codecs: codecs}
transceivers = List.insert_at(state.transceivers, -1, transceiver)
{:reply, {:ok, transceiver}, %{state | transceivers: transceivers}}
end
Expand Down Expand Up @@ -386,7 +376,8 @@ defmodule ExWebRTC.PeerConnection do
with :ok <- SDPUtils.ensure_mid(sdp),
:ok <- SDPUtils.ensure_bundle(sdp),
{:ok, {ice_ufrag, ice_pwd}} <- SDPUtils.get_ice_credentials(sdp),
{:ok, new_transceivers} <- update_remote_transceivers(state.transceivers, sdp) do
{:ok, new_transceivers} <-
update_remote_transceivers(state.transceivers, sdp, state.config) do
:ok = ICEAgent.set_remote_credentials(state.ice_agent, ice_ufrag, ice_pwd)
:ok = ICEAgent.gather_candidates(state.ice_agent)

Expand Down Expand Up @@ -424,11 +415,11 @@ defmodule ExWebRTC.PeerConnection do
end
end

defp update_remote_transceivers(transceivers, sdp) do
defp update_remote_transceivers(transceivers, sdp, config) do
Enum.reduce_while(sdp.media, {:ok, transceivers}, fn mline, {:ok, transceivers} ->
case ExSDP.Media.get_attribute(mline, :mid) do
{:mid, mid} ->
transceivers = RTPTransceiver.update_or_create(transceivers, mid, mline)
transceivers = RTPTransceiver.update_or_create(transceivers, mid, mline, config)
{:cont, {:ok, transceivers}}

_other ->
Expand Down
166 changes: 156 additions & 10 deletions lib/ex_webrtc/peer_connection/configuration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,55 @@ defmodule ExWebRTC.PeerConnection.Configuration do
PeerConnection configuration
"""

alias ExWebRTC.RTPCodecParameters

@default_audio_codecs [
%RTPCodecParameters{
payload_type: 111,
mime_type: "audio/opus",
clock_rate: 48_000,
channels: 2,
sdp_fmtp_line: %ExSDP.Attribute.FMTP{pt: 111, minptime: 10, useinbandfec: true},
rtcp_fbs: []
}
]

@default_video_codecs [
%RTPCodecParameters{
payload_type: 98,
mime_type: "video/H264",
clock_rate: 90_000,
channels: nil,
sdp_fmtp_line: %ExSDP.Attribute.FMTP{
pt: 98,
level_asymmetry_allowed: true,
packetization_mode: 1,
profile_level_id: 0x42001F
},
rtcp_fbs: []
},
%RTPCodecParameters{
payload_type: 96,
mime_type: "video/VP8",
clock_rate: 90_000,
channels: nil,
sdp_fmtp_line: nil,
rtcp_fbs: []
}
]

@rtp_hdr_extensions %{
:mid => "urn:ietf:params:rtp-hdrext:sdes:mid",
:audio_level => "urn:ietf:params:rtp-hdrext:ssrc-audio-level"
}

@mandatory_rtp_hdr_exts [:mid]

@typedoc """
Supported RTP header extensions.
"""
@type rtp_hdr_extension() :: :audio_level

@type ice_server() :: %{
optional(:credential) => String.t(),
optional(:username) => String.t(),
Expand All @@ -12,32 +61,129 @@ defmodule ExWebRTC.PeerConnection.Configuration do
@typedoc """
Options that can be passed to `ExWebRTC.PeerConnection.start_link/1`.
Currently, ExWebRTC always uses the following config:
* `ice_servers` - list of STUN servers to use.
TURN servers are not supported right now and will be filtered out.
* `audio_codecs` - list of audio codecs to use.
Use `default_audio_codecs/0` to get a list of default audio codecs.
This option overrides default audio codecs.
If you wish to add codecs to default ones do
`audio_codecs: Configuration.default_audio_codecs() ++ my_codecs`
* `video_codecs` - the same as `audio_codecs` but for video.
* `rtp_hdr_extensions` - list of RTP header extensions to use.
MID extension is enabled by default and cannot be turned off.
Besides options listed above, ExWebRTC uses the following config:
* bundle_policy - max_bundle
* ice_candidate_pool_size - 0
* ice_transport_policy - all
* rtcp_mux_policy - require
This config cannot be changed.
"""
@type options() :: [ice_servers: [ice_server()]]
@type options() :: [
ice_servers: [ice_server()],
audio_codecs: [RTPCodecParameters.t()],
video_codecs: [RTPCodecParameters.t()],
rtp_hdr_extensions: [rtp_hdr_extension()]
]

@typedoc false
@type t() :: %__MODULE__{ice_servers: [ice_server()]}
@type t() :: %__MODULE__{
ice_servers: [ice_server()],
audio_codecs: [RTPCodecParameters.t()],
video_codecs: [RTPCodecParameters.t()],
rtp_hdr_extensions: [rtp_hdr_extension()]
}

defstruct ice_servers: [],
audio_codecs: @default_audio_codecs,
video_codecs: @default_video_codecs,
rtp_hdr_extensions: @mandatory_rtp_hdr_exts

@doc """
Returns a list of default audio codecs.
"""
@spec default_audio_codecs() :: [RTPCodecParameters.t()]
def default_audio_codecs(), do: @default_audio_codecs

defstruct ice_servers: []
@doc """
Returns a list of default video codecs.
"""
@spec default_video_codecs() :: [RTPCodecParameters.t()]
def default_video_codecs(), do: @default_video_codecs

@doc false
@spec from_options!(options()) :: t()
def from_options!(options) do
config = struct!(__MODULE__, options)
options =
options
|> add_mandatory_rtp_hdr_extensions()
|> resolve_rtp_hdr_extensions()
# ATM, ExICE does not support relay via TURN
|> reject_turn_servers()

struct!(__MODULE__, options)
end

# ATM, ExICE does not support relay via TURN
stun_servers =
config.ice_servers
@doc false
@spec is_supported_codec(t(), RTPCodecParameters.t()) :: boolean()
def is_supported_codec(config, codec) do
# This function doesn't check if rtcp-fb is supported.
# Instead, `is_supported_rtcp_fb` has to be used to filter out
# rtcp-fb that are not supported.
Enum.find(
config.audio_codecs ++ config.video_codecs,
fn supported_codec ->
# for the time of comparision, override payload type in our codec
supported_codec =
if supported_codec.sdp_fmtp_line != nil and codec.sdp_fmtp_line != nil do
%RTPCodecParameters{
supported_codec
| sdp_fmtp_line: %ExSDP.Attribute.FMTP{
supported_codec.sdp_fmtp_line
| pt: codec.sdp_fmtp_line.pt
}
}
else
supported_codec
end

supported_codec.mime_type == codec.mime_type and
supported_codec.clock_rate == codec.clock_rate and
supported_codec.channels == codec.channels and
supported_codec.sdp_fmtp_line == codec.sdp_fmtp_line
end
)
end

@doc false
@spec is_supported_rtp_hdr_extension(t(), ExSDP.Attribute.Extmap.t()) :: boolean()
def is_supported_rtp_hdr_extension(config, rtp_hdr_extension) do
rtp_hdr_extension.uri in config.rtp_hdr_extensions
end

@doc false
@spec is_supported_rtcp_fb(t(), ExSDP.Attribute.RTCPFeedback.t()) :: boolean()
def is_supported_rtcp_fb(_config, _rtcp_fb), do: false

defp add_mandatory_rtp_hdr_extensions(options) do
Keyword.update(options, :rtp_hdr_extensions, @mandatory_rtp_hdr_exts, fn exts ->
exts ++ @mandatory_rtp_hdr_exts
end)
end

defp resolve_rtp_hdr_extensions(options) do
rtp_hdr_extensions =
Enum.map(options[:rtp_hdr_extensions], fn ext -> Map.fetch!(@rtp_hdr_extensions, ext) end)

Keyword.put(options, :rtp_hdr_extensions, rtp_hdr_extensions)
end

defp reject_turn_servers(options) do
Keyword.update(options, :ice_servers, [], fn ice_servers ->
ice_servers
|> Enum.flat_map(&List.wrap(&1.urls))
|> Enum.filter(&String.starts_with?(&1, "stun:"))

%__MODULE__{config | ice_servers: stun_servers}
end)
end
end
9 changes: 9 additions & 0 deletions lib/ex_webrtc/rtp_codec_parameters.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ defmodule ExWebRTC.RTPCodecParameters do
RTPCodecParameters
"""

@type t() :: %__MODULE__{
payload_type: non_neg_integer(),
mime_type: binary(),
clock_rate: non_neg_integer(),
channels: non_neg_integer() | nil,
sdp_fmtp_line: ExSDP.Attribute.FMTP.t() | nil,
rtcp_fbs: [ExSDP.Attribute.RTCPFeedback.t()]
}

defstruct [:payload_type, :mime_type, :clock_rate, :channels, :sdp_fmtp_line, :rtcp_fbs]

def new(type, rtp_mapping, fmtp, rtcp_fbs) do
Expand Down
Loading

0 comments on commit 24b8944

Please sign in to comment.