17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Stats
51 |
uninitialized
52 |
53 |
--------------------------------------------------------------------------------
/webrtc/integration_test/test_videoroom/lib/test_videoroom_web/views/error_view.ex:
--------------------------------------------------------------------------------
1 | defmodule TestVideoroomWeb.ErrorView do
2 | use TestVideoroomWeb, :view
3 | # If you want to customize a particular status code
4 | # for a certain format, you may uncomment below.
5 | # def render("500.html", _assigns) do
6 | # "Internal Server Error"
7 | # end
8 |
9 | # By default, Phoenix returns the status message from
10 | # the template name. For example, "404.html" becomes
11 | # "Not Found".
12 | def template_not_found(template, _assigns) do
13 | Phoenix.Controller.status_message_from_template(template)
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/webrtc/integration_test/test_videoroom/lib/test_videoroom_web/views/layout_view.ex:
--------------------------------------------------------------------------------
1 | defmodule TestVideoroomWeb.LayoutView do
2 | use TestVideoroomWeb, :view
3 | end
4 |
--------------------------------------------------------------------------------
/webrtc/integration_test/test_videoroom/lib/test_videoroom_web/views/page_view.ex:
--------------------------------------------------------------------------------
1 | defmodule TestVideoroomWeb.PageView do
2 | use TestVideoroomWeb, :view
3 | end
4 |
--------------------------------------------------------------------------------
/webrtc/integration_test/test_videoroom/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule TestVideoroom.MixProject do
2 | use Mix.Project
3 |
4 | def project do
5 | [
6 | app: :test_videoroom,
7 | version: "0.1.0",
8 | elixir: "~> 1.12",
9 | elixirc_paths: elixirc_paths(Mix.env()),
10 | compilers: Mix.compilers(),
11 | start_permanent: Mix.env() == :prod,
12 | deps: deps(),
13 | aliases: aliases()
14 | ]
15 | end
16 |
17 | def application do
18 | [
19 | mod: {TestVideoroom.Application, []},
20 | extra_applications: [:logger]
21 | ]
22 | end
23 |
24 | # Specifies which paths to compile per environment.
25 | defp elixirc_paths(:test), do: ["lib", "test/support"]
26 | defp elixirc_paths(_env), do: ["lib"]
27 |
28 | defp deps do
29 | [
30 | {:phoenix, "~> 1.6.2"},
31 | {:phoenix_html, "~> 3.0"},
32 | {:phoenix_live_view, "~> 0.17"},
33 | {:esbuild, "~> 0.4", runtime: Mix.env() == :dev},
34 | {:telemetry, "~> 1.0", override: true},
35 | {:jason, "~> 1.2"},
36 | {:plug_cowboy, "~> 2.5"},
37 | {:cowlib, "~> 2.11", override: true},
38 | {:membrane_rtc_engine, path: rtc_engine_path(), override: true},
39 | {:membrane_rtc_engine_webrtc, path: Path.join(rtc_engine_path(), "../webrtc/")},
40 | {:stampede, github: "membraneframework-labs/stampede-elixir"}
41 | ]
42 | end
43 |
44 | defp rtc_engine_path(), do: System.get_env("RTC_ENGINE_PATH", "../../../engine/")
45 |
46 | defp aliases() do
47 | [
48 | test: ["assets.deploy", "test --exclude containerised"],
49 | "assets.deploy": ["esbuild default --minify", "phx.digest"],
50 | "test.containerised": ["test --only containerised"]
51 | ]
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/webrtc/integration_test/test_videoroom/test/support/conn_case.ex:
--------------------------------------------------------------------------------
1 | defmodule TestVideoroomWeb.ConnCase do
2 | @moduledoc """
3 | This module defines the test case to be used by
4 | tests that require setting up a connection.
5 |
6 | Such tests rely on `Phoenix.ConnTest` and also
7 | import other functionality to make it easier
8 | to build common data structures and query the data layer.
9 |
10 | Finally, if the test case interacts with the database,
11 | we enable the SQL sandbox, so changes done to the database
12 | are reverted at the end of every test. If you are using
13 | PostgreSQL, you can even run database tests asynchronously
14 | by setting `use TestVideoroomWeb.ConnCase, async: true`, although
15 | this option is not recommended for other databases.
16 | """
17 |
18 | use ExUnit.CaseTemplate
19 |
20 | using do
21 | quote do
22 | # Import conveniences for testing with connections
23 | import Plug.Conn
24 | import Phoenix.ConnTest
25 | import TestVideoroomWeb.ConnCase
26 |
27 | alias TestVideoroomWeb.Router.Helpers, as: Routes
28 |
29 | # The default endpoint for testing
30 | @endpoint TestVideoroomWeb.Endpoint
31 | end
32 | end
33 |
34 | setup _tags do
35 | {:ok, conn: Phoenix.ConnTest.build_conn()}
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/webrtc/integration_test/test_videoroom/test/support/test_result_receiver.ex:
--------------------------------------------------------------------------------
1 | defmodule TestVideoroom.Integration.ResultReceiver do
2 | @moduledoc false
3 |
4 | use GenServer
5 | require Logger
6 |
7 | def start_link(opts) do
8 | GenServer.start_link(__MODULE__, opts, name: __MODULE__)
9 | end
10 |
11 | @impl true
12 | def init(args) do
13 | state = %{
14 | browser_count: Keyword.fetch!(args, :browser_count),
15 | parent: Keyword.fetch!(args, :parent),
16 | results: %{},
17 | received: 0
18 | }
19 |
20 | {:ok, state}
21 | end
22 |
23 | @impl true
24 | def handle_info({:stats, name, stats}, %{results: results} = state) do
25 | Logger.info("Received stats from #{inspect(name)}")
26 |
27 | results = Map.put(results, name, stats)
28 | received = Map.keys(results) |> length()
29 |
30 | if received < state.browser_count do
31 | {:noreply, %{state | results: results, received: received}}
32 | else
33 | Logger.info("All #{inspect(state.browser_count)} stats received. Result receiver exiting")
34 | send(state.parent, {:results, results})
35 |
36 | {:stop, :normal, state}
37 | end
38 | end
39 |
40 | @impl true
41 | def handle_info(msg, state) do
42 | send(state.parent, msg)
43 |
44 | {:noreply, state}
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/webrtc/integration_test/test_videoroom/test/support/utils.ex:
--------------------------------------------------------------------------------
1 | defmodule TestVideoroom.Integration.Utils do
2 | @moduledoc false
3 |
4 | def receive_stats(mustangs_number, pid, acc \\ %{}) do
5 | if mustangs_number > 0 do
6 | receive do
7 | {_browser_id, :end} ->
8 | receive_stats(mustangs_number - 1, pid, acc)
9 |
10 | {browser_id, tag, data} ->
11 | acc
12 | |> then(fn acc ->
13 | default_map = %{browser_id => [data]}
14 |
15 | Map.update(
16 | acc,
17 | tag,
18 | default_map,
19 | fn tag_map -> Map.update(tag_map, browser_id, [data], &(&1 ++ [data])) end
20 | )
21 | end)
22 | |> then(&receive_stats(mustangs_number, pid, &1))
23 | end
24 | else
25 | send(pid, {:stats, acc})
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/webrtc/integration_test/test_videoroom/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start(capture_log: true, exclude: [:skip])
2 |
--------------------------------------------------------------------------------
/webrtc/internal_docs/protocol.md:
--------------------------------------------------------------------------------
1 | # Signaling Protocol
2 |
3 | ### Joining the server
4 |
5 | ```mermaid
6 | sequenceDiagram
7 | actor client
8 | participant server
9 | actor other_client
10 | client->>server: connect
11 | server->>client: connected
12 | server->>other_client: endpointAdded
13 | ```
14 |
15 | ### Adding a track
16 |
17 | ```mermaid
18 | sequenceDiagram
19 | actor client
20 | participant server
21 | actor other_client
22 | client->>server: renegotiateTracks
23 | server->>client: custom(offerData)
24 | client->>server: custom(sdpOffer)
25 | par
26 | client->>server: custom(candidate)
27 | and
28 | server->>client: custom(sdpAnswer)
29 | server->>client: custom(candidate)
30 | server->>other_client: tracksAdded
31 | server->>client: tracksAdded
32 | rect rgb(135, 204, 232)
33 | note right of server: renegotiation
34 | server->>other_client: custom(offerData)
35 | other_client->>server: custom(sdpOffer)
36 | par
37 | other_client->>server: custom(candidate)
38 | and
39 | server->>other_client: custom(sdpAnswer)
40 | server->>other_client: custom(candidate)
41 | end
42 | end
43 | end
44 | ```
45 |
46 | ### Leaving the server
47 |
48 | ```mermaid
49 | sequenceDiagram
50 | actor client
51 | participant server
52 | actor other_client
53 | client->>server: disconnect
54 | rect rgb(135, 204, 232)
55 | note right of server: renegotiation
56 | server->>other_client: custom(offerData)
57 | other_client->>server: custom(sdpOffer)
58 | par
59 | other_client->>server: custom(candidate)
60 | and
61 | server->>other_client: custom(sdpAnswer)
62 | server->>other_client: custom(candidate)
63 | end
64 | end
65 | ```
66 |
--------------------------------------------------------------------------------
/webrtc/internal_docs/webrtc_endpoint.md:
--------------------------------------------------------------------------------
1 | # WebRTC Endpoint
2 |
3 | The WebRTC Endpoint is responsible for sending/receiving media tracks to/from a web borwser.
4 |
5 |
6 | ## Architecture
7 |
8 | 
9 |
10 |
11 | ## TrackSender
12 |
13 | `TrackSender` is responsible for observing track activity and publishing track data to the engine.
14 | In particular, it is responsible for sending `Membrane.RTC.Engine.Event.TrackVariantResumed`
15 | and `Membrane.RTC.Engine.Event.TrackVariantPaused` events, and replying to `Membrane.KeyframeRequestEvent`.
16 |
17 | #### VariantTracker
18 |
19 | A browser can pause sending some encoding when e.g. it doesn't have enough bandwidth.
20 | This fact is not communicated to the server.
21 | `VariantTracker` is responsible for tracking variant activity i.e. whether it is still active.
22 |
23 | ## TrackReceiver
24 |
25 | `TrackReceiver` is responsible for receiving a track from the engine.
26 | In particular, it is responsible for sending the `Membrane.RTC.Engine.Event.RequestTrackVariant` event,
27 | forwarding keyframe requests from the browser to the `TrackSender` and repairing RTP packets after
28 | switching track variants.
29 |
30 | ## ConnectionAllocator
31 | `ConnectionAllocator` is responsible for probing the connection and negotiating bandwidth with `TrackReceiver`.
32 | The RTC Engine ships with two implementations of `ConnectionAllocator`:
33 | * `Membrane.RTC.Engine.Endpoint.WebRTC.NoOpConnectionAllocator` - always grants bandwidth requested by `TrackReceiver`
34 | * `Membrane.RTC.Engine.Endpoint.WebRTC.RTPConnectionAllocator` - probes the connection with RTP padding packets and grants bandwidth basing on GCC estimates
35 |
36 | You can read more in `Membrane.RTC.Engine.Endpoint.WebRTC.ConnectionAllocator` module docs.
37 |
--------------------------------------------------------------------------------
/webrtc/lib/endpoint_bin/extension/mid.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.WebRTC.Extension.Mid do
2 | @moduledoc """
3 | Module implementing `Membrane.WebRTC.Extension` behaviour for Media Identification RTP Header extension.
4 |
5 | This extension is described at https://tools.ietf.org/pdf/draft-ietf-mmusic-rfc8843bis-09.pdf.
6 | """
7 | @behaviour Membrane.WebRTC.Extension
8 | alias ExSDP.Attribute.Extmap
9 | alias Membrane.WebRTC.Extension
10 |
11 | @name :mid
12 | @uri "urn:ietf:params:rtp-hdrext:sdes:mid"
13 |
14 | @impl true
15 | def new(opts \\ Keyword.new()),
16 | do: %Extension{
17 | module: __MODULE__,
18 | rtp_opts: opts,
19 | uri: @uri,
20 | name: @name
21 | }
22 |
23 | @impl true
24 | def compatible?(_encoding), do: true
25 |
26 | # there is no module parsing RTP headers against this extension as
27 | # for the whole session mid for same buffer will be the same.
28 | # It is used only in handler for `:new_rtp_stream` notification.
29 | @impl true
30 | def get_rtp_module(_mid_id, _opts, _track_type), do: :no_rtp_module
31 |
32 | @impl true
33 | def add_to_media(media, id, _direction, _payload_types),
34 | do: ExSDP.add_attribute(media, %Extmap{id: id, uri: @uri})
35 |
36 | @impl true
37 | def uri, do: @uri
38 | end
39 |
--------------------------------------------------------------------------------
/webrtc/lib/endpoint_bin/extension/repaired_rid.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.WebRTC.Extension.RepairedRid do
2 | @moduledoc """
3 | Module implementing `Membrane.WebRTC.Extension` behaviour for Repaired RTP Stream Identifier RTP Header extension.
4 |
5 | This extension is described in RFC 8852.
6 | """
7 | @behaviour Membrane.WebRTC.Extension
8 |
9 | alias ExSDP.Attribute.Extmap
10 | alias Membrane.WebRTC.Extension
11 |
12 | @name :repaired_rid
13 | @uri "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"
14 |
15 | @impl true
16 | def new(opts \\ Keyword.new()),
17 | do: %Extension{module: __MODULE__, rtp_opts: opts, uri: @uri, name: @name}
18 |
19 | @impl true
20 | def compatible?(_encoding), do: true
21 |
22 | @impl true
23 | def get_rtp_module(_rid_id, _opts, _track_type), do: :no_rtp_module
24 |
25 | @impl true
26 | def add_to_media(media, id, _direction, _payload_types),
27 | do: ExSDP.add_attribute(media, %Extmap{id: id, uri: @uri})
28 |
29 | @impl true
30 | def uri, do: @uri
31 | end
32 |
--------------------------------------------------------------------------------
/webrtc/lib/endpoint_bin/extension/rid.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.WebRTC.Extension.Rid do
2 | @moduledoc """
3 | Module implementing `Membrane.WebRTC.Extension` behaviour for RTP Stream Identifier RTP Header extension.
4 |
5 | This extension is described in RFC 8852.
6 | """
7 | @behaviour Membrane.WebRTC.Extension
8 |
9 | alias ExSDP.Attribute.Extmap
10 | alias Membrane.WebRTC.Extension
11 |
12 | @name :rid
13 | @uri "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"
14 |
15 | @impl true
16 | def new(opts \\ Keyword.new()),
17 | do: %Extension{module: __MODULE__, rtp_opts: opts, uri: @uri, name: @name}
18 |
19 | @impl true
20 | def compatible?(_encoding), do: true
21 |
22 | @impl true
23 | def get_rtp_module(_rid_id, _opts, _track_type), do: :no_rtp_module
24 |
25 | @impl true
26 | def add_to_media(media, id, _direction, _payload_types),
27 | do: ExSDP.add_attribute(media, %Extmap{id: id, uri: @uri})
28 |
29 | @impl true
30 | def uri, do: @uri
31 | end
32 |
--------------------------------------------------------------------------------
/webrtc/lib/endpoint_bin/extension/twcc.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.WebRTC.Extension.TWCC do
2 | @moduledoc """
3 | Module implementing `Membrane.WebRTC.Extension` behaviour for Transport-wide Congestion Control RTP Header extension.
4 |
5 | This extension is described at https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01.
6 | """
7 | @behaviour Membrane.WebRTC.Extension
8 |
9 | alias ExSDP.Attribute.{Extmap, RTCPFeedback}
10 | alias Membrane.WebRTC.Extension
11 |
12 | @name :twcc
13 | @uri "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
14 | @twcc_recv_rtp_module Membrane.RTP.TWCCReceiver
15 | @twcc_send_rtp_module Membrane.RTP.TWCCSender
16 |
17 | @impl true
18 | def new(opts \\ Keyword.new()),
19 | do: %Extension{module: __MODULE__, rtp_opts: opts, uri: @uri, name: @name}
20 |
21 | @impl true
22 | def compatible?(_encoding), do: true
23 |
24 | @impl true
25 | def get_rtp_module(twcc_id, _rtp_opts, :inbound), do: %@twcc_recv_rtp_module{twcc_id: twcc_id}
26 | def get_rtp_module(_twcc_id, _rtp_opts, :outbound), do: @twcc_send_rtp_module
27 |
28 | @impl true
29 | def add_to_media(media, id, _direction, payload_types) do
30 | media
31 | |> ExSDP.add_attribute(%Extmap{id: id, uri: @uri})
32 | |> ExSDP.add_attributes(Enum.map(payload_types, &%RTCPFeedback{pt: &1, feedback_type: :twcc}))
33 | end
34 |
35 | @impl true
36 | def uri, do: @uri
37 | end
38 |
--------------------------------------------------------------------------------
/webrtc/lib/endpoint_bin/extension/vad.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.WebRTC.Extension.VAD do
2 | @moduledoc """
3 | Module implementing `Membrane.WebRTC.Extension` behaviour for Client-to-Mixer Audio Level Indication RTP Header extension.
4 |
5 | This extension is described in RFC 6464.
6 | """
7 | @behaviour Membrane.WebRTC.Extension
8 |
9 | alias ExSDP.Attribute.Extmap
10 | alias Membrane.WebRTC.Extension
11 |
12 | @name :vad
13 | @uri "urn:ietf:params:rtp-hdrext:ssrc-audio-level"
14 | @attributes ["vad=on"]
15 | @rtp_module Membrane.RTP.VAD
16 |
17 | @impl true
18 | def new(opts \\ Keyword.new()),
19 | do: %Extension{module: __MODULE__, rtp_opts: opts, uri: @uri, name: @name}
20 |
21 | @impl true
22 | def compatible?(encoding), do: encoding == :OPUS
23 |
24 | @impl true
25 | def get_rtp_module(vad_id, rtp_opts, _track_type) do
26 | struct!(@rtp_module, [{:vad_id, vad_id} | rtp_opts])
27 | end
28 |
29 | @impl true
30 | def add_to_media(media, id, _direction, _pt),
31 | do:
32 | ExSDP.add_attribute(media, %Extmap{
33 | id: id,
34 | uri: @uri,
35 | attributes: @attributes
36 | })
37 |
38 | @impl true
39 | def uri, do: @uri
40 | end
41 |
--------------------------------------------------------------------------------
/webrtc/lib/endpoint_bin/metrics.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.WebRTC.Metrics do
2 | @moduledoc """
3 | Defines a list of metrics that can be aggregated based on events from `membrane_webrtc_plugin`.
4 | """
5 |
6 | alias Telemetry.Metrics
7 |
8 | @spec metrics() :: [Metrics.t()]
9 | def metrics(),
10 | do: [
11 | Metrics.last_value(
12 | "sdp.offer",
13 | event_name: [Membrane.WebRTC, :sdp, :offer],
14 | measurement: :sdp
15 | ),
16 | Metrics.last_value(
17 | "sdp.answer",
18 | event_name: [Membrane.WebRTC, :sdp, :answer],
19 | measurement: :sdp
20 | )
21 | ]
22 | end
23 |
--------------------------------------------------------------------------------
/webrtc/lib/endpoint_bin/track/constraints.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.WebRTC.Track.Constraints do
2 | @moduledoc false
3 |
4 | alias ExSDP.Attribute.{FMTP, RTPMapping}
5 | alias Membrane.WebRTC.{EndpointBin, Extension}
6 |
7 | @type t :: %__MODULE__{
8 | codecs_filter: ({RTPMapping.t(), FMTP.t() | nil} -> boolean()),
9 | enabled_extensions: [Extension.t()],
10 | simulcast?: boolean(),
11 | endpoint_direction: EndpointBin.direction()
12 | }
13 |
14 | @enforce_keys [:codecs_filter, :enabled_extensions, :endpoint_direction, :simulcast?]
15 | defstruct @enforce_keys
16 | end
17 |
--------------------------------------------------------------------------------
/webrtc/lib/endpoint_bin/track/encoding.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.WebRTC.Track.Encoding do
2 | @moduledoc """
3 | A struct holding data passed via SDP about a possible encoding of a track
4 | """
5 |
6 | alias ExSDP.Attribute.{FMTP, RTCPFeedback}
7 |
8 | @type t :: %__MODULE__{
9 | payload_type: non_neg_integer(),
10 | name: String.t(),
11 | clock_rate: pos_integer(),
12 | rtx: %{payload_type: non_neg_integer(), rtx_time: non_neg_integer() | nil} | nil,
13 | red_payload_type: non_neg_integer() | nil,
14 | audio_channels: pos_integer() | nil,
15 | rtcp_feedback: MapSet.t(RTCPFeedback.t()),
16 | format_params: FMTP.t() | nil
17 | }
18 |
19 | @enforce_keys [:payload_type, :name, :clock_rate]
20 | defstruct @enforce_keys ++
21 | [
22 | rtx: nil,
23 | red_payload_type: nil,
24 | audio_channels: nil,
25 | rtcp_feedback: MapSet.new(),
26 | format_params: nil
27 | ]
28 | end
29 |
--------------------------------------------------------------------------------
/webrtc/lib/endpoint_bin/utils.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.WebRTC.Utils do
2 | @moduledoc false
3 |
4 | @doc """
5 | Converts encoding name to its SDP string representation.
6 | """
7 | @spec encoding_name_to_string(atom()) :: String.t()
8 | def encoding_name_to_string(encoding_name) do
9 | case encoding_name do
10 | :VP8 -> "VP8"
11 | :H264 -> "H264"
12 | :OPUS -> "opus"
13 | nil -> raise "Empty encoding name!"
14 | x -> to_string(x)
15 | end
16 | end
17 |
18 | @spec anonymize_sdp(String.t()) :: String.t()
19 | def anonymize_sdp(sdp) do
20 | Regex.replace(~r/(ice-ufrag|fingerprint|ice-pwd):.*\n/, sdp, "\\1:****\n")
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/webrtc/lib/webrtc/connection_allocator/allocation_granted_notification.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.RTC.Engine.Endpoint.WebRTC.ConnectionAllocator.AllocationGrantedNotification do
2 | @moduledoc """
3 | Message sent by ConnectionProber to TrackReceiver whenever new allocation is granted
4 | """
5 |
6 | @enforce_keys [:allocation]
7 | defstruct @enforce_keys
8 |
9 | @type t() :: %__MODULE__{allocation: non_neg_integer()}
10 | end
11 |
--------------------------------------------------------------------------------
/webrtc/lib/webrtc/noop_connection_allocator.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.RTC.Engine.Endpoint.WebRTC.NoOpConnectionAllocator do
2 | @moduledoc """
3 | Implementation of `Membrane.RTC.Engine.Endpoint.WebRTC.ConnectionAllocator` that grants all allocations immediately.
4 |
5 | It might be useful for non-WebRTC Endpoints
6 | """
7 | @behaviour Membrane.RTC.Engine.Endpoint.WebRTC.ConnectionAllocator
8 |
9 | alias Membrane.RTC.Engine.Endpoint.WebRTC.ConnectionAllocator.AllocationGrantedNotification
10 |
11 | @impl true
12 | def create(), do: {:ok, nil}
13 |
14 | @impl true
15 | def destroy(nil), do: :ok
16 |
17 | @impl true
18 | def register_track_receiver(_manager, _bandwidth, _track, _options \\ []), do: :ok
19 |
20 | @impl true
21 | def update_bandwidth_estimation(_manager, _estimation), do: :ok
22 |
23 | @impl true
24 | def buffer_sent(_manager, _buffer), do: :ok
25 |
26 | @impl true
27 | def request_allocation(_manager, requested_allocation) do
28 | send(self(), %AllocationGrantedNotification{allocation: requested_allocation})
29 | :ok
30 | end
31 |
32 | @impl true
33 | def set_negotiability_status(_manager, _value), do: :ok
34 | end
35 |
--------------------------------------------------------------------------------
/webrtc/lib/webrtc/simulcast_config.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.RTC.Engine.Endpoint.WebRTC.SimulcastConfig do
2 | @moduledoc """
3 | Module representing simulcast configuration for WebRTC endpoint.
4 | """
5 |
6 | alias Membrane.RTC.Engine.Track
7 |
8 | @typedoc """
9 | Simulcast configuration.
10 |
11 | * `enabled` - whether to accept simulcast tracks or not.
12 | Setting this to false will result in rejecting all incoming
13 | simulcast tracks i.e. client will not send them.
14 | * `initial_target_variant` - function used to determine initial
15 | target variant this endpoint is willing to receive for given track.
16 | It is called for each track this endpoint subscribes for.
17 | If not provided, the highest possible variant will be used.
18 | """
19 | @type t() :: %__MODULE__{
20 | enabled: boolean(),
21 | initial_target_variant: (Track.t() -> Track.variant())
22 | }
23 | defstruct enabled: false,
24 | initial_target_variant: &__MODULE__.initial_target_variant/1
25 |
26 | @doc """
27 | Default implementation of `initial_target_variant` function in `t:t/0`.
28 |
29 | Returns :high, which will result in choosing the highest possible encoding.
30 | """
31 | @spec initial_target_variant(Track.t()) :: :high
32 | def initial_target_variant(_track), do: :high
33 | end
34 |
--------------------------------------------------------------------------------
/webrtc/test/fixtures/2_incoming_tracks_sdp.txt:
--------------------------------------------------------------------------------
1 | v=0
2 | o=- 7097343221192843169 3 IN IP4 127.0.0.1
3 | s=-
4 | t=0 0
5 | a=group:BUNDLE 0 1
6 | a=extmap-allow-mixed
7 | a=msid-semantic: WMS
8 | m=audio 9 UDP/TLS/RTP/SAVPF 111
9 | c=IN IP4 0.0.0.0
10 | a=rtcp:9 IN IP4 0.0.0.0
11 | a=ice-ufrag:RxBw
12 | a=ice-pwd:fy2QR48Q60jSkViidvOVg6Oa
13 | a=ice-options:trickle
14 | a=fingerprint:sha-256 7B:65:24:37:0B:A4:6B:90:56:E7:61:40:14:24:06:32:F2:D2:45:E7:77:DD:D9:5B:BF:CA:6A:27:5D:1F:12:6D
15 | a=setup:actpass
16 | a=mid:0
17 | a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
18 | a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
19 | a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
20 | a=sendonly
21 | a=msid:- 37b798cc-aa83-427f-8add-55d6cb88d447
22 | a=rtcp-mux
23 | a=rtpmap:111 opus/48000/2
24 | a=rtcp-fb:111 transport-cc
25 | a=fmtp:111 minptime=10;useinbandfec=1
26 | a=ssrc:182703199 cname:nje6WnGPirP+JG+s
27 | a=ssrc:182703199 msid:- 37b798cc-aa83-427f-8add-55d6cb88d447
28 | a=ssrc:182703199 mslabel:-
29 | a=ssrc:182703199 label:37b798cc-aa83-427f-8add-55d6cb88d447
30 | m=video 9 UDP/TLS/RTP/SAVPF 96
31 | c=IN IP4 0.0.0.0
32 | a=rtcp:9 IN IP4 0.0.0.0
33 | a=ice-ufrag:RxBw
34 | a=ice-pwd:fy2QR48Q60jSkViidvOVg6Oa
35 | a=ice-options:trickle
36 | a=fingerprint:sha-256 7B:65:24:37:0B:A4:6B:90:56:E7:61:40:14:24:06:32:F2:D2:45:E7:77:DD:D9:5B:BF:CA:6A:27:5D:1F:12:6D
37 | a=setup:actpass
38 | a=mid:1
39 | a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
40 | a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
41 | a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
42 | a=sendonly
43 | a=msid:- 4441187a-e2e9-43b9-95d9-62205f52a73e
44 | a=rtcp-mux
45 | a=rtcp-rsize
46 | a=rtpmap:96 VP8/90000
47 | a=rtcp-fb:96 goog-remb
48 | a=rtcp-fb:96 transport-cc
49 | a=rtcp-fb:96 ccm fir
50 | a=rtcp-fb:96 nack
51 | a=rtcp-fb:96 nack pli
52 | a=rid:l send
53 | a=rid:m send
54 | a=rid:h send
55 | a=simulcast:send l;m;~h
56 |
--------------------------------------------------------------------------------
/webrtc/test/fixtures/2_outgoing_tracks_sdp.txt:
--------------------------------------------------------------------------------
1 | v=0
2 | o=- 2466309757551448993 2 IN IP4 127.0.0.1
3 | s=-
4 | t=0 0
5 | a=group:BUNDLE 0 1 2 3
6 | a=extmap-allow-mixed
7 | a=msid-semantic: WMS
8 | m=audio 9 UDP/TLS/RTP/SAVPF 111
9 | c=IN IP4 0.0.0.0
10 | a=rtcp:9 IN IP4 0.0.0.0
11 | a=ice-ufrag:ZCxs
12 | a=ice-pwd:dGx5puvwZ0uiI20ScIPz2xuC
13 | a=ice-options:trickle
14 | a=fingerprint:sha-256 98:DD:1C:34:B4:52:ED:6D:E1:DA:51:22:F6:26:8B:1A:34:39:00:64:C2:FE:B6:55:FB:7E:63:0B:B5:B9:49:3F
15 | a=setup:actpass
16 | a=mid:2
17 | a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
18 | a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
19 | a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
20 | a=recvonly
21 | a=rtcp-mux
22 | a=rtpmap:111 opus/48000/2
23 | a=rtcp-fb:111 transport-cc
24 | a=fmtp:111 minptime=10;useinbandfec=1
25 | m=video 9 UDP/TLS/RTP/SAVPF 96
26 | c=IN IP4 0.0.0.0
27 | a=rtcp:9 IN IP4 0.0.0.0
28 | a=ice-ufrag:ZCxs
29 | a=ice-pwd:dGx5puvwZ0uiI20ScIPz2xuC
30 | a=ice-options:trickle
31 | a=fingerprint:sha-256 98:DD:1C:34:B4:52:ED:6D:E1:DA:51:22:F6:26:8B:1A:34:39:00:64:C2:FE:B6:55:FB:7E:63:0B:B5:B9:49:3F
32 | a=setup:actpass
33 | a=mid:3
34 | a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
35 | a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
36 | a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
37 | a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
38 | a=recvonly
39 | a=rtcp-mux
40 | a=rtcp-rsize
41 | a=rtpmap:96 VP8/90000
42 | a=rtcp-fb:96 goog-remb
43 | a=rtcp-fb:96 transport-cc
44 | a=rtcp-fb:96 ccm fir
45 | a=rtcp-fb:96 nack
46 | a=rtcp-fb:96 nack pli
47 |
--------------------------------------------------------------------------------
/webrtc/test/fixtures/README.md:
--------------------------------------------------------------------------------
1 | # Membrane WebRTC Plugin Test Fixtures
2 |
3 | List of fixtures used in tests.
4 |
5 | SDP offers/answers where generated using [`membrane_videoroom`](https://github.com/membraneframework/membrane_videoroom).
6 |
7 | * 2_incoming_tracks_sdp.txt - SDP offer containing two incoming (i.e. sent by the offerer to the peer) tracks:
8 | 1 audio and 1 video. It was simplified to contain only one codec for m-line.
9 | * 2_outgoing_tracks_sdp.txt - SDP offer containing two outgoing (i.e. received by the offerer) tracks:
10 | 1 audio and 1 video. It was simplified to contain only one codec for m-line.
--------------------------------------------------------------------------------
/webrtc/test/support/test_connection_allocator.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.RTC.Engine.Endpoint.WebRTC.TestConnectionAllocator do
2 | @moduledoc false
3 | @behaviour Membrane.RTC.Engine.Endpoint.WebRTC.ConnectionAllocator
4 |
5 | @impl true
6 | def create(), do: {:ok, self()}
7 |
8 | @impl true
9 | def destroy(_pid), do: :ok
10 |
11 | @impl true
12 | def register_track_receiver(allocator, bandwidth, track, _options \\ []) do
13 | send(allocator, {:register_tr, self(), bandwidth, track})
14 | :ok
15 | end
16 |
17 | @impl true
18 | def request_allocation(allocator, allocation) do
19 | send(allocator, {:request_allocation, self(), allocation})
20 | :ok
21 | end
22 |
23 | @impl true
24 | def update_bandwidth_estimation(_allocator, _estimation) do
25 | raise "Unimplemented!"
26 | end
27 |
28 | @impl true
29 | def buffer_sent(_allocator, _buffer) do
30 | :ok
31 | end
32 |
33 | @impl true
34 | def set_negotiability_status(_allocator, _value) do
35 | raise "unimplemented!"
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/webrtc/test/support/utils.ex:
--------------------------------------------------------------------------------
1 | defmodule Membrane.RTC.Engine.Support.Utils do
2 | @moduledoc false
3 |
4 | alias Membrane.Buffer
5 | alias Membrane.WebRTC.Track
6 |
7 | @spec generate_random_payload(non_neg_integer) :: binary()
8 | def generate_random_payload(size) when size >= 0 do
9 | for _i <- 1..size, into: <<>>, do: <