Skip to content

Commit 71f23f6

Browse files
authored
Merge pull request #11 from membraneframework/support-various-video-codecs-in-source
Support various video codecs in source
2 parents 50e1242 + 7f761d3 commit 71f23f6

File tree

13 files changed

+365
-106
lines changed

13 files changed

+365
-106
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The package can be installed by adding `membrane_webrtc_plugin` to your list of
1515
```elixir
1616
def deps do
1717
[
18-
{:membrane_webrtc_plugin, "~> 0.22.1"}
18+
{:membrane_webrtc_plugin, "~> 0.23.0"}
1919
]
2020
end
2121
```

lib/membrane_webrtc/ex_webrtc/sink.ex

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -210,20 +210,16 @@ defmodule Membrane.WebRTC.ExWebRTCSink do
210210

211211
%{negotiating_tracks: negotiating_tracks, negotiated_tracks: negotiated_tracks} = state
212212

213-
video_codecs = get_negotiated_video_codecs(sdp)
213+
video_codecs = ExWebRTCUtils.get_video_codecs_from_sdp(sdp)
214214

215215
to_notify =
216-
negotiating_tracks
217-
|> Enum.filter(& &1.notify)
218-
|> Enum.map(&Map.take(&1, [:id, :kind]))
219-
|> Enum.map(fn
220-
%{kind: :audio} = track -> Map.put(track, :codec, :opus)
221-
%{kind: :video} = track -> Map.put(track, :codec, video_codecs)
222-
end)
216+
negotiating_tracks |> Enum.filter(& &1.notify) |> Enum.map(&Map.take(&1, [:id, :kind]))
223217

224-
actions =
218+
new_tracks_notification =
225219
if to_notify == [], do: [], else: [notify_parent: {:new_tracks, to_notify}]
226220

221+
actions = [notify_parent: {:negotiated_video_codecs, video_codecs}] ++ new_tracks_notification
222+
227223
negotiated_tracks = negotiated_tracks ++ negotiating_tracks
228224

229225
state =
@@ -296,20 +292,4 @@ defmodule Membrane.WebRTC.ExWebRTCSink do
296292
seq_num = rem(params.seq_num + 1, @max_rtp_seq_num + 1)
297293
put_in(state.input_tracks[pad], {id, %{params | seq_num: seq_num}})
298294
end
299-
300-
defp get_negotiated_video_codecs(sdp_answer) do
301-
ex_sdp = ExSDP.parse!(sdp_answer.sdp)
302-
303-
ex_sdp.media
304-
|> Enum.flat_map(fn
305-
%{type: :video, attributes: attributes} -> attributes
306-
_media -> []
307-
end)
308-
|> Enum.flat_map(fn
309-
%ExSDP.Attribute.RTPMapping{encoding: "H264"} -> [:h264]
310-
%ExSDP.Attribute.RTPMapping{encoding: "VP8"} -> [:vp8]
311-
_attribute -> []
312-
end)
313-
|> Enum.uniq()
314-
end
315295
end

lib/membrane_webrtc/ex_webrtc/source.ex

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
99
alias Membrane.WebRTC.{ExWebRTCUtils, SignalingChannel, SimpleWebSocketServer, WhipServer}
1010

1111
def_options signaling: [],
12-
video_codec: [],
12+
allowed_video_codecs: [],
13+
preferred_video_codec: [],
1314
ice_servers: [],
1415
keyframe_interval: [],
1516
sdp_candidates_timeout: []
@@ -39,6 +40,8 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
3940
status: :init | :connecting | :connected | :closed,
4041
audio_params: [ExWebRTC.RTPCodecParameters.t()],
4142
video_params: [ExWebRTC.RTPCodecParameters.t()],
43+
allowed_video_codecs: [:h264 | :vp8],
44+
preferred_video_codec: :h264 | :vp8,
4245
ice_servers: [ExWebRTC.PeerConnection.Configuration.ice_server()],
4346
keyframe_interval: Membrane.Time.t() | nil,
4447
sdp_candidates_timeout: Membrane.Time.t() | nil
@@ -47,13 +50,16 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
4750
@enforce_keys [
4851
:signaling,
4952
:audio_params,
50-
:video_params,
53+
:allowed_video_codecs,
54+
:preferred_video_codec,
5155
:ice_servers,
5256
:keyframe_interval,
5357
:sdp_candidates_timeout
5458
]
59+
5560
defstruct @enforce_keys ++
5661
[
62+
video_params: nil,
5763
pc: nil,
5864
output_tracks: %{},
5965
awaiting_outputs: [],
@@ -69,7 +75,8 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
6975
%State{
7076
signaling: opts.signaling,
7177
audio_params: ExWebRTCUtils.codec_params(:opus),
72-
video_params: ExWebRTCUtils.codec_params(opts.video_codec),
78+
allowed_video_codecs: opts.allowed_video_codecs |> Enum.uniq(),
79+
preferred_video_codec: opts.preferred_video_codec,
7380
ice_servers: opts.ice_servers,
7481
keyframe_interval: opts.keyframe_interval,
7582
sdp_candidates_timeout: opts.sdp_candidates_timeout
@@ -95,18 +102,10 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
95102

96103
@impl true
97104
def handle_playing(_ctx, state) do
98-
{:ok, pc} =
99-
PeerConnection.start(
100-
ice_servers: state.ice_servers,
101-
video_codecs: state.video_params,
102-
audio_codecs: state.audio_params
103-
)
104-
105-
Process.monitor(pc)
106105
Process.monitor(state.signaling.pid)
107106
SignalingChannel.register_element(state.signaling)
108107

109-
{[], %{state | pc: pc, status: :connecting}}
108+
{[], %{state | status: :connecting}}
110109
end
111110

112111
@impl true
@@ -216,6 +215,8 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
216215
state
217216
) do
218217
Membrane.Logger.debug("Received SDP offer")
218+
219+
{codecs_notification, state} = ensure_peer_connection_started(sdp, state)
219220
:ok = PeerConnection.set_remote_description(state.pc, sdp)
220221

221222
{new_tracks, awaiting_outputs} =
@@ -264,7 +265,7 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
264265
[]
265266
end)
266267

