Skip to content

Commit 15eee61

Browse files
authored
Fix set_remote_description failing on description with rejected m-lines (#145)
1 parent af1fec4 commit 15eee61

File tree

3 files changed

+90
-22
lines changed

3 files changed

+90
-22
lines changed

lib/ex_webrtc/peer_connection.ex

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,10 +1398,8 @@ defmodule ExWebRTC.PeerConnection do
13981398
defp apply_remote_description(%SessionDescription{type: type, sdp: raw_sdp}, state) do
13991399
with {:ok, next_sig_state} <- next_signaling_state(state.signaling_state, :remote, type),
14001400
{:ok, sdp} <- parse_sdp(raw_sdp),
1401-
:ok <- SDPUtils.ensure_mid(sdp),
1402-
:ok <- SDPUtils.ensure_bundle(sdp),
1403-
:ok <- SDPUtils.ensure_rtcp_mux(sdp),
1404-
{:ok, {ice_ufrag, ice_pwd}} <- SDPUtils.get_ice_credentials(sdp),
1401+
:ok <- SDPUtils.ensure_valid(sdp),
1402+
{:ok, ice_creds} <- SDPUtils.get_ice_credentials(sdp),
14051403
{:ok, {:fingerprint, {:sha256, peer_fingerprint}}} <- SDPUtils.get_cert_fingerprint(sdp),
14061404
{:ok, dtls_role} <- SDPUtils.get_dtls_role(sdp) do
14071405
config = Configuration.update(state.config, sdp)
@@ -1420,11 +1418,19 @@ defmodule ExWebRTC.PeerConnection do
14201418
dtls_role = if dtls_role in [:actpass, :passive], do: :active, else: :passive
14211419
DTLSTransport.start_dtls(state.dtls_transport, dtls_role, peer_fingerprint)
14221420

1421+
# ice_creds will be nil if all of the mlines in the description are rejected
1422+
# in such case, if this is the first remote description, connection won't be established
14231423
# TODO: this can result in ICE restart (when it should, e.g. when this is answer)
1424-
:ok = state.ice_transport.set_remote_credentials(state.ice_pid, ice_ufrag, ice_pwd)
1424+
case ice_creds do
1425+
nil ->
1426+
:ok
1427+
1428+
{ice_ufrag, ice_pwd} ->
1429+
:ok = state.ice_transport.set_remote_credentials(state.ice_pid, ice_ufrag, ice_pwd)
14251430

1426-
for candidate <- SDPUtils.get_ice_candidates(sdp) do
1427-
state.ice_transport.add_remote_candidate(state.ice_pid, candidate)
1431+
for candidate <- SDPUtils.get_ice_candidates(sdp) do
1432+
state.ice_transport.add_remote_candidate(state.ice_pid, candidate)
1433+
end
14281434
end
14291435

14301436
state =

lib/ex_webrtc/sdp_utils.ex

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,24 @@ defmodule ExWebRTC.SDPUtils do
88

99
@type extension() :: {Extension.SourceDescription, atom()}
1010

11-
@spec ensure_mid(ExSDP.t()) :: :ok | {:error, :missing_mid | :duplicated_mid}
12-
def ensure_mid(sdp) do
11+
@spec ensure_valid(ExSDP.t()) :: :ok | {:error, term()}
12+
def ensure_valid(sdp) do
13+
with :ok <- ensure_non_empty(sdp),
14+
:ok <- ensure_mid(sdp),
15+
:ok <- ensure_bundle(sdp) do
16+
ensure_rtcp_mux(sdp)
17+
end
18+
end
19+
20+
defp ensure_non_empty(sdp) do
21+
if Enum.empty?(sdp.media) do
22+
{:error, :empty_sdp}
23+
else
24+
:ok
25+
end
26+
end
27+
28+
defp ensure_mid(sdp) do
1329
sdp.media
1430
|> Enum.reduce_while({:ok, []}, fn media, {:ok, acc} ->
1531
case ExSDP.get_attributes(media, :mid) do
@@ -24,14 +40,7 @@ defmodule ExWebRTC.SDPUtils do
2440
end
2541
end
2642

27-
@spec ensure_bundle(ExSDP.t()) ::
28-
:ok
29-
| {:error,
30-
:non_exhaustive_bundle_group
31-
| :missing_bundle_group
32-
| :multiple_bundle_groups
33-
| :invalid_bundle_group}
34-
def ensure_bundle(sdp) do
43+
defp ensure_bundle(sdp) do
3544
groups = ExSDP.get_attributes(sdp, ExSDP.Attribute.Group)
3645

3746
mline_mids = get_bundle_mids(sdp.media)
@@ -56,8 +65,7 @@ defmodule ExWebRTC.SDPUtils do
5665
Enum.filter(groups, fn %ExSDP.Attribute.Group{semantics: name} -> name == to_filter end)
5766
end
5867

59-
@spec ensure_rtcp_mux(ExSDP.t()) :: :ok | {:error, :missing_rtcp_mux}
60-
def ensure_rtcp_mux(sdp) do
68+
defp ensure_rtcp_mux(sdp) do
6169
sdp.media
6270
|> Enum.all?(&(ExSDP.get_attribute(&1, :rtcp_mux) == :rtcp_mux))
6371
|> case do
@@ -116,20 +124,24 @@ defmodule ExWebRTC.SDPUtils do
116124
end
117125

118126
@spec get_ice_credentials(ExSDP.t()) ::
119-
{:ok, {binary(), binary()}}
127+
{:ok, {binary(), binary()} | nil}
120128
| {:error,
121129
:missing_ice_pwd
122130
| :missing_ice_ufrag
123131
| :missing_ice_credentials
124132
| :conflicting_ice_credentials}
125133
def get_ice_credentials(sdp) do
126134
session_creds = do_get_ice_credentials(sdp)
127-
mline_creds = Enum.map(sdp.media, fn mline -> do_get_ice_credentials(mline) end)
135+
136+
mline_creds =
137+
sdp.media
138+
|> Enum.reject(&rejected?/1)
139+
|> Enum.map(&do_get_ice_credentials/1)
128140

129141
case {session_creds, mline_creds} do
130142
# no session creds and no mlines (empty SDP)
131143
{{nil, nil}, []} ->
132-
{:error, :missing_ice_credentials}
144+
{:ok, nil}
133145

134146
# session creds but no mlines (empty SDP)
135147
{session_creds, []} ->

test/ex_webrtc/peer_connection_test.exs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,56 @@ defmodule ExWebRTC.PeerConnectionTest do
467467
assert expected_result == PeerConnection.set_remote_description(pc, offer)
468468
end)
469469
end
470+
471+
test "mline rejected" do
472+
{:ok, pc} = PeerConnection.start_link()
473+
474+
{:ok, _sender} = PeerConnection.add_track(pc, MediaStreamTrack.new(:video))
475+
{:ok, _sender} = PeerConnection.add_track(pc, MediaStreamTrack.new(:audio))
476+
{:ok, offer} = PeerConnection.create_offer(pc)
477+
:ok = PeerConnection.set_local_description(pc, offer)
478+
479+
# first m-line is rejected = ice ufrag and passwd are random (thats what Chromium seems to do)
480+
sdp =
481+
"""
482+
v=0
483+
o=- 4455712322662451611 2 IN IP4 127.0.0.1
484+
s=-
485+
t=0 0
486+
a=group:BUNDLE 1
487+
a=extmap-allow-mixed
488+
a=msid-semantic: WMS
489+
m=video 0 UDP/TLS/RTP/SAVPF 0
490+
c=IN IP4 0.0.0.0
491+
a=rtcp:9 IN IP4 0.0.0.0
492+
a=ice-ufrag:oIRa
493+
a=ice-pwd:10rPa8NrMCm602mt1OVkUHJs
494+
a=ice-options:trickle
495+
a=fingerprint:sha-256 C8:06:34:28:7C:5B:55:00:42:61:54:D2:84:29:B5:07:3D:9A:6C:5C:C6:79:B1:B0:A8:12:30:AD:26:36:93:45
496+
a=setup:active
497+
a=mid:0
498+
a=sendonly
499+
a=rtcp-mux
500+
m=audio 9 UDP/TLS/RTP/SAVPF 111
501+
c=IN IP4 0.0.0.0
502+
a=rtcp:9 IN IP4 0.0.0.0
503+
a=ice-ufrag:Anyh
504+
a=ice-pwd:cdaU0jbHlOTf98BNTRoZxMo1
505+
a=ice-options:trickle
506+
a=fingerprint:sha-256 C8:06:34:28:7C:5B:55:00:42:61:54:D2:84:29:B5:07:3D:9A:6C:5C:C6:79:B1:B0:A8:12:30:AD:26:36:93:45
507+
a=setup:active
508+
a=mid:1
509+
a=recvonly
510+
a=rtcp-mux
511+
a=rtpmap:111 opus/48000/2
512+
a=rtcp-fb:111 transport-cc
513+
a=fmtp:111 minptime=10;useinbandfec=1
514+
"""
515+
516+
answer = %SessionDescription{type: :answer, sdp: sdp}
517+
518+
assert :ok = PeerConnection.set_remote_description(pc, answer)
519+
end
470520
end
471521

472522
describe "add_transceiver/3" do

0 commit comments

Comments
 (0)