diff --git a/lib/jellyfish/peer.ex b/lib/jellyfish/peer.ex index 5a55a1b9..539cafc9 100644 --- a/lib/jellyfish/peer.ex +++ b/lib/jellyfish/peer.ex @@ -39,17 +39,20 @@ defmodule Jellyfish.Peer do end end - @spec new(peer(), map()) :: t() + @spec new(peer(), map()) :: {:ok, t()} | {:error, term()} def new(type, options) do id = UUID.uuid4() options = Map.put(options, :peer_id, id) - {:ok, endpoint} = type.config(options) - - %__MODULE__{ - id: id, - type: type, - engine_endpoint: endpoint - } + with {:ok, endpoint} <- type.config(options) do + {:ok, + %__MODULE__{ + id: id, + type: type, + engine_endpoint: endpoint + }} + else + {:error, _reason} = error -> error + end end end diff --git a/lib/jellyfish/peer/webrtc.ex b/lib/jellyfish/peer/webrtc.ex index 036f1d6e..d9c23bfa 100644 --- a/lib/jellyfish/peer/webrtc.ex +++ b/lib/jellyfish/peer/webrtc.ex @@ -18,44 +18,49 @@ defmodule Jellyfish.Peer.WebRTC do "WebRTC peers can be used only if WEBRTC_USED environmental variable is not set to \"false\"" ) - handshake_options = [ - client_mode: false, - dtls_srtp: true - ] + with {:ok, valid_opts} <- + OpenApiSpex.cast_value(options, JellyfishWeb.ApiSpec.Peer.WebRTC.schema()) do + handshake_options = [ + client_mode: false, + dtls_srtp: true + ] - simulcast? = true - webrtc_extensions = [Mid, Rid, RepairedRid, TWCC] - network_options = options.network_options + simulcast? = valid_opts.enableSimulcast + webrtc_extensions = [Mid, Rid, RepairedRid, TWCC] + network_options = options.network_options - filter_codecs = - case options.video_codec do - :h264 -> - &filter_codecs_h264/1 + filter_codecs = + case options.video_codec do + :h264 -> + &filter_codecs_h264/1 - :vp8 -> - &filter_codecs_vp8/1 + :vp8 -> + &filter_codecs_vp8/1 - nil -> - &any_codecs/1 - end + nil -> + &any_codecs/1 + end - {:ok, - %WebRTC{ - rtc_engine: options.engine_pid, - ice_name: options.peer_id, - owner: self(), - 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, - simulcast_config: %SimulcastConfig{ - enabled: simulcast?, - initial_target_variant: fn _track -> :medium end - } - }} + {:ok, + %WebRTC{ + rtc_engine: options.engine_pid, + ice_name: options.peer_id, + owner: self(), + 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, + simulcast_config: %SimulcastConfig{ + enabled: simulcast?, + initial_target_variant: fn _track -> :medium end + } + }} + else + {:error, _reason} = error -> error + end end defp filter_codecs_h264(%Encoding{name: "H264", format_params: fmtp}) do diff --git a/lib/jellyfish/room.ex b/lib/jellyfish/room.ex index 5b4e032c..bb4058b8 100644 --- a/lib/jellyfish/room.ex +++ b/lib/jellyfish/room.ex @@ -73,9 +73,10 @@ defmodule Jellyfish.Room do end end - @spec add_peer(id(), Peer.peer()) :: {:ok, Peer.t()} | {:error, :reached_peers_limit} - def add_peer(room_id, peer_type) do - GenServer.call(registry_id(room_id), {:add_peer, peer_type}) + @spec add_peer(id(), Peer.peer(), map()) :: + {:ok, Peer.t()} | :error | {:error, :reached_peers_limit} + def add_peer(room_id, peer_type, options \\ %{}) do + GenServer.call(registry_id(room_id), {:add_peer, peer_type, options}) end @spec set_peer_connected(id(), Peer.id()) :: @@ -95,9 +96,9 @@ defmodule Jellyfish.Room do GenServer.call(registry_id(room_id), {:remove_peer, peer_id}) end - @spec add_component(id(), Component.component(), map() | nil) :: + @spec add_component(id(), Component.component(), map()) :: {:ok, Component.t()} | :error | {:error, :incompatible_codec} - def add_component(room_id, component_type, options) do + def add_component(room_id, component_type, options \\ %{}) do GenServer.call(registry_id(room_id), {:add_component, component_type, options}) end @@ -126,23 +127,32 @@ defmodule Jellyfish.Room do end @impl true - def handle_call({:add_peer, peer_type}, _from, state) do + def handle_call({:add_peer, peer_type, options}, _from, state) do {reply, state} = 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, - video_codec: state.config.video_codec - } - - peer = Peer.new(peer_type, options) - state = put_in(state, [:peers, peer.id], peer) - - Logger.info("Added peer #{inspect(peer.id)}") - - {{:ok, peer}, state} + options = + Map.merge( + %{ + engine_pid: state.engine_pid, + network_options: state.network_options, + video_codec: state.config.video_codec + }, + options + ) + + with {:ok, peer} <- Peer.new(peer_type, options) do + state = put_in(state, [:peers, peer.id], peer) + + Logger.info("Added peer #{inspect(peer.id)}") + + {{:ok, peer}, state} + else + {:error, reason} -> + Logger.warn("Unable to add peer: #{inspect(reason)}") + {:error, state} + end end {:reply, reply, state} @@ -214,7 +224,7 @@ defmodule Jellyfish.Room do options = Map.merge( %{engine_pid: state.engine_pid, room_id: state.id}, - if(is_nil(options), do: %{}, else: options) + options ) with :ok <- check_video_codec(video_codec, component_type), diff --git a/lib/jellyfish_web/api_spec/peer.ex b/lib/jellyfish_web/api_spec/peer.ex index 4ff86357..54c22d49 100644 --- a/lib/jellyfish_web/api_spec/peer.ex +++ b/lib/jellyfish_web/api_spec/peer.ex @@ -2,9 +2,10 @@ defmodule JellyfishWeb.ApiSpec.Peer do @moduledoc false require OpenApiSpex - alias OpenApiSpex.Schema + alias JellyfishWeb.ApiSpec.Peer.WebRTC + defmodule Type do @moduledoc false @@ -18,6 +19,21 @@ defmodule JellyfishWeb.ApiSpec.Peer do }) end + defmodule Options do + @moduledoc false + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "PeerOptions", + description: "Peer-specific options", + type: :object, + oneOf: [ + WebRTC + ] + }) + end + defmodule Status do @moduledoc false diff --git a/lib/jellyfish_web/api_spec/peer/webrtc.ex b/lib/jellyfish_web/api_spec/peer/webrtc.ex new file mode 100644 index 00000000..0207bd20 --- /dev/null +++ b/lib/jellyfish_web/api_spec/peer/webrtc.ex @@ -0,0 +1,19 @@ +defmodule JellyfishWeb.ApiSpec.Peer.WebRTC do + @moduledoc false + + require OpenApiSpex + alias OpenApiSpex.Schema + + OpenApiSpex.schema(%{ + title: "PeerOptionsWebRTC", + description: "Options specific to the WebRTC peer", + type: :object, + properties: %{ + enableSimulcast: %Schema{ + type: :boolean, + description: "Enables the peer to use simulcast", + default: true + } + } + }) +end diff --git a/lib/jellyfish_web/controllers/component_controller.ex b/lib/jellyfish_web/controllers/component_controller.ex index 02fa2b72..2a4da461 100644 --- a/lib/jellyfish_web/controllers/component_controller.ex +++ b/lib/jellyfish_web/controllers/component_controller.ex @@ -59,7 +59,7 @@ defmodule JellyfishWeb.ComponentController do ] def create(conn, %{"room_id" => room_id} = params) do - with component_options <- Map.get(params, "options"), + with component_options <- Map.get(params, "options", %{}), {:ok, component_type_string} <- Map.fetch(params, "type"), {:ok, component_type} <- Component.parse_type(component_type_string), {:ok, _room_pid} <- RoomService.find_room(room_id), diff --git a/lib/jellyfish_web/controllers/peer_controller.ex b/lib/jellyfish_web/controllers/peer_controller.ex index d532b568..429ed11d 100644 --- a/lib/jellyfish_web/controllers/peer_controller.ex +++ b/lib/jellyfish_web/controllers/peer_controller.ex @@ -27,9 +27,10 @@ defmodule JellyfishWeb.PeerController do %Schema{ type: :object, properties: %{ + options: ApiSpec.Peer.Options, type: ApiSpec.Peer.Type }, - required: [:type] + required: [:type, :options] }}, responses: [ created: ApiSpec.data("Peer successfully created", ApiSpec.PeerDetailsResponse), @@ -64,10 +65,11 @@ defmodule JellyfishWeb.PeerController do # pid and adding a new peer to it # in such a case, the controller will fail # and Phoenix will return 500 - with {:ok, peer_type_string} <- Map.fetch(params, "type"), + with peer_options <- Map.get(params, "options", %{}), + {:ok, peer_type_string} <- Map.fetch(params, "type"), {:ok, peer_type} <- Peer.parse_type(peer_type_string), {:ok, _room_pid} <- RoomService.find_room(room_id), - {:ok, peer} <- Room.add_peer(room_id, peer_type) do + {:ok, peer} <- Room.add_peer(room_id, peer_type, peer_options) do assigns = [peer: peer, token: PeerToken.generate(%{peer_id: peer.id, room_id: room_id})] conn diff --git a/openapi.yaml b/openapi.yaml index 1e63e4fc..e64a1644 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -144,6 +144,23 @@ components: title: PeerDetailsResponse type: object x-struct: Elixir.JellyfishWeb.ApiSpec.PeerDetailsResponse + PeerOptions: + description: Peer-specific options + oneOf: + - $ref: '#/components/schemas/PeerOptionsWebRTC' + title: PeerOptions + type: object + x-struct: Elixir.JellyfishWeb.ApiSpec.Peer.Options + PeerOptionsWebRTC: + description: Options specific to the WebRTC peer + properties: + enableSimulcast: + default: true + description: Enables the peer to use simulcast + type: boolean + title: PeerOptionsWebRTC + type: object + x-struct: Elixir.JellyfishWeb.ApiSpec.Peer.WebRTC PeerStatus: description: Informs about the peer status enum: @@ -203,22 +220,6 @@ components: title: RoomConfig type: object x-struct: Elixir.JellyfishWeb.ApiSpec.Room.Config - RoomCreateDetailsResponse: - description: Response containing room details - properties: - data: - properties: - room: - $ref: '#/components/schemas/Room' - jellyfish_address: - description: Jellyfish instance address where the room was created. This might be different than the address of Jellyfish where the request was sent only when running a cluster of Jellyfishes. - example: "jellyfish1:5003" - type: string - required: - - data - title: RoomDetailsResponse - type: object - x-struct: Elixir.JellyfishWeb.ApiSpec.RoomDetailsResponse RoomDetailsResponse: description: Response containing room details properties: @@ -287,7 +288,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RoomCreateDetailsResponse' + $ref: '#/components/schemas/RoomDetailsResponse' description: Room successfully created '400': content: @@ -470,10 +471,13 @@ paths: application/json: schema: properties: + options: + $ref: '#/components/schemas/PeerOptions' type: $ref: '#/components/schemas/PeerType' required: - type + - options type: object description: Peer specification required: false