Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The package can be installed by adding `membrane_webrtc_plugin` to your list of
```elixir
def deps do
[
{:membrane_webrtc_plugin, "~> 0.22.1"}
{:membrane_webrtc_plugin, "~> 0.23.0"}
]
end
```
Expand Down
30 changes: 5 additions & 25 deletions lib/membrane_webrtc/ex_webrtc/sink.ex
Original file line number Diff line number Diff line change
Expand Up @@ -210,20 +210,16 @@ defmodule Membrane.WebRTC.ExWebRTCSink do

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

video_codecs = get_negotiated_video_codecs(sdp)
video_codecs = ExWebRTCUtils.get_video_codecs_from_sdp(sdp)

to_notify =
negotiating_tracks
|> Enum.filter(& &1.notify)
|> Enum.map(&Map.take(&1, [:id, :kind]))
|> Enum.map(fn
%{kind: :audio} = track -> Map.put(track, :codec, :opus)
%{kind: :video} = track -> Map.put(track, :codec, video_codecs)
end)
negotiating_tracks |> Enum.filter(& &1.notify) |> Enum.map(&Map.take(&1, [:id, :kind]))

actions =
new_tracks_notification =
if to_notify == [], do: [], else: [notify_parent: {:new_tracks, to_notify}]

actions = [notify_parent: {:negotiated_video_codecs, video_codecs}] ++ new_tracks_notification

negotiated_tracks = negotiated_tracks ++ negotiating_tracks

state =
Expand Down Expand Up @@ -296,20 +292,4 @@ defmodule Membrane.WebRTC.ExWebRTCSink do
seq_num = rem(params.seq_num + 1, @max_rtp_seq_num + 1)
put_in(state.input_tracks[pad], {id, %{params | seq_num: seq_num}})
end

defp get_negotiated_video_codecs(sdp_answer) do
ex_sdp = ExSDP.parse!(sdp_answer.sdp)

ex_sdp.media
|> Enum.flat_map(fn
%{type: :video, attributes: attributes} -> attributes
_media -> []
end)
|> Enum.flat_map(fn
%ExSDP.Attribute.RTPMapping{encoding: "H264"} -> [:h264]
%ExSDP.Attribute.RTPMapping{encoding: "VP8"} -> [:vp8]
_attribute -> []
end)
|> Enum.uniq()
end
end
56 changes: 43 additions & 13 deletions lib/membrane_webrtc/ex_webrtc/source.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
alias Membrane.WebRTC.{ExWebRTCUtils, SignalingChannel, SimpleWebSocketServer, WhipServer}

def_options signaling: [],
video_codec: [],
allowed_video_codecs: [],
preferred_video_codec: [],
ice_servers: [],
keyframe_interval: [],
sdp_candidates_timeout: []
Expand Down Expand Up @@ -39,6 +40,8 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
status: :init | :connecting | :connected | :closed,
audio_params: [ExWebRTC.RTPCodecParameters.t()],
video_params: [ExWebRTC.RTPCodecParameters.t()],
allowed_video_codecs: [:h264 | :vp8],
preferred_video_codec: :h264 | :vp8,
ice_servers: [ExWebRTC.PeerConnection.Configuration.ice_server()],
keyframe_interval: Membrane.Time.t() | nil,
sdp_candidates_timeout: Membrane.Time.t() | nil
Expand All @@ -47,13 +50,16 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
@enforce_keys [
:signaling,
:audio_params,
:video_params,
:allowed_video_codecs,
:preferred_video_codec,
:ice_servers,
:keyframe_interval,
:sdp_candidates_timeout
]

defstruct @enforce_keys ++
[
video_params: nil,
pc: nil,
output_tracks: %{},
awaiting_outputs: [],
Expand All @@ -69,7 +75,8 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
%State{
signaling: opts.signaling,
audio_params: ExWebRTCUtils.codec_params(:opus),
video_params: ExWebRTCUtils.codec_params(opts.video_codec),
allowed_video_codecs: opts.allowed_video_codecs |> Enum.uniq(),
preferred_video_codec: opts.preferred_video_codec,
ice_servers: opts.ice_servers,
keyframe_interval: opts.keyframe_interval,
sdp_candidates_timeout: opts.sdp_candidates_timeout
Expand All @@ -95,18 +102,10 @@ defmodule Membrane.WebRTC.ExWebRTCSource do

@impl true
def handle_playing(_ctx, state) do
{:ok, pc} =
PeerConnection.start(
ice_servers: state.ice_servers,
video_codecs: state.video_params,
audio_codecs: state.audio_params
)

Process.monitor(pc)
Process.monitor(state.signaling.pid)
SignalingChannel.register_element(state.signaling)

{[], %{state | pc: pc, status: :connecting}}
{[], %{state | status: :connecting}}
end

@impl true
Expand Down Expand Up @@ -216,6 +215,8 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
state
) do
Membrane.Logger.debug("Received SDP offer")

{codecs_notification, state} = ensure_peer_connection_started(sdp, state)
:ok = PeerConnection.set_remote_description(state.pc, sdp)

{new_tracks, awaiting_outputs} =
Expand Down Expand Up @@ -264,7 +265,7 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
[]
end)

