Skip to content

Commit 65f6b88

Browse files
committed
Update config on receiving remote SDP
1 parent 792184e commit 65f6b88

File tree

5 files changed

+113
-22
lines changed

5 files changed

+113
-22
lines changed

lib/ex_webrtc/peer_connection.ex

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -436,8 +436,8 @@ defmodule ExWebRTC.PeerConnection do
436436

437437
{rtp_hdr_exts, codecs} =
438438
case kind do
439-
:audio -> {config.audio_rtp_hdr_exts, config.audio_codecs}
440-
:video -> {config.video_rtp_hdr_exts, config.video_codecs}
439+
:audio -> {Map.values(config.audio_rtp_hdr_exts), config.audio_codecs}
440+
:video -> {Map.values(config.video_rtp_hdr_exts), config.video_codecs}
441441
end
442442

443443
# When we create sendonly or sendrecv transceiver, we always only take one codec
@@ -504,6 +504,9 @@ defmodule ExWebRTC.PeerConnection do
504504
{:ok, {ice_ufrag, ice_pwd}} <- SDPUtils.get_ice_credentials(sdp),
505505
{:ok, {:fingerprint, {:sha256, peer_fingerprint}}} <- SDPUtils.get_cert_fingerprint(sdp),
506506
{:ok, dtls_role} <- SDPUtils.get_dtls_role(sdp) do
507+
config = Configuration.update(state.config, sdp)
508+
state = %{state | config: config}
509+
507510
transceivers =
508511
state
509512
|> update_transceivers(sdp)

lib/ex_webrtc/peer_connection/configuration.ex

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ defmodule ExWebRTC.PeerConnection.Configuration do
33
PeerConnection configuration
44
"""
55

6-
alias ExWebRTC.RTPCodecParameters
6+
alias ExWebRTC.{RTPCodecParameters, SDPUtils}
77
alias ExSDP.Attribute.{Extmap, FMTP, RTCPFeedback}
88

99
@default_audio_codecs [
@@ -48,8 +48,15 @@ defmodule ExWebRTC.PeerConnection.Configuration do
4848
}
4949
}
5050

51-
@mandatory_audio_rtp_hdr_exts Enum.map([:mid], &Map.fetch!(@rtp_hdr_extensions, &1).ext)
52-
@mandatory_video_rtp_hdr_exts Enum.map([:mid], &Map.fetch!(@rtp_hdr_extensions, &1).ext)
51+
@mandatory_audio_rtp_hdr_exts Map.new([:mid], fn ext_shortcut ->
52+
extmap = Map.fetch!(@rtp_hdr_extensions, ext_shortcut).ext
53+
{extmap.uri, extmap}
54+
end)
55+
56+
@mandatory_video_rtp_hdr_exts Map.new([:mid], fn ext_shortcut ->
57+
extmap = Map.fetch!(@rtp_hdr_extensions, ext_shortcut).ext
58+
{extmap.uri, extmap}
59+
end)
5360

5461
@typedoc """
5562
Supported RTP header extensions.
@@ -113,8 +120,8 @@ defmodule ExWebRTC.PeerConnection.Configuration do
113120
ice_ip_filter: (:inet.ip_address() -> boolean()),
114121
audio_codecs: [RTPCodecParameters.t()],
115122
video_codecs: [RTPCodecParameters.t()],
116-
audio_rtp_hdr_exts: [Extmap.t()],
117-
video_rtp_hdr_exts: [Extmap.t()]
123+
audio_rtp_hdr_exts: %{(uri :: String.t()) => Extmap.t()},
124+
video_rtp_hdr_exts: %{(uri :: String.t()) => Extmap.t()}
118125
}
119126

120127
defstruct ice_servers: [],
@@ -183,8 +190,8 @@ defmodule ExWebRTC.PeerConnection.Configuration do
183190
def is_supported_rtp_hdr_extension(config, rtp_hdr_extension, media_type) do
184191
supported_uris =
185192
case media_type do
186-
:audio -> Enum.map(config.audio_rtp_hdr_exts, & &1.uri)
187-
:video -> Enum.map(config.video_rtp_hdr_exts, & &1.uri)
193+
:audio -> Map.keys(config.audio_rtp_hdr_exts)
194+
:video -> Map.keys(config.video_rtp_hdr_exts)
188195
end
189196

