Skip to content

Commit

Permalink
Add intersection... functions to Configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
LVala committed Jun 20, 2024
1 parent 6835193 commit 6e2850a
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 38 deletions.
83 changes: 51 additions & 32 deletions lib/ex_webrtc/peer_connection/configuration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ defmodule ExWebRTC.PeerConnection.Configuration do
}

@enforce_keys [
:controlling_process,
:ice_ip_filter,
:audio_extensions,
:video_extensions
Expand Down Expand Up @@ -486,41 +485,61 @@ defmodule ExWebRTC.PeerConnection.Configuration do
%RTPCodecParameters{codec | payload_type: new_pt, rtcp_fbs: new_fbs, sdp_fmtp_line: new_fmtp}
end

# @doc false
@spec supported_codec?(t(), RTPCodecParameters.t()) :: boolean()
def supported_codec?(config, codec) do
# This function doesn't check if rtcp-fb is supported.
# Instead, `supported_rtcp_fb?` has to be used to filter out
# rtcp-fb that are not supported.
# TODO: this function doesn't compare fmtp at all
Enum.any?(config.audio_codecs ++ config.video_codecs, fn supported_codec ->
# For the purposes of comparison, lowercase mime check
%{
supported_codec
| mime_type: String.downcase(supported_codec.mime_type),
rtcp_fbs: codec.rtcp_fbs,
sdp_fmtp_line: codec.sdp_fmtp_line
} == %{
codec
| mime_type: String.downcase(codec.mime_type)
}
@doc false
@spec intersect_codecs(t(), ExSDP.Media.t()) :: [RTPCodecParameters.t()]
def intersect_codecs(config, mline) do
# we assume that this function is called after
# the config was updated based on the remote SDP
# so the payload types should match
codecs =
case mline.type do
:audio -> config.audio_codecs
:video -> config.video_codecs
end

mline
|> SDPUtils.get_rtp_codec_parameters()
|> Enum.flat_map(fn sdp_codec ->
codecs
|> Enum.find(
# as of now, we ignore sdp_fmtp_line
&(&1.mime_type == sdp_codec.mime_type and
&1.payload_type == sdp_codec.payload_type and
&1.clock_rate == sdp_codec.clock_rate and
&1.channels == sdp_codec.channels)
)
|> case do
nil ->
[]

other ->
fbs = Enum.filter(sdp_codec.rtcp_fbs, fn fb -> fb in other.rtcp_fbs end)
[%RTPCodecParameters{sdp_codec | rtcp_fbs: fbs}]
end
end)
end

# @doc false
# @spec supported_rtp_hdr_extension?(t(), Extmap.t(), :audio | :video) ::
# boolean()
def supported_rtp_hdr_extension?(config, rtp_hdr_extension, media_type) do
supported_uris =
case media_type do
:audio -> Map.keys(config.audio_rtp_hdr_exts)
:video -> Map.keys(config.video_rtp_hdr_exts)
@doc false
@spec intersect_extensions(t(), ExSDP.Media.t()) :: [Extmap.t()]
def intersect_extensions(config, mline) do
extensions =
case mline.type do
:audio -> config.audio_extensions
:video -> config.video_extensions
end

rtp_hdr_extension.uri in supported_uris
mline
|> ExSDP.get_attributes(Extmap)
|> Enum.flat_map(fn sdp_extension ->
extensions
|> Enum.find(
&(&1.id == sdp_extension.id and
&1.uri == sdp_extension.uri)
)
|> case do
nil -> []
_other -> [%Extmap{sdp_extension | direction: nil}]
end
end)
end

# @doc false
# @spec supported_rtcp_fb?(t(), RTCPFeedback.t()) :: boolean()
def supported_rtcp_fb?(_config, _rtcp_fb), do: false
end
99 changes: 93 additions & 6 deletions test/ex_webrtc/peer_connection/configuration_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule ExWebRTC.PeerConnection.ConfigurationTest do
alias ExWebRTC.PeerConnection.Configuration
alias ExWebRTC.RTPCodecParameters

alias ExSDP.Attribute.{Extmap, FMTP}
alias ExSDP.Attribute.{Extmap, FMTP, RTCPFeedback}

@mid_uri "urn:ietf:params:rtp-hdrext:sdes:mid"
@twcc_uri "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
Expand Down Expand Up @@ -51,10 +51,8 @@ defmodule ExWebRTC.PeerConnection.ConfigurationTest do

describe "from_options!/1" do
test "with everything turned off" do
self = self()

options = [
controlling_process: self,
controlling_process: self(),
ice_servers: [],
ice_transport_policy: :all,
ice_ip_filter: fn _ -> true end,
Expand All @@ -68,7 +66,6 @@ defmodule ExWebRTC.PeerConnection.ConfigurationTest do
config = Configuration.from_options!(options)

assert %Configuration{
controlling_process: ^self,
ice_servers: [],
ice_transport_policy: :all,
audio_codecs: [],
Expand Down Expand Up @@ -149,7 +146,6 @@ defmodule ExWebRTC.PeerConnection.ConfigurationTest do
end)
end

@tag :wip
test "with defaults" do
config = Configuration.from_options!([])

Expand Down Expand Up @@ -345,4 +341,95 @@ defmodule ExWebRTC.PeerConnection.ConfigurationTest do
assert pt not in [100, 101, 96, 110]
end
end

test "intersect_codecs/2" do
og_config =
Configuration.from_options!(
audio_codecs: [%{@opus_codec | payload_type: 111}],
video_codecs: [%{@h264_codec | payload_type: 112}, %{@vp8_codec | payload_type: 113}],
feedbacks: [%{type: :all, feedback: :pli}],
features: [:inbound_rtx]
)

sdp =
"""
m=audio 9 UDP/TLS/RTP/SAVPF 0
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
m=video 9 UDP/TLS/RTP/SAVPF 1
a=rtpmap:112 H264/90000
a=rtcp-fb:112 transport-cc
a=rtcp-fb:112 nack pli
a=rtpmap:115 rtx/90000
a=fmtp:115 apt=112
a=rtpmap:113 VP8/90000
a=rtcp-fb:113 transport-cc
a=rtpmap:117 VP9/90000
a=rtpmap:119 rtx/90000
a=fmtp:119 apt=117
"""
|> ExSDP.parse!()

config = Configuration.update(og_config, sdp)

audio_mline = Enum.find(sdp.media, &(&1.type == :audio))

# opus should not contain any RTCP feedbacks (SDP contains TWCC, but the config does not)
assert [opus] = Configuration.intersect_codecs(config, audio_mline)
assert %RTPCodecParameters{mime_type: "audio/opus", payload_type: 111, rtcp_fbs: []} = opus

video_mline = Enum.find(sdp.media, &(&1.type == :video))

assert {[h264_rtx], [h264, vp8]} =
config
|> Configuration.intersect_codecs(video_mline)
|> Enum.split_with(&String.ends_with?(&1.mime_type, "/rtx"))

# h264 has PLI, but VP8 does not, none of the codecs has TWCC, there's no VP9 in the SDP at all
assert %RTPCodecParameters{mime_type: "video/VP8", payload_type: 113, rtcp_fbs: []} = vp8

assert %RTPCodecParameters{
mime_type: "video/H264",
payload_type: 112,
rtcp_fbs: [%RTCPFeedback{pt: 112, feedback_type: :pli}]
} = h264

assert %RTPCodecParameters{
mime_type: "video/rtx",
payload_type: 115,
rtcp_fbs: [],
sdp_fmtp_line: %FMTP{pt: 115, apt: 112}
} = h264_rtx
end

test "intersect_extensions/2" do
og_config =
Configuration.from_options!(
header_extensions: [%{type: :all, uri: @mid_uri}, %{type: :video, uri: @twcc_uri}],
features: []
)

sdp =
"""
m=audio 9 UDP/TLS/RTP/SAVPF 0
a=extmap:14 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=rtpmap:111 opus/48000/2
m=video 9 UDP/TLS/RTP/SAVPF 1
a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=rtpmap:112 H264/90000
"""
|> ExSDP.parse!()

config = Configuration.update(og_config, sdp)

audio_mline = Enum.find(sdp.media, &(&1.type == :audio))
assert [mid] = Configuration.intersect_extensions(config, audio_mline)
assert %Extmap{id: 14, uri: @mid_uri} = mid

video_mline = Enum.find(sdp.media, &(&1.type == :video))
assert [twcc] = Configuration.intersect_extensions(config, video_mline)
assert %Extmap{id: 5, uri: @twcc_uri} = twcc
end
end

0 comments on commit 6e2850a

Please sign in to comment.