267-
{tracks_notification ++ stream_formats, state}
268+
{codecs_notification ++ tracks_notification ++ stream_formats, state}
268269
end
269270

270271
@impl true
@@ -303,6 +304,35 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
303304
{[], state}
304305
end
305306

307+
defp ensure_peer_connection_started(sdp, %{pc: nil} = state) do
308+
video_codecs_in_sdp = ExWebRTCUtils.get_video_codecs_from_sdp(sdp)
309+
310+
negotiated_video_codecs =
311+
state.allowed_video_codecs
312+
|> Enum.filter(&(&1 in video_codecs_in_sdp))
313+
|> case do
314+
[] -> []
315+
[codec] -> [codec]
316+
_both -> [state.preferred_video_codec]
317+
end
318+
319+
video_params = ExWebRTCUtils.codec_params(negotiated_video_codecs)
320+
321+
{:ok, pc} =
322+
PeerConnection.start(
323+
ice_servers: state.ice_servers,
324+
video_codecs: video_params,
325+
audio_codecs: state.audio_params
326+
)
327+
328+
Process.monitor(pc)
329+
330+
notify_parent = [notify_parent: {:negotiated_video_codecs, negotiated_video_codecs}]
331+
{notify_parent, %{state | pc: pc, video_params: video_params}}
332+
end
333+
334+
defp ensure_peer_connection_started(_sdp, state), do: {[], state}
335+
306336
defp maybe_answer(state) do
307337
if Enum.all?(state.output_tracks, fn {_id, %{status: status}} -> status == :connected end) do
308338
%{pc: pc} = state

lib/membrane_webrtc/ex_webrtc/utils.ex

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,21 @@ defmodule Membrane.WebRTC.ExWebRTCUtils do
6161
90_000
6262
end
6363
end
64+
65+
@spec get_video_codecs_from_sdp(ExWebRTC.SessionDescription.t()) :: [:h264 | :vp8]
66+
def get_video_codecs_from_sdp(%ExWebRTC.SessionDescription{sdp: sdp}) do
67+
ex_sdp = ExSDP.parse!(sdp)
68+
69+
ex_sdp.media
70+
|> Enum.flat_map(fn
71+
%{type: :video, attributes: attributes} -> attributes
72+
_media -> []
73+
end)
74+
|> Enum.flat_map(fn
75+
%ExSDP.Attribute.RTPMapping{encoding: "H264"} -> [:h264]
76+
%ExSDP.Attribute.RTPMapping{encoding: "VP8"} -> [:vp8]
77+
_attribute -> []
78+
end)
79+
|> Enum.uniq()
80+
end
6481
end

lib/membrane_webrtc/sink/forwarding_filter.ex renamed to lib/membrane_webrtc/forwarding_filter.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
defmodule Membrane.WebRTC.Sink.ForwardingFilter do
1+
defmodule Membrane.WebRTC.ForwardingFilter do
22
@moduledoc false
33
use Membrane.Filter
44

lib/membrane_webrtc/sink.ex

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,10 @@ defmodule Membrane.WebRTC.Sink do
1717
"""
1818
use Membrane.Bin
1919

20-
alias __MODULE__.ForwardingFilter
21-
2220
alias Membrane.H264
2321
alias Membrane.RemoteStream
2422
alias Membrane.VP8
25-
alias Membrane.WebRTC.{ExWebRTCSink, SignalingChannel, SimpleWebSocketServer}
23+
alias Membrane.WebRTC.{ExWebRTCSink, ForwardingFilter, SignalingChannel, SimpleWebSocketServer}
2624

2725
@typedoc """
2826
Notification that should be sent to the bin to negotiate new tracks.
@@ -184,13 +182,9 @@ defmodule Membrane.WebRTC.Sink do
184182
end
185183

186184
@impl true
187-
def handle_child_notification({:new_tracks, tracks}, :webrtc, _ctx, state) do
188-
{[notify_parent: {:new_tracks, tracks}], state}
189-
end
190-
191-
@impl true
192-
def handle_child_notification({:negotiated_video_codecs, codecs}, :webrtc, _ctx, state) do
193-
{[notify_parent: {:negotiated_video_codecs, codecs}], state}
185+
def handle_child_notification({type, _content} = notification, :webrtc, _ctx, state)
186+
when type in [:new_tracks, :negotiated_video_codecs] do
187+
{[notify_parent: notification], state}
194188
end
195189

196190
@impl true

0 commit comments

Comments
 (0)