190197
rtp_hdr_extension.uri in supported_uris
@@ -194,34 +201,66 @@ defmodule ExWebRTC.PeerConnection.Configuration do
194201
@spec is_supported_rtcp_fb(t(), RTCPFeedback.t()) :: boolean()
195202
def is_supported_rtcp_fb(_config, _rtcp_fb), do: false
196203

204+
@doc false
205+
@spec update(t(), ExSDP.t()) :: t()
206+
def update(config, sdp) do
207+
extmaps = SDPUtils.get_extensions2(sdp)
208+
209+
{audio_exts, video_exts} =
210+
Enum.reduce(extmaps, {config.audio_rtp_hdr_exts, config.video_rtp_hdr_exts}, fn extmap,
211+
{audio_exts,
212+
video_exts} ->
213+
audio_exts =
214+
if Map.has_key?(audio_exts, extmap.uri) do
215+
Map.put(audio_exts, extmap.uri, extmap)
216+
else
217+
audio_exts
218+
end
219+
220+
video_exts =
221+
if Map.has_key?(video_exts, extmap.uri) do
222+
Map.put(video_exts, extmap.uri, extmap)
223+
else
224+
video_exts
225+
end
226+
227+
{audio_exts, video_exts}
228+
end)
229+
230+
%{config | audio_rtp_hdr_exts: audio_exts, video_rtp_hdr_exts: video_exts}
231+
end
232+
197233
defp add_mandatory_rtp_hdr_extensions(options) do
198234
options
199-
|> Keyword.update(:audio_rtp_hdr_exts, [], fn exts ->
200-
exts ++ @mandatory_audio_rtp_hdr_exts
235+
|> Keyword.update(:audio_rtp_hdr_exts, %{}, fn exts ->
236+
Map.merge(exts, @mandatory_audio_rtp_hdr_exts)
201237
end)
202-
|> Keyword.update(:video_rtp_hdr_exts, [], fn exts ->
203-
exts ++ @mandatory_video_rtp_hdr_exts
238+
|> Keyword.update(:video_rtp_hdr_exts, %{}, fn exts ->
239+
Map.merge(exts, @mandatory_video_rtp_hdr_exts)
204240
end)
205241
end
206242

207243
defp resolve_rtp_hdr_extensions(options) do
208244
{audio_exts, video_exts} =
209245
Keyword.get(options, :rtp_hdr_extensions, [])
210-
|> Enum.reduce({[], []}, fn ext, {audio_exts, video_exts} ->
246+
|> Enum.reduce({%{}, %{}}, fn ext, {audio_exts, video_exts} ->
211247
resolved_ext = Map.fetch!(@rtp_hdr_extensions, ext)
212248

213249
case resolved_ext.media_type do
214250
:audio ->
215-
{[resolved_ext.ext | audio_exts], video_exts}
251+
audio_exts = Map.put(audio_exts, resolved_ext.ext.uri, resolved_ext.ext)
252+
{audio_exts, video_exts}
216253

217254
:all ->
218-
{[resolved_ext.ext | audio_exts], [resolved_ext.ext | video_exts]}
255+
audio_exts = Map.put(audio_exts, resolved_ext.ext.uri, resolved_ext.ext)
256+
video_exts = Map.put(video_exts, resolved_ext.ext.uri, resolved_ext.ext)
257+
{audio_exts, video_exts}
219258
end
220259
end)
221260

222261
options
223-
|> Keyword.put(:audio_rtp_hdr_exts, Enum.reverse(audio_exts))
224-
|> Keyword.put(:video_rtp_hdr_exts, Enum.reverse(video_exts))
262+
|> Keyword.put(:audio_rtp_hdr_exts, audio_exts)
263+
|> Keyword.put(:video_rtp_hdr_exts, video_exts)
225264
|> Keyword.delete(:rtp_hdr_extensions)
226265
end
227266

lib/ex_webrtc/sdp_utils.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ defmodule ExWebRTC.SDPUtils do
22
@moduledoc false
33

44
alias ExRTP.Packet.Extension
5+
alias ExSDP.Attribute.Extmap
56

67
@type extension() :: {Extension.SourceDescription, atom()}
78

@@ -201,6 +202,11 @@ defmodule ExWebRTC.SDPUtils do
201202
end
202203
end
203204

205+
@spec get_extensions2(ExSDP.t()) :: [Extmap.t()]
206+
def get_extensions2(sdp) do
207+
Enum.flat_map(sdp.media, &ExSDP.get_attributes(&1, Extmap))
208+
end
209+
204210
@spec get_extensions(ExSDP.t()) :: %{(id :: non_neg_integer()) => extension() | :unknown}
205211
def get_extensions(sdp) do
206212
# we assume that, if extension is present in multiple mlines, the IDs are the same (RFC 8285)

test/ex_webrtc/peer_connection/configuration_test.exs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,12 @@ defmodule ExWebRTC.PeerConnection.ConfigurationTest do
132132

133133
# we can't compare ids as those used in audio_video_offer are
134134
# different than those used by us
135-
assert [
136-
%Extmap{uri: "urn:ietf:params:rtp-hdrext:ssrc-audio-level"},
137-
%Extmap{uri: "urn:ietf:params:rtp-hdrext:sdes:mid"}
138-
] = tr.rtp_hdr_exts
135+
tr_rtp_hdr_exts = Enum.map(tr.rtp_hdr_exts, & &1.uri) |> MapSet.new()
136+
137+
assert MapSet.new([
138+
"urn:ietf:params:rtp-hdrext:ssrc-audio-level",
139+
"urn:ietf:params:rtp-hdrext:sdes:mid"
140+
]) == tr_rtp_hdr_exts
139141

140142
{:ok, tr} = PeerConnection.add_transceiver(pc, :video)
141143
assert [%Extmap{uri: "urn:ietf:params:rtp-hdrext:sdes:mid"}] = tr.rtp_hdr_exts

test/ex_webrtc/peer_connection_test.exs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ defmodule ExWebRTC.PeerConnectionTest do
33

44
alias ExWebRTC.{MediaStreamTrack, PeerConnection, SessionDescription, SDPUtils, Utils}
55

6+
alias ExSDP.Attribute.Extmap
7+
68
{_pkey, cert} = ExDTLS.generate_key_cert()
79

810
@fingerprint cert
@@ -355,4 +357,43 @@ defmodule ExWebRTC.PeerConnectionTest do
355357

356358
assert_receive {:ex_webrtc, ^pc2, {:rtp, ^id2, %ExRTP.Packet{payload: ^payload}}}
357359
end
360+
361+
test "updates rtp header extension ids" do
362+
# check wheter we update our RTP header extension ids
363+
# when we receive a remote offer with different ones
364+
{:ok, pc1} = PeerConnection.start_link()
365+
{:ok, _} = PeerConnection.add_transceiver(pc1, :audio)
366+
{:ok, offer} = PeerConnection.create_offer(pc1)
367+
368+
sdp = ExSDP.parse!(offer.sdp)
369+
370+
# munge Extmap so that we use different id
371+
[mline] = sdp.media
372+
373+
extmaps =
374+
mline
375+
|> ExSDP.get_attributes(Extmap)
376+
|> Enum.map(fn extmap -> %{extmap | id: extmap.id + 1} end)
377+
378+
mline =
379+
mline
380+
|> SDPUtils.delete_attribute(Extmap)
381+
|> ExSDP.add_attributes(extmaps)
382+
383+
sdp = %{sdp | media: [mline]}
384+
385+
offer = %SessionDescription{type: :offer, sdp: to_string(sdp)}
386+
387+
{:ok, pc2} = PeerConnection.start_link()
388+
:ok = PeerConnection.set_remote_description(pc2, offer)
389+
{:ok, answer} = PeerConnection.create_answer(pc2)
390+
:ok = PeerConnection.set_local_description(pc2, answer)
391+
392+
{:ok, _} = PeerConnection.add_transceiver(pc2, :audio)
393+
{:ok, offer} = PeerConnection.create_offer(pc2)
394+
395+
[audio1, audio2] = ExSDP.parse!(offer.sdp).media
396+
397+
assert ExSDP.get_attributes(audio1, Extmap) == ExSDP.get_attributes(audio2, Extmap)
398+
end
358399
end

0 commit comments

Comments
 (0)