Skip to content

Commit

Permalink
Ensure BUNDLE group (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
mickel8 authored Oct 24, 2023
1 parent fdcc06f commit ec1c9f7
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 16 deletions.
23 changes: 17 additions & 6 deletions lib/ex_webrtc/peer_connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ defmodule ExWebRTC.PeerConnection do

alias __MODULE__.Configuration
alias ExICE.ICEAgent
alias ExWebRTC.{IceCandidate, MediaStreamTrack, RTPTransceiver, SessionDescription}

import ExWebRTC.Utils
alias ExWebRTC.{
IceCandidate,
MediaStreamTrack,
RTPTransceiver,
SessionDescription,
SDPUtils,
Utils
}

@type peer_connection() :: GenServer.server()

Expand Down Expand Up @@ -121,7 +127,7 @@ defmodule ExWebRTC.PeerConnection do
ice_ufrag: ice_ufrag,
ice_pwd: ice_pwd,
ice_options: "trickle",
fingerprint: {:sha256, hex_dump(dtls_fingerprint)},
fingerprint: {:sha256, Utils.hex_dump(dtls_fingerprint)},
# TODO offer will always contain actpass
# and answer should contain active
# see RFC 8829 sec. 5.3.1
Expand Down Expand Up @@ -276,8 +282,11 @@ defmodule ExWebRTC.PeerConnection do
# TODO apply steps listed in RFC 8829 5.10
media = hd(sdp.media)

with {:ice_ufrag, ufrag} <- ExSDP.Media.get_attribute(media, :ice_ufrag),
{:ice_pwd, pwd} <- ExSDP.Media.get_attribute(media, :ice_pwd),
with :ok <- SDPUtils.ensure_mid(sdp),
:ok <- SDPUtils.ensure_bundle(sdp),
{:ice_ufrag, {:ice_ufrag, ufrag}} <-
{:ice_ufrag, ExSDP.Media.get_attribute(media, :ice_ufrag)},
{:ice_pwd, {:ice_pwd, pwd}} <- {:ice_pwd, ExSDP.Media.get_attribute(media, :ice_pwd)},
{:ok, new_transceivers} <- update_transceivers(state.transceivers, sdp) do
:ok = ICEAgent.set_remote_credentials(state.ice_agent, ufrag, pwd)
:ok = ICEAgent.gather_candidates(state.ice_agent)
Expand All @@ -297,7 +306,9 @@ defmodule ExWebRTC.PeerConnection do

{:ok, %{state | current_remote_desc: sdp, transceivers: new_transceivers}}
else
nil -> {:error, :missing_ice_ufrag_or_pwd}
{:ice_ufrag, nil} -> {:error, :missing_ice_ufrag}
{:ice_pwd, nil} -> {:error, :missing_ice_pwd}
other -> other
end
end

Expand Down
59 changes: 59 additions & 0 deletions lib/ex_webrtc/sdp_utils.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
defmodule ExWebRTC.SDPUtils do
@moduledoc false

@spec get_media_direction(ExSDP.Media.t()) ::
:sendrecv | :sendonly | :recvonly | :inactive | nil
def get_media_direction(media) do
Enum.find(media.attributes, fn attr ->
attr in [:sendrecv, :sendonly, :recvonly, :inactive]
end)
end

@spec ensure_mid(ExSDP.t()) :: :ok | {:error, :missing_mid | :duplicated_mid}
def ensure_mid(sdp) do
sdp.media
|> Enum.reduce_while({:ok, []}, fn media, {:ok, acc} ->
case ExSDP.Media.get_attributes(media, :mid) do
[{:mid, mid}] -> {:cont, {:ok, [mid | acc]}}
[] -> {:halt, {:error, :missing_mid}}
other when is_list(other) -> {:halt, {:error, :duplicated_mid}}
end
end)
|> case do
{:ok, mids} -> if Enum.uniq(mids) == mids, do: :ok, else: {:error, :duplicated_mid}
error -> error
end
end

@spec ensure_bundle(ExSDP.t()) ::
:ok
| {:error,
:non_exhaustive_bundle_group
| :missing_bundle_group
| :multiple_bundle_groups
| :invalid_bundle_group}
def ensure_bundle(sdp) do
groups = ExSDP.get_attributes(sdp, ExSDP.Attribute.Group)

mline_mids =
Enum.map(sdp.media, fn media ->
{:mid, mid} = ExSDP.Media.get_attribute(media, :mid)
mid
end)

case groups do
[%ExSDP.Attribute.Group{semantics: "BUNDLE", mids: group_mids}] ->
case {mline_mids -- group_mids, group_mids -- mline_mids} do
{[], []} -> :ok
{_, []} -> {:error, :non_exhaustive_bundle_group}
_other -> {:error, :invalid_bundle_group}
end

[] ->
{:error, :missing_bundle_group}

other when is_list(other) ->
{:error, :multiple_bundle_groups}
end
end
end
6 changes: 0 additions & 6 deletions lib/ex_webrtc/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,4 @@ defmodule ExWebRTC.Utils do
|> :binary.bin_to_list()
|> Enum.map_join(":", &Base.encode16(<<&1>>))
end

def get_media_direction(media) do
Enum.find(media.attributes, fn attr ->
attr in [:sendrecv, :sendonly, :recvonly, :inactive]
end)
end
end
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ defmodule ExWebRTC.MixProject do

defp deps do
[
{:ex_sdp, "~> 0.11"},
# {:ex_sdp, "~> 0.11"},
{:ex_sdp, github: "membraneframework/ex_sdp", branch: "get-attr"},
{:ex_ice, "~> 0.1"},
{:ex_dtls, "~> 0.13"},

Expand Down
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"ex_doc": {:hex, :ex_doc, "0.30.6", "5f8b54854b240a2b55c9734c4b1d0dd7bdd41f71a095d42a70445c03cf05a281", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bd48f2ddacf4e482c727f9293d9498e0881597eae6ddc3d9562bd7923375109f"},
"ex_dtls": {:hex, :ex_dtls, "0.13.0", "4d7631eefc19a8820d4f79883f379ff2ad642976bda55493d4ec4e5d10d6c078", [:mix], [{:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "3ece30967006ec12a4088e60514cb08847814fba8b8a21aca3862e5d1fd4a6bc"},
"ex_ice": {:hex, :ex_ice, "0.1.0", "2653c884872d8769cf9fc655c74002a63ed6c21be1b3c2badfa42bdc74de2355", [:mix], [{:ex_stun, "~> 0.1.0", [hex: :ex_stun, repo: "hexpm", optional: false]}], "hexpm", "e2539a321f87f31997ba974d532d00511e5828f2f113b550b1ef6aa799dd2ffe"},
"ex_sdp": {:hex, :ex_sdp, "0.11.0", "19e3af1d70b945381752db3139dfc22a19da1e9394036721449b7fb8c49fe039", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:uuid, "~> 1.1", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm", "7a3fe42f4ec0c18de09b10464829c27482d81d9c50c21bdebdbcfe17d2046408"},
"ex_sdp": {:git, "https://github.com/membraneframework/ex_sdp.git", "2d8dc9e2c964ed2d883a21d88547e9c5aaf0df1a", [branch: "get-attr"]},
"ex_stun": {:hex, :ex_stun, "0.1.0", "252474bf4c8519fbf4bc0fbfc6a1b846a634b1478c65dbbfb4b6ab4e33c2a95a", [:mix], [], "hexpm", "629fc8be45b624a92522f81d85ba001877b1f0745889a2419bdb678790d7480c"},
"excoveralls": {:hex, :excoveralls, "0.17.1", "83fa7906ef23aa7fc8ad7ee469c357a63b1b3d55dd701ff5b9ce1f72442b2874", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "95bc6fda953e84c60f14da4a198880336205464e75383ec0f570180567985ae0"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
Expand Down
45 changes: 44 additions & 1 deletion test/peer_connection_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ defmodule ExWebRTC.PeerConnectionTest do
a=ssrc:184440407 msid:- 1259ea70-c6b7-445a-9c20-49cec7433ccb
"""

test "transceivers" do
test "track notification" do
{:ok, pc} = PeerConnection.start_link()

offer = %SessionDescription{type: :offer, sdp: @single_audio_offer}
Expand All @@ -95,4 +95,47 @@ defmodule ExWebRTC.PeerConnectionTest do
assert_receive {:ex_webrtc, ^pc, {:track, %MediaStreamTrack{mid: "1", kind: :video}}}
refute_receive {:ex_webrtc, ^pc, {:track, %MediaStreamTrack{}}}
end

test "set_remote_description/2" do
{:ok, pc} = PeerConnection.start_link()

raw_sdp = ExSDP.new()

audio_mline =
ExSDP.Media.new("audio", 9, "UDP/TLS/RTP/SAVPF", [108])
|> ExSDP.Media.add_attributes(mid: "0", ice_ufrag: "someufrag", ice_pwd: "somepwd")

video_mline =
ExSDP.Media.new("video", 9, "UDP/TLS/RTP/SAVPF", [96])
|> ExSDP.Media.add_attributes(mid: "1", ice_ufrag: "someufrag", ice_pwd: "somepwd")

sdp = ExSDP.add_media(raw_sdp, audio_mline) |> to_string()
offer = %SessionDescription{type: :offer, sdp: sdp}
assert {:error, :missing_bundle_group} = PeerConnection.set_remote_description(pc, offer)

mline = ExSDP.Media.add_attribute(audio_mline, {:mid, "1"})
sdp = ExSDP.add_media(raw_sdp, mline) |> to_string()
offer = %SessionDescription{type: :offer, sdp: sdp}
assert {:error, :duplicated_mid} = PeerConnection.set_remote_description(pc, offer)

mline = ExSDP.Media.new("audio", 9, "UDP/TLS/RTP/SAVPF", [96])
sdp = ExSDP.add_media(raw_sdp, mline) |> to_string()
offer = %SessionDescription{type: :offer, sdp: sdp}
assert {:error, :missing_mid} = PeerConnection.set_remote_description(pc, offer)

sdp =
raw_sdp
|> ExSDP.add_attribute(%ExSDP.Attribute.Group{semantics: "BUNDLE", mids: [0]})
|> ExSDP.add_media(audio_mline)

offer = %SessionDescription{type: :offer, sdp: to_string(sdp)}
assert :ok == PeerConnection.set_remote_description(pc, offer)

sdp = ExSDP.add_media(sdp, video_mline) |> to_string()

offer = %SessionDescription{type: :offer, sdp: sdp}

assert {:error, :non_exhaustive_bundle_group} ==
PeerConnection.set_remote_description(pc, offer)
end
end
2 changes: 1 addition & 1 deletion test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ExUnit.start()
ExUnit.start(capture_log: true)

0 comments on commit ec1c9f7

Please sign in to comment.