diff --git a/.gitignore b/.gitignore index ebd22a2c..4346c06e 100644 --- a/.gitignore +++ b/.gitignore @@ -184,3 +184,7 @@ $RECYCLE.BIN/ # Default Jellyfish output directory jellyfish_output + + +# asdf +.tool-versions \ No newline at end of file diff --git a/lib/jellyfish/component.ex b/lib/jellyfish/component.ex index 34861b23..fe43184b 100644 --- a/lib/jellyfish/component.ex +++ b/lib/jellyfish/component.ex @@ -29,7 +29,7 @@ defmodule Jellyfish.Component do @type t :: %__MODULE__{ id: id(), type: component(), - engine_endpoint: Membrane.ChildrenSpec.child_definition_t() + engine_endpoint: Membrane.ChildrenSpec.child_definition() } @spec parse_type(String.t()) :: {:ok, component()} | {:error, :invalid_type} diff --git a/lib/jellyfish/component/hls.ex b/lib/jellyfish/component/hls.ex index 4cc62880..342d6215 100644 --- a/lib/jellyfish/component/hls.ex +++ b/lib/jellyfish/component/hls.ex @@ -6,7 +6,11 @@ defmodule Jellyfish.Component.HLS do @behaviour Jellyfish.Endpoint.Config alias Membrane.RTC.Engine.Endpoint.HLS - alias Membrane.RTC.Engine.Endpoint.HLS.HLSConfig + alias Membrane.RTC.Engine.Endpoint.HLS.{CompositorConfig, HLSConfig, MixerConfig} + alias Membrane.Time + + @segment_duration Time.seconds(4) + @partial_segment_duration Time.milliseconds(400) @impl true def config(options) do @@ -18,8 +22,24 @@ defmodule Jellyfish.Component.HLS do rtc_engine: options.engine_pid, owner: self(), output_directory: output_dir, - mixer_config: nil, - hls_config: %HLSConfig{} + mixer_config: %MixerConfig{ + video: %CompositorConfig{ + stream_format: %Membrane.RawVideo{ + width: 1920, + height: 1080, + pixel_format: :I420, + framerate: {24, 1}, + aligned: true + } + } + }, + hls_config: %HLSConfig{ + hls_mode: :muxed_av, + mode: :live, + target_window_duration: :infinity, + segment_duration: @segment_duration, + partial_segment_duration: @partial_segment_duration + } }} end end diff --git a/lib/jellyfish/endpoint/config.ex b/lib/jellyfish/endpoint/config.ex index 6e370865..a060595b 100644 --- a/lib/jellyfish/endpoint/config.ex +++ b/lib/jellyfish/endpoint/config.ex @@ -3,5 +3,5 @@ defmodule Jellyfish.Endpoint.Config do An interface for RTC Engine endpoint configuration. """ - @callback config(map()) :: {:ok, Membrane.ChildrenSpec.child_definition_t()} | {:error, term()} + @callback config(map()) :: {:ok, Membrane.ChildrenSpec.child_definition()} | {:error, term()} end diff --git a/lib/jellyfish/peer.ex b/lib/jellyfish/peer.ex index 97acbc7e..5a55a1b9 100644 --- a/lib/jellyfish/peer.ex +++ b/lib/jellyfish/peer.ex @@ -28,7 +28,7 @@ defmodule Jellyfish.Peer do type: peer(), status: status(), socket_pid: pid() | nil, - engine_endpoint: Membrane.ChildrenSpec.child_definition_t() + engine_endpoint: Membrane.ChildrenSpec.child_definition() } @spec parse_type(String.t()) :: {:ok, peer()} | {:error, :invalid_type} diff --git a/lib/jellyfish/peer/webrtc.ex b/lib/jellyfish/peer/webrtc.ex index 61e04b2a..0f815ed8 100644 --- a/lib/jellyfish/peer/webrtc.ex +++ b/lib/jellyfish/peer/webrtc.ex @@ -8,6 +8,7 @@ defmodule Jellyfish.Peer.WebRTC do alias Membrane.RTC.Engine.Endpoint.WebRTC alias Membrane.RTC.Engine.Endpoint.WebRTC.SimulcastConfig alias Membrane.WebRTC.Extension.{Mid, RepairedRid, Rid, TWCC} + alias Membrane.WebRTC.Track.Encoding @impl true def config(options) do @@ -26,6 +27,18 @@ defmodule Jellyfish.Peer.WebRTC do webrtc_extensions = [Mid, Rid, RepairedRid, TWCC] network_options = options.network_options + filter_codecs = + case options.enforce_encoding do + :h264 -> + &filter_codecs_h264/1 + + :vp8 -> + &filter_codecs_vp8/1 + + nil -> + &any_codecs/1 + end + {:ok, %WebRTC{ rtc_engine: options.engine_pid, @@ -34,6 +47,7 @@ defmodule Jellyfish.Peer.WebRTC do integrated_turn_options: network_options[:integrated_turn_options], integrated_turn_domain: network_options[:integrated_turn_domain], handshake_opts: handshake_options, + filter_codecs: filter_codecs, log_metadata: [peer_id: options.peer_id], trace_context: nil, webrtc_extensions: webrtc_extensions, @@ -43,4 +57,27 @@ defmodule Jellyfish.Peer.WebRTC do } }} end + + defp filter_codecs_h264(%Encoding{name: "H264", format_params: fmtp}) do + import Bitwise + + # Only accept constrained baseline + # based on RFC 6184, Table 5. + case fmtp.profile_level_id >>> 16 do + 0x42 -> (fmtp.profile_level_id &&& 0x00_4F_00) == 0x00_40_00 + 0x4D -> (fmtp.profile_level_id &&& 0x00_8F_00) == 0x00_80_00 + 0x58 -> (fmtp.profile_level_id &&& 0x00_CF_00) == 0x00_C0_00 + _otherwise -> false + end + end + + defp filter_codecs_h264(encoding), do: filter_codecs(encoding) + + defp filter_codecs_vp8(%Encoding{name: "VP8"}), do: true + defp filter_codecs_vp8(encoding), do: filter_codecs(encoding) + + defp any_codecs(_encoding), do: true + + defp filter_codecs(%Encoding{name: "opus"}), do: true + defp filter_codecs(_rtp_mapping), do: false end diff --git a/lib/jellyfish/room.ex b/lib/jellyfish/room.ex index d2f2ba70..b20676b5 100644 --- a/lib/jellyfish/room.ex +++ b/lib/jellyfish/room.ex @@ -24,6 +24,7 @@ defmodule Jellyfish.Room do @type id :: String.t() @type max_peers :: non_neg_integer() | nil + @type enforce_encoding :: :h264 | :vp8 | nil @typedoc """ This module contains: @@ -35,17 +36,24 @@ defmodule Jellyfish.Room do """ @type t :: %__MODULE__{ id: id(), - config: %{max_peers: max_peers(), simulcast?: boolean()}, + config: %{ + max_peers: max_peers(), + enforce_encoding: enforce_encoding(), + simulcast?: boolean() + }, components: %{Component.id() => Component.t()}, peers: %{Peer.id() => Peer.t()}, engine_pid: pid(), network_options: map() } - @spec start(max_peers()) :: {:ok, pid(), id()} - def start(max_peers) do + @spec start(max_peers(), enforce_encoding()) :: {:ok, pid(), id()} + def start(max_peers, enforce_encoding) do id = UUID.uuid4() - {:ok, pid} = GenServer.start(__MODULE__, [id, max_peers], name: registry_id(id)) + + {:ok, pid} = + GenServer.start(__MODULE__, [id, max_peers, enforce_encoding], name: registry_id(id)) + {:ok, pid, id} end @@ -103,8 +111,8 @@ defmodule Jellyfish.Room do end @impl true - def init([id, max_peers]) do - state = new(id, max_peers) + def init([id, max_peers, enforce_encoding]) do + state = new(id, max_peers, enforce_encoding) Logger.metadata(room_id: id) Logger.info("Initialize room") @@ -122,7 +130,12 @@ defmodule Jellyfish.Room do if Enum.count(state.peers) == state.config.max_peers do {{:error, :reached_peers_limit}, state} else - options = %{engine_pid: state.engine_pid, network_options: state.network_options} + options = %{ + engine_pid: state.engine_pid, + network_options: state.network_options, + enforce_encoding: state.config.enforce_encoding + } + peer = Peer.new(peer_type, options) state = put_in(state, [:peers, peer.id], peer) @@ -306,7 +319,7 @@ defmodule Jellyfish.Room do {:noreply, state} end - defp new(id, max_peers) do + defp new(id, max_peers, enforce_encoding) do rtc_engine_options = [ id: id ] @@ -337,7 +350,7 @@ defmodule Jellyfish.Room do %__MODULE__{ id: id, - config: %{max_peers: max_peers}, + config: %{max_peers: max_peers, enforce_encoding: enforce_encoding}, engine_pid: pid, network_options: [integrated_turn_options: integrated_turn_options] } diff --git a/lib/jellyfish/room_service.ex b/lib/jellyfish/room_service.ex index cf38c953..cc72d548 100644 --- a/lib/jellyfish/room_service.ex +++ b/lib/jellyfish/room_service.ex @@ -54,9 +54,10 @@ defmodule Jellyfish.RoomService do |> Enum.reject(&(&1 == nil)) end - @spec create_room(Room.max_peers()) :: {:ok, Room.t()} | {:error, :bad_arg} - def create_room(max_peers) do - GenServer.call(__MODULE__, {:create_room, max_peers}) + @spec create_room(Room.max_peers(), Room.enforce_encoding()) :: + {:ok, Room.t()} | {:error, :invalid_max_peers | :invalid_enforce_encoding} + def create_room(max_peers, enforce_encoding) do + GenServer.call(__MODULE__, {:create_room, max_peers, enforce_encoding}) end @spec delete_room(Room.id()) :: :ok | {:error, :room_not_found} @@ -70,24 +71,32 @@ defmodule Jellyfish.RoomService do end @impl true - def handle_call({:create_room, max_peers}, _from, state) - when is_nil(max_peers) or (is_integer(max_peers) and max_peers >= 0) do - {:ok, room_pid, room_id} = Room.start(max_peers) - room = Room.get_state(room_id) - Process.monitor(room_pid) + def handle_call({:create_room, max_peers, enforce_encoding}, _from, state) do + with :ok <- validate_max_peers(max_peers), + {:ok, enforce_encoding} <- encoding_to_atom(enforce_encoding), + {:ok, room_pid, room_id} <- Room.start(max_peers, enforce_encoding) do + room = Room.get_state(room_id) + Process.monitor(room_pid) - state = put_in(state, [:rooms, room_pid], room_id) + state = put_in(state, [:rooms, room_pid], room_id) - Logger.info("Created room #{inspect(room.id)}") + Logger.info("Created room #{inspect(room.id)}") - Phoenix.PubSub.broadcast(Jellyfish.PubSub, "server_notification", {:room_created, room_id}) + Phoenix.PubSub.broadcast( + Jellyfish.PubSub, + "server_notification", + {:room_created, room_id} + ) - {:reply, {:ok, room}, state} - end + {:reply, {:ok, room}, state} + else + {:error, :max_peers} -> + {:reply, {:error, :invalid_max_peers}, state} - @impl true - def handle_call({:create_room, _max_peers}, _from, state), - do: {:reply, {:error, :bad_arg}, state} + {:error, :enforce_encoding} -> + {:reply, {:error, :invalid_enforce_encoding}, state} + end + end @impl true def handle_call({:delete_room, room_id}, _from, state) do @@ -140,4 +149,13 @@ defmodule Jellyfish.RoomService do Logger.warn("Room process with id #{inspect(room_id)} doesn't exist") end end + + defp validate_max_peers(nil), do: :ok + defp validate_max_peers(max_peers) when is_integer(max_peers) and max_peers >= 0, do: :ok + defp validate_max_peers(_max_peers), do: {:error, :max_peers} + + defp encoding_to_atom("h264"), do: {:ok, :h264} + defp encoding_to_atom("vp8"), do: {:ok, :vp8} + defp encoding_to_atom(nil), do: {:ok, nil} + defp encoding_to_atom(_encoding), do: {:error, :enforce_encoding} end diff --git a/lib/jellyfish_web/api_spec/room.ex b/lib/jellyfish_web/api_spec/room.ex index 363ba99b..9bef301e 100644 --- a/lib/jellyfish_web/api_spec/room.ex +++ b/lib/jellyfish_web/api_spec/room.ex @@ -20,6 +20,12 @@ defmodule JellyfishWeb.ApiSpec.Room do example: 10, description: "Maximum amount of peers allowed into the room", nullable: true + }, + enforceEncoding: %Schema{ + description: "Enforces video codec for each peer in the room", + type: :string, + enum: ["h264", "vp8"], + nullable: true } } }) diff --git a/lib/jellyfish_web/controllers/room_controller.ex b/lib/jellyfish_web/controllers/room_controller.ex index 5b4bb3a5..7a0fcb0f 100644 --- a/lib/jellyfish_web/controllers/room_controller.ex +++ b/lib/jellyfish_web/controllers/room_controller.ex @@ -68,13 +68,18 @@ defmodule JellyfishWeb.RoomController do def create(conn, params) do with max_peers <- Map.get(params, "maxPeers"), - {:ok, room} <- RoomService.create_room(max_peers) do + enforce_encoding <- Map.get(params, "enforceEncoding"), + {:ok, room} <- RoomService.create_room(max_peers, enforce_encoding) do conn |> put_resp_content_type("application/json") |> put_status(:created) |> render("show.json", room: room) else - {:error, :bad_arg} -> {:error, :bad_request, "maxPeers must be a number"} + {:error, :invalid_max_peers} -> + {:error, :bad_request, "maxPeers must be a number"} + + {:error, :invalid_enforce_encoding} -> + {:error, :bad_request, "enforceEncoding must be 'h264' or 'vp8'"} end end diff --git a/lib/jellyfish_web/server_socket.ex b/lib/jellyfish_web/server_socket.ex index d2cea572..9923165f 100644 --- a/lib/jellyfish_web/server_socket.ex +++ b/lib/jellyfish_web/server_socket.ex @@ -229,7 +229,10 @@ defmodule JellyfishWeb.ServerSocket do } ) - config = struct!(RoomState.Config, room.config) + config = + room.config + |> Map.update!(:enforce_encoding, &to_proto_encoding/1) + |> then(&struct!(RoomState.Config, &1)) %RoomState{id: room.id, config: config, peers: peers, components: components} end @@ -238,6 +241,10 @@ defmodule JellyfishWeb.ServerSocket do defp to_proto_type(Jellyfish.Component.RTSP), do: :TYPE_RTSP defp to_proto_type(Jellyfish.Peer.WebRTC), do: :TYPE_WEBRTC + defp to_proto_encoding(:h264), do: :ENCODING_H264 + defp to_proto_encoding(:vp8), do: :ENCODING_VP8 + defp to_proto_encoding(nil), do: :ENCODING_UNSPECIFIED + defp to_proto_status(:disconnected), do: :STATUS_DISCONNECTED defp to_proto_status(:connected), do: :STATUS_CONNECTED end diff --git a/lib/protos/jellyfish/server_notifications.pb.ex b/lib/protos/jellyfish/server_notifications.pb.ex index 995616a3..e951d375 100644 --- a/lib/protos/jellyfish/server_notifications.pb.ex +++ b/lib/protos/jellyfish/server_notifications.pb.ex @@ -7,6 +7,16 @@ defmodule Jellyfish.ServerMessage.SubscribeRequest.ServerNotification.Option do field :OPTION_ALL, 1 end +defmodule Jellyfish.ServerMessage.SubscriptionResponse.RoomState.Config.Encoding do + @moduledoc false + + use Protobuf, enum: true, protoc_gen_elixir_version: "0.12.0", syntax: :proto3 + + field :ENCODING_UNSPECIFIED, 0 + field :ENCODING_H264, 1 + field :ENCODING_VP8, 2 +end + defmodule Jellyfish.ServerMessage.SubscriptionResponse.RoomState.Peer.Type do @moduledoc false @@ -130,6 +140,11 @@ defmodule Jellyfish.ServerMessage.SubscriptionResponse.RoomState.Config do use Protobuf, protoc_gen_elixir_version: "0.12.0", syntax: :proto3 field :max_peers, 1, type: :uint32, json_name: "maxPeers" + + field :enforce_encoding, 2, + type: Jellyfish.ServerMessage.SubscriptionResponse.RoomState.Config.Encoding, + json_name: "enforceEncoding", + enum: true end defmodule Jellyfish.ServerMessage.SubscriptionResponse.RoomState.Peer do diff --git a/mix.exs b/mix.exs index 288f45ff..670473e6 100644 --- a/mix.exs +++ b/mix.exs @@ -59,27 +59,24 @@ defmodule Jellyfish.MixProject do {:protobuf, "~> 0.10.0"}, # Membrane deps - {:membrane_rtc_engine, "~> 0.14.0"}, - {:membrane_ice_plugin, "~> 0.15.0"}, + {:membrane_rtc_engine, "~> 0.15.0"}, + {:membrane_ice_plugin, "~> 0.16.0"}, # HLS endpoints deps - {:membrane_aac_plugin, "~> 0.13.0"}, - {:membrane_opus_plugin, "~> 0.16.0"}, - {:membrane_aac_fdk_plugin, "~> 0.14.0"}, - {:membrane_generator_plugin, "~> 0.8.0"}, - {:membrane_realtimer_plugin, "~> 0.6.0"}, - {:membrane_audio_mix_plugin, "~> 0.13.0"}, - {:membrane_raw_audio_format, "~> 0.10.0"}, - {:membrane_h264_ffmpeg_plugin, "~> 0.26.2"}, - {:membrane_h264_plugin, "~> 0.2.0"}, - {:membrane_audio_filler_plugin, "~> 0.1.0"}, - {:membrane_video_compositor_plugin, "~> 0.3.1"}, - {:membrane_http_adaptive_stream_plugin, "~> 0.14.0"}, + {:membrane_aac_plugin, "~> 0.15.0"}, + {:membrane_opus_plugin, "~> 0.17.1"}, + {:membrane_aac_fdk_plugin, "~> 0.15.1"}, + {:membrane_audio_mix_plugin, "~> 0.15.2"}, + {:membrane_raw_audio_format, "~> 0.11.0"}, + {:membrane_h264_ffmpeg_plugin, "~> 0.27.0"}, + {:membrane_h264_plugin, "~> 0.4.0"}, + {:membrane_video_compositor_plugin, "~> 0.5.1"}, + {:membrane_http_adaptive_stream_plugin, "~> 0.15.0"}, # RTSP endpoints deps {:connection, "~> 1.1"}, {:membrane_rtsp, "~> 0.5.0"}, - {:membrane_udp_plugin, "~> 0.9.2"}, + {:membrane_udp_plugin, "~> 0.10.0"}, # Dialyzer and credo {:dialyxir, ">= 0.0.0", only: :dev, runtime: false}, diff --git a/mix.lock b/mix.lock index 541d90d7..309896fd 100644 --- a/mix.lock +++ b/mix.lock @@ -12,7 +12,7 @@ "cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, - "crc": {:hex, :crc, "0.10.4", "06f5f54e2ec2954968703dcd37d7a4c65cee7a5305c48a23c509dc20a5469d4f", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "90bdd5b5ac883f0b1860692324ed3f53fdaa6f1e8483771873fea07e71def91d"}, + "crc": {:hex, :crc, "0.10.5", "ee12a7c056ac498ef2ea985ecdc9fa53c1bfb4e53a484d9f17ff94803707dfd8", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3e673b6495a9525c5c641585af1accba59a1eb33de697bedf341e247012c2c7f"}, "credo": {:hex, :credo, "1.7.0", "6119bee47272e85995598ee04f2ebbed3e947678dee048d10b5feca139435f75", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6839fcf63d1f0d1c0f450abc8564a57c43d644077ab96f2934563e68b8a769d7"}, "dialyxir": {:hex, :dialyxir, "1.3.0", "fd1672f0922b7648ff9ce7b1b26fcf0ef56dda964a459892ad15f6b4410b5284", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "00b2a4bcd6aa8db9dcb0b38c1225b7277dca9bc370b6438715667071a304696f"}, "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, @@ -27,45 +27,42 @@ "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~> 2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, "heap": {:hex, :heap, "2.0.2", "d98cb178286cfeb5edbcf17785e2d20af73ca57b5a2cf4af584118afbcf917eb", [:mix], [], "hexpm", "ba9ea2fe99eb4bcbd9a8a28eaf71cbcac449ca1d8e71731596aace9028c9d429"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, - "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, - "membrane_aac_fdk_plugin": {:hex, :membrane_aac_fdk_plugin, "0.14.0", "cb28755af184b1fe850fe8fc8e25d57c7a8508c902eb878f5ac08fe94c2210c7", [:mix], [{:bunch, "~> 1.4", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_aac_format, "~> 0.7", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.14.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.10.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "82ade303234aa36c37e517a7cd8e3d19cb1c27787b4a7b4ea0c514550ccd789b"}, + "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, + "membrane_aac_fdk_plugin": {:hex, :membrane_aac_fdk_plugin, "0.15.1", "35f70afcafdb27e5ea4bb67f3e5dbfd4a0ab4686c6fda7b2c422385fadabc4d2", [:mix], [{:bunch, "~> 1.4", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_aac_format, "~> 0.7", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.15.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.11.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "f48c3e3b1150cd87fae03a0e2f5fee6c77d32521f97a3f23a1620842ce5a64c4"}, "membrane_aac_format": {:hex, :membrane_aac_format, "0.7.0", "93ea75b57d5cbee5db59f0250baba65a8949d32ee4cfa38224e93ea306c30048", [:mix], [{:bimap, "~> 1.1", [hex: :bimap, repo: "hexpm", optional: false]}], "hexpm", "3233d2a5a1b264039a198abcef3b7f1c64f5bde9704ec01afbf1bab43a56d415"}, - "membrane_aac_plugin": {:hex, :membrane_aac_plugin, "0.13.0", "ea3bd7105abca13897be6da5ef325e9dfa55e6b83b83cee7b71d5703f108344f", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:crc, "~> 0.10.2", [hex: :crc, repo: "hexpm", optional: false]}, {:membrane_aac_format, "~> 0.7.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "91bc75536319b1a5c962bb5c211811898553c10a2210da905de035227e9f279f"}, - "membrane_audio_filler_plugin": {:hex, :membrane_audio_filler_plugin, "0.1.0", "5a1cdd4bb00dad1b7dde0a9b45a73924d99379a41df8859f58151dc57d968bca", [:mix], [{:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.10.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}], "hexpm", "eac8123e60584769f8d72963e39e5e73559ac47771edd8043c2f2f6f0ac4a844"}, - "membrane_audio_mix_plugin": {:hex, :membrane_audio_mix_plugin, "0.13.0", "b7bf0fed36c86da3b4afef5fc3929da1432882bf902f18b4df643b9fd282ae66", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.14.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.2", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.10.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "01e86cf695c04c15a5de4ae2d66acfd6dd810c5f0332a5f2ae4de2b8e20d5c12"}, + "membrane_aac_plugin": {:hex, :membrane_aac_plugin, "0.15.0", "070367d3d2e5f5279cd52eb5511bafea7d7a3cf4f14229f0eb66978ffaae0451", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:crc, "~> 0.10.2", [hex: :crc, repo: "hexpm", optional: false]}, {:membrane_aac_format, "~> 0.7.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "0ba6e70c484a65c6264cdfa70c974c417eaa611341e16136d5ba02991fdda868"}, + "membrane_audio_mix_plugin": {:hex, :membrane_audio_mix_plugin, "0.15.2", "6f52ccf1a052115f509520fe24261b547121e87a4c5e55b7245c0aabbfcb5ddc", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.15.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.1", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.11.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "06c29679b2ec997597519665d5f2c621800003fe8f0069b2e478a4771b3e44ba"}, "membrane_cmaf_format": {:hex, :membrane_cmaf_format, "0.6.1", "89d130b5f8786d4285d395697b0f7763a2c82a02de1658cdeb4f8e37e6a6c85c", [:mix], [], "hexpm", "e916e3c8216f3bf999b069ffda94da48c9bdbe3181ce7155a458d1ccf1a97b3d"}, - "membrane_common_c": {:hex, :membrane_common_c, "0.14.0", "35621d9736829bf675062dc0af66e931b0d82ff8361c2088576d63d4d002692e", [:mix], [{:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:shmex, "~> 0.5.0", [hex: :shmex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "262b06e93fe3f1be57a111fa19f5cba4f26000a442ac32a59f3678e83cbb001f"}, - "membrane_core": {:hex, :membrane_core, "0.11.4", "1edfb8ffb1fca7f6ceff36fb51b89f02cb9a7f5ee3ee994f729b2b3ff8c5b831", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.3", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 2.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3626c29ffdfaf40514fc2bf9c62f41b52bd2088568bbcb23206ffec1d45a1f92"}, - "membrane_file_plugin": {:hex, :membrane_file_plugin, "0.13.3", "aaf40a72e5fccf6da47ec85ef525234acbec828425f1300f74c464bca1487f40", [:mix], [{:membrane_core, "~> 0.11", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "1c1acf610d4fc0279b7fd65a5db06c8bc3ef6a276bf40fb033c2c735c25839ba"}, - "membrane_framerate_converter_plugin": {:hex, :membrane_framerate_converter_plugin, "0.6.1", "bcfa2c7fda2b3fa285a8eb6949b85a4223551037300e726de7e88c4c5683c1e0", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.2", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:ratio, "~> 2.4.2", [hex: :ratio, repo: "hexpm", optional: false]}], "hexpm", "0113f56f8fa47b6c2b68c6c4cb38c00833dc7df692ec6edf26e94202b0f6b715"}, - "membrane_funnel_plugin": {:hex, :membrane_funnel_plugin, "0.7.0", "f633a32a7549923a5b15f9afe8aede111b51f1ff9a03e8bb134f5cd54a6a2237", [:mix], [{:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "8f08686cef36ea71cd2bf820163db729a821d5c10f6441c02b24476d6ed639d4"}, - "membrane_generator_plugin": {:hex, :membrane_generator_plugin, "0.8.1", "ec335b98c921b45b81d05bd9ee15a727695c2951fa0bf2dd252a138447c31d00", [:mix], [{:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.10.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}], "hexpm", "d4a8173d458cb01a78ee6397121c0accaddd210beee2986479c103939ed92270"}, - "membrane_h264_ffmpeg_plugin": {:hex, :membrane_h264_ffmpeg_plugin, "0.26.2", "ec916f86f059ca32a626153cf19ab024990d4aeb86122235c6c75a18a9dd5f10", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.14.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.5.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:ratio, "~> 2.4.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "7a7e5511bc7f2de03e861d8c068e67d83c400986be4e2f6655f508de35f6bec9"}, + "membrane_common_c": {:hex, :membrane_common_c, "0.15.0", "4b6005c562bf025e4a53c95a9646a9f5fa993ac440dd44c1a4d1ea210ec53793", [:mix], [{:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:shmex, "~> 0.5.0", [hex: :shmex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "f9584cca9865ed754b8333e362d49d6c449c708d7c87be6c5f7bd5a1d978d6bf"}, + "membrane_core": {:hex, :membrane_core, "0.12.5", "098733b1677bd9235a74fc3dabcdff5459d879466c7dc4b862e485c161153947", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.3", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 2.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "59ecef38a9c184a41810d23dfc77b899717538dd0a177bdbd57383c199f7675b"}, + "membrane_file_plugin": {:hex, :membrane_file_plugin, "0.14.0", "87f19f5f5afbfbaf2219b8f1d8496534cb9ad01fca74687910bf3f7aa866e244", [:mix], [{:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "28956f8d5d87735d499c57f1c24f62aeab71e0211863759e7e695ead966eb433"}, + "membrane_framerate_converter_plugin": {:hex, :membrane_framerate_converter_plugin, "0.7.0", "f1a12b914dde380f43ca83363431ed3c743cf20320afe6011f0f77e97d1d867f", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:ratio, "~> 2.4.2", [hex: :ratio, repo: "hexpm", optional: false]}], "hexpm", "6c552f839f047d392adec7ce06ee6c720ef0561d6a766ff7087df7950501b835"}, + "membrane_funnel_plugin": {:hex, :membrane_funnel_plugin, "0.8.0", "fe735a88e4ac815041f3aba0bbfa25297769c4cb8b501f3875809698fe3f8fbf", [:mix], [{:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "61776c7e5b87eaa33eb314d06440cd124246a794482bf0c1b8cd2b796714f420"}, + "membrane_h264_ffmpeg_plugin": {:hex, :membrane_h264_ffmpeg_plugin, "0.27.0", "4fced15b5791b87758869537770d00b39d103d1239ef7311f4a883e036d83d7b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.15.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.5.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:ratio, "~> 2.4.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "031887921970f793d11bfd2e2ed36ba6c3010b17c61a4702bcf50028431786dc"}, "membrane_h264_format": {:hex, :membrane_h264_format, "0.5.0", "95c1cc00896dab6de322cee3be0c9b0b0840537b4cc49e82a34e901b4e4763f9", [:mix], [], "hexpm", "7b44e0b02d86b45d24699de3c970186dffbca6d51aca80bbed2f6a3fe5c9eba1"}, - "membrane_h264_plugin": {:hex, :membrane_h264_plugin, "0.2.1", "d1643644afd45e45f5bd9b47e52fa02c6e17a6371d3045417096f6a2dea69a13", [:mix], [{:bunch, "~> 1.4", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.5.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}], "hexpm", "875f9ccbee6cada3181b7b54ee2e89a22816e6372dfa9ea190725dc7908a57fe"}, - "membrane_http_adaptive_stream_plugin": {:hex, :membrane_http_adaptive_stream_plugin, "0.14.0", "431d053b772aa306dd452865fcc6593c8dfe58694d8e3e6fef2552f5f4f77c28", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_cmaf_format, "~> 0.6.1", [hex: :membrane_cmaf_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.3", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_mp4_plugin, "~> 0.21.0", [hex: :membrane_mp4_plugin, repo: "hexpm", optional: false]}, {:membrane_tee_plugin, "~> 0.10.1", [hex: :membrane_tee_plugin, repo: "hexpm", optional: false]}], "hexpm", "5c538065b1ee749146ea811c06805fb317c2c32f2966284683c56e6b8a13d2b8"}, - "membrane_ice_plugin": {:hex, :membrane_ice_plugin, "0.15.1", "34190bc0be340e1c8978068874c05164517f66b33588750f4455569a47969c57", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:ex_dtls, "~> 0.12.0", [hex: :ex_dtls, repo: "hexpm", optional: false]}, {:fake_turn, "~> 0.4.0", [hex: :fake_turn, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_funnel_plugin, "~> 0.7.0", [hex: :membrane_funnel_plugin, repo: "hexpm", optional: false]}, {:membrane_opentelemetry, "~> 0.1.0", [hex: :membrane_opentelemetry, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.6.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}, {:membrane_telemetry_metrics, "~> 0.1.0", [hex: :membrane_telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "11babf966882e83dd3423818b69cc4b4bdaea5ed62b5f8589df9c285e4a69a8d"}, + "membrane_h264_plugin": {:hex, :membrane_h264_plugin, "0.4.1", "73f4019a298091605fc34f21296328cae81d1223991ae4cb1aae96509f6e20b9", [:mix], [{:bunch, "~> 1.4", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.5.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}], "hexpm", "4593d6f43e4a4829dd35c1d7e5405c411b42aa0a11fd0548e337852407ad3118"}, + "membrane_http_adaptive_stream_plugin": {:hex, :membrane_http_adaptive_stream_plugin, "0.15.0", "f1f1084b7c9a1a227da4bf31c3637d3cadc2d111e830f34f442faaf6d776539d", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_cmaf_format, "~> 0.6.1", [hex: :membrane_cmaf_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.3", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_mp4_plugin, "~> 0.24.1", [hex: :membrane_mp4_plugin, repo: "hexpm", optional: false]}, {:membrane_tee_plugin, "~> 0.11.0", [hex: :membrane_tee_plugin, repo: "hexpm", optional: false]}], "hexpm", "7e85d26ad72fbfe171fbd897292feb78bd4396355a075fd79c64d11a5e4fe902"}, + "membrane_ice_plugin": {:hex, :membrane_ice_plugin, "0.16.0", "de694a18760bfd9b06d9e8fa329f95b526aa05be77ade0a6dde69a6fe9cd1ecb", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:ex_dtls, "~> 0.12.0", [hex: :ex_dtls, repo: "hexpm", optional: false]}, {:fake_turn, "~> 0.4.0", [hex: :fake_turn, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_funnel_plugin, "~> 0.8.0", [hex: :membrane_funnel_plugin, repo: "hexpm", optional: false]}, {:membrane_opentelemetry, "~> 0.1.0", [hex: :membrane_opentelemetry, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.7.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}, {:membrane_telemetry_metrics, "~> 0.1.0", [hex: :membrane_telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0999a407c0ccbe434e5dd93274ff0d820a074943d0f94fa5c397cc43667e8fc9"}, "membrane_mp4_format": {:hex, :membrane_mp4_format, "0.7.0", "0cc33f21dc571b43b4d2db66a056e2b7eecdc7ada71a9e0e923ab7a1554f15f2", [:mix], [], "hexpm", "7653a20e7b0c048ea05ffad6df88249abf4c1b63772c14dee843acffcad6b038"}, - "membrane_mp4_plugin": {:hex, :membrane_mp4_plugin, "0.21.0", "d20516cad6152e92ff55b340f3b98f0f26c7a62e1a97d1ce770d6298eb8019d1", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_aac_format, "~> 0.7.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_cmaf_format, "~> 0.6.0", [hex: :membrane_cmaf_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.2", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_file_plugin, "~> 0.13.2", [hex: :membrane_file_plugin, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.5.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_mp4_format, "~> 0.7.0", [hex: :membrane_mp4_format, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}], "hexpm", "d63934b1c8c038668454985797025d4149be825850ecda178624b5dc71b85f78"}, + "membrane_mp4_plugin": {:hex, :membrane_mp4_plugin, "0.24.1", "d6dda76d9811e6c0fc5c8c6f5701920432ef024a2f82973ce7fccdee10cdc9f5", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_aac_format, "~> 0.7.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_cmaf_format, "~> 0.6.0", [hex: :membrane_cmaf_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.3", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_file_plugin, "~> 0.14.0", [hex: :membrane_file_plugin, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.5.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_h264_plugin, "~> 0.4.0", [hex: :membrane_h264_plugin, repo: "hexpm", optional: false]}, {:membrane_mp4_format, "~> 0.7.0", [hex: :membrane_mp4_format, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}], "hexpm", "d0f95edd324b6f0dfeeac70967da10082b156b1c56029c18cfc3860f15009a75"}, "membrane_opentelemetry": {:hex, :membrane_opentelemetry, "0.1.0", "af774bc5b9bad3a822e9a26d8530819b0291b569a282c65a7dd51cc498e6e9cd", [:mix], [{:opentelemetry_api, "~> 1.0.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}], "hexpm", "2e4072a5f14eb95514701e242a7667a7178dfc6afd313d4643ec0726391c243b"}, "membrane_opus_format": {:hex, :membrane_opus_format, "0.3.0", "3804d9916058b7cfa2baa0131a644d8186198d64f52d592ae09e0942513cb4c2", [:mix], [], "hexpm", "8fc89c97be50de23ded15f2050fe603dcce732566fe6fdd15a2de01cb6b81afe"}, - "membrane_opus_plugin": {:hex, :membrane_opus_plugin, "0.16.0", "f84b1644410848c336e8598415589c65e4f8a58c09559a3a7998470de4af4fe2", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.14.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.10.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "f049376cc98c188b70674dea33f7e37224cbd2c39f803cd35393e7074f0c0cca"}, - "membrane_raw_audio_format": {:hex, :membrane_raw_audio_format, "0.10.0", "4ff07002d95b8e9259c38991ead8d98e907ee66346545c168b8a8ad6ae73d87e", [:mix], [{:bimap, "~> 1.1", [hex: :bimap, repo: "hexpm", optional: false]}, {:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "d5b4201d0e634e9fdbc2b9528f1fee245eb667c82ace91c745af6604401003ae"}, + "membrane_opus_plugin": {:hex, :membrane_opus_plugin, "0.17.1", "533d694d8c6f38fbf7f09a514e16037e8980d167899d4c35cb5017b088122ce9", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.15.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.3", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.11.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "8d2bf3d97651f136fd393ce80c099a4f282bbe1dfafde887f8ab18668fd349aa"}, + "membrane_raw_audio_format": {:hex, :membrane_raw_audio_format, "0.11.0", "9b7e8c77f321a3fa1cac2ef157c897938084b704a90ac5450d9f5c87a249b613", [:mix], [{:bimap, "~> 1.1", [hex: :bimap, repo: "hexpm", optional: false]}, {:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "89e0d46893b7cd63d1ab76467d3aae95bd8081e487b18ab0d1679c70d75f7bd8"}, "membrane_raw_video_format": {:hex, :membrane_raw_video_format, "0.3.0", "ba10f475e0814a6fe79602a74536b796047577c7ef5b0e33def27cd344229699", [:mix], [], "hexpm", "2f08760061c8a5386ecf04273480f10e48d25a1a40aa99476302b0bcd34ccb1c"}, - "membrane_realtimer_plugin": {:hex, :membrane_realtimer_plugin, "0.6.1", "2db274f8194bdc6f81d233592af609c5ca27ad2397649313a2c13b7dd426cfd6", [:mix], [{:membrane_core, "~> 0.11", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "685a19abf9292c177ac65329eb0b24736070ca9d2fd1ac094f002db9c12ca9bc"}, - "membrane_rtc_engine": {:hex, :membrane_rtc_engine, "0.14.0", "a6ad337564dd2b11d59b6a849ed5eb4f3016fa93b4c3a465127e07cde5d560d5", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: true]}, {:ex_sdp, "~> 0.11.0", [hex: :ex_sdp, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:membrane_aac_fdk_plugin, "~> 0.14.0", [hex: :membrane_aac_fdk_plugin, repo: "hexpm", optional: true]}, {:membrane_aac_plugin, "~> 0.13.0", [hex: :membrane_aac_plugin, repo: "hexpm", optional: true]}, {:membrane_audio_filler_plugin, "~> 0.1.0", [hex: :membrane_audio_filler_plugin, repo: "hexpm", optional: true]}, {:membrane_audio_mix_plugin, "~> 0.13.0", [hex: :membrane_audio_mix_plugin, repo: "hexpm", optional: true]}, {:membrane_core, "~> 0.11.2", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_file_plugin, "~> 0.13.0", [hex: :membrane_file_plugin, repo: "hexpm", optional: false]}, {:membrane_generator_plugin, "~> 0.8.1", [hex: :membrane_generator_plugin, repo: "hexpm", optional: true]}, {:membrane_h264_ffmpeg_plugin, "~> 0.26.2", [hex: :membrane_h264_ffmpeg_plugin, repo: "hexpm", optional: true]}, {:membrane_h264_plugin, "~> 0.2.0", [hex: :membrane_h264_plugin, repo: "hexpm", optional: true]}, {:membrane_http_adaptive_stream_plugin, "~> 0.14.0", [hex: :membrane_http_adaptive_stream_plugin, repo: "hexpm", optional: true]}, {:membrane_ice_plugin, "~> 0.15.0", [hex: :membrane_ice_plugin, repo: "hexpm", optional: false]}, {:membrane_opentelemetry, "~> 0.1.0", [hex: :membrane_opentelemetry, repo: "hexpm", optional: false]}, {:membrane_opus_plugin, "~> 0.16.0", [hex: :membrane_opus_plugin, repo: "hexpm", optional: true]}, {:membrane_raw_audio_format, "~> 0.10.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: true]}, {:membrane_realtimer_plugin, "~> 0.6.1", [hex: :membrane_realtimer_plugin, repo: "hexpm", optional: true]}, {:membrane_rtp_format, "~> 0.6.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}, {:membrane_rtp_h264_plugin, "~> 0.15.1", [hex: :membrane_rtp_h264_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_opus_plugin, "~> 0.7.0", [hex: :membrane_rtp_opus_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_plugin, "~> 0.22.0", [hex: :membrane_rtp_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_vp8_plugin, "~> 0.7.0", [hex: :membrane_rtp_vp8_plugin, repo: "hexpm", optional: false]}, {:membrane_rtsp, "~> 0.5.0", [hex: :membrane_rtsp, repo: "hexpm", optional: true]}, {:membrane_tee_plugin, "~> 0.10.0", [hex: :membrane_tee_plugin, repo: "hexpm", optional: false]}, {:membrane_telemetry_metrics, "~> 0.1.0", [hex: :membrane_telemetry_metrics, repo: "hexpm", optional: false]}, {:membrane_udp_plugin, "~> 0.9.2", [hex: :membrane_udp_plugin, repo: "hexpm", optional: true]}, {:membrane_video_compositor_plugin, "~> 0.3.1", [hex: :membrane_video_compositor_plugin, repo: "hexpm", optional: true]}, {:membrane_webrtc_plugin, "~> 0.14.6", [hex: :membrane_webrtc_plugin, repo: "hexpm", optional: false]}, {:opentelemetry, "~> 1.0.0", [hex: :opentelemetry, repo: "hexpm", optional: false]}, {:opentelemetry_api, "~> 1.0.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:statistics, "~> 0.6.0", [hex: :statistics, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}, {:uuid, "~> 1.1", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm", "2a30fa8c60411115d572301384d94d8f0dce777aa259bb00334373c6b76631d8"}, - "membrane_rtp_format": {:hex, :membrane_rtp_format, "0.6.0", "666a77a19157899a523d7aa2e5bf69523b92798850f132fee1fc3ced821ca277", [:mix], [{:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "5ca136b9b5fa32992bfa8541dfb1bf00f2f012ac41ab1a63969a9f2b4f559cd0"}, - "membrane_rtp_h264_plugin": {:hex, :membrane_rtp_h264_plugin, "0.15.1", "374c17397018b35d6215ca46b87670f0a10a761bc12c6cb43c29fd9f2ad41ad6", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.5.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.6.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "f461b114ee8dfe7d9b81807d14b191cf0867f4c10155165f40b2a154be697a20"}, - "membrane_rtp_opus_plugin": {:hex, :membrane_rtp_opus_plugin, "0.7.0", "caa874875166f5e5435b7f957c36e96723e0ff415b65b9e1490ae34a1e5bfd5b", [:mix], [{:membrane_core, "~> 0.11.2", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.6.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "df07fc181108fd81fb43e55a7bdfc7c00e4dff45bc5c00963db72169b96e273f"}, - "membrane_rtp_plugin": {:hex, :membrane_rtp_plugin, "0.22.1", "f9c8a0f8b7e3a0f8c68f3ca91c1779f6d06e622c3c156c7b309e85a0614b5808", [:mix], [{:bimap, "~> 1.2", [hex: :bimap, repo: "hexpm", optional: false]}, {:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:ex_libsrtp, "~> 0.6.0", [hex: :ex_libsrtp, repo: "hexpm", optional: true]}, {:heap, "~> 2.0.2", [hex: :heap, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.2", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_funnel_plugin, "~> 0.6", [hex: :membrane_funnel_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.6.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}, {:membrane_telemetry_metrics, "~> 0.1.0", [hex: :membrane_telemetry_metrics, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.1", [hex: :qex, repo: "hexpm", optional: false]}], "hexpm", "9657eefcb42fae25836fd27e94f0928178860ef405a1e5a3c097c4a93767a9d4"}, - "membrane_rtp_vp8_plugin": {:hex, :membrane_rtp_vp8_plugin, "0.7.1", "8a9d41eb53e2af8595fa87994a88030d0eb299cd7976f7b3273a6118553c58d2", [:mix], [{:membrane_core, "~> 0.11.2", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.6.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}, {:membrane_vp8_format, "~> 0.4.0", [hex: :membrane_vp8_format, repo: "hexpm", optional: false]}], "hexpm", "e5fdca517aac0520636c6ce3c709d1eb190d1d266a58c1768cc0c8e1e806a197"}, + "membrane_rtc_engine": {:hex, :membrane_rtc_engine, "0.15.0", "6638f5b955046865c6831feb155c488f5572a4aecea67f62d89838f107b08268", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: true]}, {:ex_sdp, "~> 0.11.0", [hex: :ex_sdp, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:membrane_aac_fdk_plugin, "~> 0.15.1", [hex: :membrane_aac_fdk_plugin, repo: "hexpm", optional: true]}, {:membrane_aac_plugin, "~> 0.15.0", [hex: :membrane_aac_plugin, repo: "hexpm", optional: true]}, {:membrane_audio_mix_plugin, "~> 0.15.2", [hex: :membrane_audio_mix_plugin, repo: "hexpm", optional: true]}, {:membrane_core, "~> 0.12.3", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_file_plugin, "~> 0.14.0", [hex: :membrane_file_plugin, repo: "hexpm", optional: false]}, {:membrane_h264_ffmpeg_plugin, "~> 0.27.0", [hex: :membrane_h264_ffmpeg_plugin, repo: "hexpm", optional: true]}, {:membrane_h264_plugin, "~> 0.4.0", [hex: :membrane_h264_plugin, repo: "hexpm", optional: true]}, {:membrane_http_adaptive_stream_plugin, "~> 0.15.0", [hex: :membrane_http_adaptive_stream_plugin, repo: "hexpm", optional: true]}, {:membrane_ice_plugin, "~> 0.16.0", [hex: :membrane_ice_plugin, repo: "hexpm", optional: false]}, {:membrane_opentelemetry, "~> 0.1.0", [hex: :membrane_opentelemetry, repo: "hexpm", optional: false]}, {:membrane_opus_plugin, "~> 0.17.1", [hex: :membrane_opus_plugin, repo: "hexpm", optional: true]}, {:membrane_raw_audio_format, "~> 0.11.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: true]}, {:membrane_rtp_format, "~> 0.7.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}, {:membrane_rtp_h264_plugin, "~> 0.16.0", [hex: :membrane_rtp_h264_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_opus_plugin, "~> 0.8.0", [hex: :membrane_rtp_opus_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_plugin, "~> 0.23.0", [hex: :membrane_rtp_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_vp8_plugin, "~> 0.8.0", [hex: :membrane_rtp_vp8_plugin, repo: "hexpm", optional: false]}, {:membrane_rtsp, "~> 0.5.0", [hex: :membrane_rtsp, repo: "hexpm", optional: true]}, {:membrane_tee_plugin, "~> 0.11.0", [hex: :membrane_tee_plugin, repo: "hexpm", optional: false]}, {:membrane_telemetry_metrics, "~> 0.1.0", [hex: :membrane_telemetry_metrics, repo: "hexpm", optional: false]}, {:membrane_udp_plugin, "~> 0.10.0", [hex: :membrane_udp_plugin, repo: "hexpm", optional: true]}, {:membrane_video_compositor_plugin, "~> 0.5.1", [hex: :membrane_video_compositor_plugin, repo: "hexpm", optional: true]}, {:membrane_webrtc_plugin, "~> 0.15.0", [hex: :membrane_webrtc_plugin, repo: "hexpm", optional: false]}, {:opentelemetry, "~> 1.0.0", [hex: :opentelemetry, repo: "hexpm", optional: false]}, {:opentelemetry_api, "~> 1.0.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:statistics, "~> 0.6.0", [hex: :statistics, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}, {:uuid, "~> 1.1", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm", "f699ca6db89ab3b9cd989a4bb1f24d7c971c17c94e53fcc3ebbb40215efad1f6"}, + "membrane_rtp_format": {:hex, :membrane_rtp_format, "0.7.0", "c07cca86d420732663b55a9ca046f327737ca87354508c3df829d240c477df25", [:mix], [{:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "2cead4d2c4f9f5c7669677c2cd3921f9f99ec29c06a871f9dffdb97840a1a279"}, + "membrane_rtp_h264_plugin": {:hex, :membrane_rtp_h264_plugin, "0.16.0", "c5781793b1c866335e409f97443f27347d54e1507fb84b5f13dadf0dddaa5f86", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.5.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.7.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "61893b28a02ef62f22abd84628e5d530a76a782e8cedcd6ae8cb8ad4e71d9691"}, + "membrane_rtp_opus_plugin": {:hex, :membrane_rtp_opus_plugin, "0.8.0", "bf53c9135b555fccc70e16cfb2583a858b1fbafe7f2e595e6a7d233c8b78682c", [:mix], [{:membrane_core, "~> 0.12.1", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.7.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "3098224986aff8c22f6f93ccb39a10e774391cf2fccaa4f9635000dbd3212635"}, + "membrane_rtp_plugin": {:hex, :membrane_rtp_plugin, "0.23.0", "82c20663917a06e2f2dd4726c394910a4541107a3634d7fb1136a1a68b411ff2", [:mix], [{:bimap, "~> 1.2", [hex: :bimap, repo: "hexpm", optional: false]}, {:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:ex_libsrtp, "~> 0.6.0", [hex: :ex_libsrtp, repo: "hexpm", optional: true]}, {:heap, "~> 2.0.2", [hex: :heap, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.1", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_funnel_plugin, "~> 0.8.0", [hex: :membrane_funnel_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.7.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}, {:membrane_telemetry_metrics, "~> 0.1.0", [hex: :membrane_telemetry_metrics, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.1", [hex: :qex, repo: "hexpm", optional: false]}], "hexpm", "794568f4336cddb0680e6e2834c4ce88a15b413f86418b47323b52b39836bea3"}, + "membrane_rtp_vp8_plugin": {:hex, :membrane_rtp_vp8_plugin, "0.8.0", "8e005028994a709383883a5172a01d16027c68dffe81be0834c8f82bf95b1201", [:mix], [{:membrane_core, "~> 0.12.1", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.7.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}, {:membrane_vp8_format, "~> 0.4.0", [hex: :membrane_vp8_format, repo: "hexpm", optional: false]}], "hexpm", "c7821dbce02bc5e48c821ca2b9469244e7cf20791cbc268f09cd2f7b8cd18f13"}, "membrane_rtsp": {:hex, :membrane_rtsp, "0.5.0", "29ff82df4596118d104c3a8a5c658266fe4977bcca8accbd4df11aa953a12094", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:ex_sdp, "~> 0.11.0", [hex: :ex_sdp, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "3b218648c8beb21e0bc6ccd15ebb853a92c28f34dfaa3b03ee2e58ae89e44471"}, - "membrane_tee_plugin": {:hex, :membrane_tee_plugin, "0.10.1", "a78aef5f04894495f845f4fdeac855320ae0fe21466ff7cb3f00446bcd2a9d0d", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "6b97a7c3f67c467f0f6c02a048947aa7f579387f438a50fd06a6e1cbf5a5371d"}, + "membrane_tee_plugin": {:hex, :membrane_tee_plugin, "0.11.0", "7891283843fb42df788793217cc4117aa054de515b5e8a5b45109f069976e264", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "1af8aff9488eb2303f18757a58d8d4a872f967fe66d2470e055e3c38dc770e88"}, "membrane_telemetry_metrics": {:hex, :membrane_telemetry_metrics, "0.1.0", "cb93d28356b436b0597736c3e4153738d82d2a14ff547f831df7e9051e54fc06", [:mix], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.1", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "aba28dc8311f70ced95d984509be930fac55857d2d18bffcf768815e627be3f0"}, - "membrane_udp_plugin": {:hex, :membrane_udp_plugin, "0.9.2", "b3e3f1026cbff45b80c9ff5f18206c95e5e2df598111b9a1264fb6e4cb62097d", [:mix], [{:membrane_core, "~> 0.11.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3.0", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "f542452e503be5ae4cbb2484c9e4454fbd60b3fb35a71cb7fe8099460b5a5a0d"}, - "membrane_video_compositor_plugin": {:hex, :membrane_video_compositor_plugin, "0.3.1", "be7746462791538d592f4d76df33bba587b7d8345bf310cc9794a9199b06d154", [:mix], [{:membrane_core, "~> 0.11.2", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_framerate_converter_plugin, "~> 0.6.1", [hex: :membrane_framerate_converter_plugin, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.1", [hex: :qex, repo: "hexpm", optional: false]}, {:rustler, "~> 0.26.0", [hex: :rustler, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "c4b2efca02daadef0084737faedc77a14593ee2f2195eafa70754f9c02796af4"}, + "membrane_udp_plugin": {:hex, :membrane_udp_plugin, "0.10.0", "d2d207e5873298fad59a7c520a01a39179b2f31ec0cf8b63f0c6687bcd32a816", [:mix], [{:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3.0", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "55f5c55fb12966236c7c8cc078fbf3ad216ba457a6089092fdca55238d0e038b"}, + "membrane_video_compositor_plugin": {:hex, :membrane_video_compositor_plugin, "0.5.1", "cbdc093bd367285900a333b1f320df167a5cb1b534594b3250ff38ca9a2c609a", [:mix], [{:membrane_core, "~> 0.12.5", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_framerate_converter_plugin, "~> 0.7.0", [hex: :membrane_framerate_converter_plugin, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.1", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 2.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:rustler, "~> 0.26.0", [hex: :rustler, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "470ff15c23f0e109cec8829e44848f3f6ec22335496254912f2b1a4d8e5af108"}, "membrane_vp8_format": {:hex, :membrane_vp8_format, "0.4.0", "6c29ec67479edfbab27b11266dc92f18f3baf4421262c5c31af348c33e5b92c7", [:mix], [], "hexpm", "8bb005ede61db8fcb3535a883f32168b251c2dfd1109197c8c3b39ce28ed08e2"}, - "membrane_webrtc_plugin": {:hex, :membrane_webrtc_plugin, "0.14.6", "cde6fd2b2812cdff91989813f41f5667662a63498cadcd1f69bfe3e4cc773516", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:ex_libsrtp, ">= 0.0.0", [hex: :ex_libsrtp, repo: "hexpm", optional: false]}, {:ex_sdp, "~> 0.11.0", [hex: :ex_sdp, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.11.2", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_funnel_plugin, "~> 0.7.0", [hex: :membrane_funnel_plugin, repo: "hexpm", optional: false]}, {:membrane_h264_ffmpeg_plugin, "~> 0.26.2", [hex: :membrane_h264_ffmpeg_plugin, repo: "hexpm", optional: false]}, {:membrane_ice_plugin, "~> 0.15.0", [hex: :membrane_ice_plugin, repo: "hexpm", optional: false]}, {:membrane_opentelemetry, "~> 0.1.0", [hex: :membrane_opentelemetry, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.6.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}, {:membrane_rtp_h264_plugin, "~> 0.15.0", [hex: :membrane_rtp_h264_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_opus_plugin, "~> 0.7.0", [hex: :membrane_rtp_opus_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_plugin, "~> 0.22.0", [hex: :membrane_rtp_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_vp8_plugin, "~> 0.7.1", [hex: :membrane_rtp_vp8_plugin, repo: "hexpm", optional: false]}, {:opentelemetry, "~> 1.0.4", [hex: :opentelemetry, repo: "hexpm", optional: false]}, {:opentelemetry_api, "~> 1.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.0", [hex: :qex, repo: "hexpm", optional: false]}], "hexpm", "5a3c4a6f1f42722887b9d7720d648074a0f363e7bc7fa1bff27f124c558234b1"}, + "membrane_webrtc_plugin": {:hex, :membrane_webrtc_plugin, "0.15.0", "24c4cecb4730542c8db5a10b530192b1c26e351663453e6db7aa5bd63acd1bbe", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:ex_libsrtp, ">= 0.0.0", [hex: :ex_libsrtp, repo: "hexpm", optional: false]}, {:ex_sdp, "~> 0.11.0", [hex: :ex_sdp, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.1", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_funnel_plugin, "~> 0.8.0", [hex: :membrane_funnel_plugin, repo: "hexpm", optional: false]}, {:membrane_h264_ffmpeg_plugin, "~> 0.27.0", [hex: :membrane_h264_ffmpeg_plugin, repo: "hexpm", optional: false]}, {:membrane_ice_plugin, "~> 0.16.0", [hex: :membrane_ice_plugin, repo: "hexpm", optional: false]}, {:membrane_opentelemetry, "~> 0.1.0", [hex: :membrane_opentelemetry, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.7.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}, {:membrane_rtp_h264_plugin, "~> 0.16.0", [hex: :membrane_rtp_h264_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_opus_plugin, "~> 0.8.0", [hex: :membrane_rtp_opus_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_plugin, "~> 0.23.0", [hex: :membrane_rtp_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_vp8_plugin, "~> 0.8.0", [hex: :membrane_rtp_vp8_plugin, repo: "hexpm", optional: false]}, {:opentelemetry, "~> 1.0.4", [hex: :opentelemetry, repo: "hexpm", optional: false]}, {:opentelemetry_api, "~> 1.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.0", [hex: :qex, repo: "hexpm", optional: false]}], "hexpm", "9c71e964b3793a7e740199b4fe201dfb4cfb29d83a9b631e8856db1a79e16576"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, diff --git a/openapi.yaml b/openapi.yaml index 77be0f7e..542ecf91 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -176,6 +176,13 @@ components: RoomConfig: description: Room configuration properties: + enforceEncoding: + description: Enforces video codec for each peer in the room + enum: + - h264 + - vp8 + nullable: true + type: string maxPeers: description: Maximum amount of peers allowed into the room example: 10 diff --git a/protos b/protos index 0895a982..c69b048e 160000 --- a/protos +++ b/protos @@ -1 +1 @@ -Subproject commit 0895a982b0f23714952d38ca8a6ba78a9b0278e6 +Subproject commit c69b048e56ccf393931ff259a45a0aed6494b13e diff --git a/test/jellyfish_web/controllers/room_controller_test.exs b/test/jellyfish_web/controllers/room_controller_test.exs index 43a5a514..fba78833 100644 --- a/test/jellyfish_web/controllers/room_controller_test.exs +++ b/test/jellyfish_web/controllers/room_controller_test.exs @@ -100,6 +100,11 @@ defmodule JellyfishWeb.RoomControllerTest do assert json_response(conn, :bad_request)["errors"] == "maxPeers must be a number" + + conn = post(conn, ~p"/room", enforceEncoding: "nan") + + assert json_response(conn, :bad_request)["errors"] == + "enforceEncoding must be 'h264' or 'vp8'" end end diff --git a/test/jellyfish_web/integration/server_socket_test.exs b/test/jellyfish_web/integration/server_socket_test.exs index d6e81265..1e24263f 100644 --- a/test/jellyfish_web/integration/server_socket_test.exs +++ b/test/jellyfish_web/integration/server_socket_test.exs @@ -29,6 +29,7 @@ defmodule JellyfishWeb.Integration.ServerSocketTest do @auth_response %Authenticated{} @max_peers 1 + @enforce_encoding :ENCODING_H264 Application.put_env( :jellyfish, @@ -159,7 +160,7 @@ defmodule JellyfishWeb.Integration.ServerSocketTest do rooms: [ %RoomState{ id: ^room_id, - config: %{max_peers: @max_peers}, + config: %{max_peers: @max_peers, enforce_encoding: @enforce_encoding}, components: [], peers: [ %RoomState.Peer{ @@ -305,7 +306,7 @@ defmodule JellyfishWeb.Integration.ServerSocketTest do defp add_room_and_peer(conn, server_api_token) do conn = put_req_header(conn, "authorization", "Bearer " <> server_api_token) - conn = post(conn, ~p"/room", maxPeers: @max_peers) + conn = post(conn, ~p"/room", maxPeers: @max_peers, enforceEncoding: "h264") assert %{"id" => room_id} = json_response(conn, :created)["data"] conn = post(conn, ~p"/room/#{room_id}/peer", type: "webrtc")