{tracks_notification ++ stream_formats, state}
{codecs_notification ++ tracks_notification ++ stream_formats, state}
end

@impl true
Expand Down Expand Up @@ -303,6 +304,35 @@ defmodule Membrane.WebRTC.ExWebRTCSource do
{[], state}
end

defp ensure_peer_connection_started(sdp, %{pc: nil} = state) do
video_codecs_in_sdp = ExWebRTCUtils.get_video_codecs_from_sdp(sdp)

negotiated_video_codecs =
state.allowed_video_codecs
|> Enum.filter(&(&1 in video_codecs_in_sdp))
|> case do
[] -> []
[codec] -> [codec]
_both -> [state.preferred_video_codec]
end

video_params = ExWebRTCUtils.codec_params(negotiated_video_codecs)

{:ok, pc} =
PeerConnection.start(
ice_servers: state.ice_servers,
video_codecs: video_params,
audio_codecs: state.audio_params
)

Process.monitor(pc)

notify_parent = [notify_parent: {:negotiated_video_codecs, negotiated_video_codecs}]
{notify_parent, %{state | pc: pc, video_params: video_params}}
end

defp ensure_peer_connection_started(_sdp, state), do: {[], state}

defp maybe_answer(state) do
if Enum.all?(state.output_tracks, fn {_id, %{status: status}} -> status == :connected end) do
%{pc: pc} = state
Expand Down
17 changes: 17 additions & 0 deletions lib/membrane_webrtc/ex_webrtc/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,21 @@ defmodule Membrane.WebRTC.ExWebRTCUtils do
90_000
end
end

@spec get_video_codecs_from_sdp(ExWebRTC.SessionDescription.t()) :: [:h264 | :vp8]
def get_video_codecs_from_sdp(%ExWebRTC.SessionDescription{sdp: sdp}) do
ex_sdp = ExSDP.parse!(sdp)

ex_sdp.media
|> Enum.flat_map(fn
%{type: :video, attributes: attributes} -> attributes
_media -> []
end)
|> Enum.flat_map(fn
%ExSDP.Attribute.RTPMapping{encoding: "H264"} -> [:h264]
%ExSDP.Attribute.RTPMapping{encoding: "VP8"} -> [:vp8]
_attribute -> []
end)
|> Enum.uniq()
end
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule Membrane.WebRTC.Sink.ForwardingFilter do
defmodule Membrane.WebRTC.ForwardingFilter do
@moduledoc false
use Membrane.Filter

Expand Down
14 changes: 4 additions & 10 deletions lib/membrane_webrtc/sink.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ defmodule Membrane.WebRTC.Sink do
"""
use Membrane.Bin

alias __MODULE__.ForwardingFilter

alias Membrane.H264
alias Membrane.RemoteStream
alias Membrane.VP8
alias Membrane.WebRTC.{ExWebRTCSink, SignalingChannel, SimpleWebSocketServer}
alias Membrane.WebRTC.{ExWebRTCSink, ForwardingFilter, SignalingChannel, SimpleWebSocketServer}

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

@impl true
def handle_child_notification({:new_tracks, tracks}, :webrtc, _ctx, state) do
{[notify_parent: {:new_tracks, tracks}], state}
end

@impl true
def handle_child_notification({:negotiated_video_codecs, codecs}, :webrtc, _ctx, state) do
{[notify_parent: {:negotiated_video_codecs, codecs}], state}
def handle_child_notification({type, _content} = notification, :webrtc, _ctx, state)
when type in [:new_tracks, :negotiated_video_codecs] do
{[notify_parent: notification], state}
end

@impl true
Expand Down
Loading