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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions lib/jellyfish/component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ defmodule Jellyfish.Component do

alias Jellyfish.Component.{File, HLS, RTSP}
alias Jellyfish.Exception.StructureError
alias Jellyfish.Track

@enforce_keys [
:id,
:type,
:properties
:properties,
:tracks
]
defstruct @enforce_keys

Expand All @@ -24,20 +26,21 @@ defmodule Jellyfish.Component do
@typedoc """
Type of the component.
"""
@type type :: HLS | RTSP
@type type :: HLS | RTSP | File

@typedoc """
Component-specific options.
"""
@type options :: HLS.t() | RTSP.t()
@type options :: HLS.t() | RTSP.t() | File.t()

@typedoc """
Stores information about the component.
"""
@type t :: %__MODULE__{
id: id(),
type: type(),
properties: map()
properties: map(),
tracks: [Track.t()]
}

@doc false
Expand All @@ -47,14 +50,16 @@ defmodule Jellyfish.Component do
%{
"id" => id,
"type" => type_str,
"properties" => properties
"properties" => properties,
"tracks" => tracks
} ->
type = type_from_string(type_str)

%__MODULE__{
id: id,
type: type,
properties: type.properties_from_json(properties)
properties: type.properties_from_json(properties),
tracks: Enum.map(tracks, &Track.from_json/1)
}

_other ->
Expand Down
131 changes: 130 additions & 1 deletion lib/jellyfish/notification.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
defmodule Jellyfish.Notification do
@moduledoc false

alias Jellyfish.{Component, Peer, Room}
alias Jellyfish.{Component, Peer, Room, Track}
alias Jellyfish.ServerMessage.{TrackAdded, TrackMetadataUpdated, TrackRemoved}

defmodule RoomCreated do
@moduledoc nil
Expand Down Expand Up @@ -60,6 +61,19 @@ defmodule Jellyfish.Notification do
}
end

defmodule PeerMetadataUpdated do
@moduledoc nil

@enforce_keys [:room_id, :peer_id, :metadata]
defstruct @enforce_keys

@type t :: %__MODULE__{
room_id: Room.id(),
peer_id: Peer.id(),
metadata: any()
}
end

defmodule PeerCrashed do
@moduledoc nil

Expand Down Expand Up @@ -118,9 +132,113 @@ defmodule Jellyfish.Notification do
}
end

defmodule ComponentTrackAdded do
@moduledoc nil

@enforce_keys [:room_id, :component_id, :track]
defstruct @enforce_keys

@type t :: %__MODULE__{
room_id: Room.id(),
component_id: Component.id(),
track: Track.t()
}
end

defmodule ComponentTrackMetadataUpdated do
@moduledoc nil

@enforce_keys [:room_id, :component_id, :track]
defstruct @enforce_keys

@type t :: %__MODULE__{
room_id: Room.id(),
component_id: Component.id(),
track: Track.t()
}
end

defmodule ComponentTrackRemoved do
@moduledoc nil

@enforce_keys [:room_id, :component_id, :track]
defstruct @enforce_keys

@type t :: %__MODULE__{
room_id: Room.id(),
component_id: Component.id(),
track: Track.t()
}
end

defmodule PeerTrackAdded do
@moduledoc nil

@enforce_keys [:room_id, :peer_id, :track]
defstruct @enforce_keys

@type t :: %__MODULE__{
room_id: Room.id(),
peer_id: Peer.id(),
track: Track.t()
}
end

defmodule PeerTrackMetadataUpdated do
@moduledoc nil

@enforce_keys [:room_id, :peer_id, :track]
defstruct @enforce_keys

@type t :: %__MODULE__{
room_id: Room.id(),
peer_id: Peer.id(),
track: Track.t()
}
end

defmodule PeerTrackRemoved do
@moduledoc nil

@enforce_keys [:room_id, :peer_id, :track]
defstruct @enforce_keys

@type t :: %__MODULE__{
room_id: Room.id(),
peer_id: Peer.id(),
track: Track.t()
}
end

@discarded_fields [:__unknown_fields__]

@doc false
def to_notification(%module{} = message)
when module in [TrackAdded, TrackMetadataUpdated, TrackRemoved] do
{endpoint_type, endpoint_id} = message.endpoint_info

notification_module =
case {endpoint_type, module} do
{:component_id, TrackAdded} -> ComponentTrackAdded
{:component_id, TrackMetadataUpdated} -> ComponentTrackMetadataUpdated
{:component_id, TrackRemoved} -> ComponentTrackRemoved
{:peer_id, TrackAdded} -> PeerTrackAdded
{:peer_id, TrackMetadataUpdated} -> PeerTrackMetadataUpdated
{:peer_id, TrackRemoved} -> PeerTrackRemoved
end

discarded_fields = @discarded_fields ++ [:endpoint_info]

message
|> Map.from_struct()
|> Map.drop(discarded_fields)
|> Map.merge(%{
endpoint_type => endpoint_id,
:track => from_proto_track(message.track)
})
|> then(&struct!(notification_module, &1))
end

def to_notification(%module{} = message) do
notification_module =
module
Expand All @@ -133,4 +251,15 @@ defmodule Jellyfish.Notification do
|> Map.drop(@discarded_fields)
|> then(&struct!(notification_module, &1))
end

defp from_proto_track(track) do
%Track{
id: track.id,
type: from_proto_track_type(track.type),
metadata: Jason.decode!(track.metadata)
}
end

defp from_proto_track_type(:TRACK_TYPE_VIDEO), do: :video
defp from_proto_track_type(:TRACK_TYPE_AUDIO), do: :audio
end
17 changes: 13 additions & 4 deletions lib/jellyfish/peer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ defmodule Jellyfish.Peer do

alias Jellyfish.Exception.StructureError
alias Jellyfish.Peer.WebRTC
alias Jellyfish.Track

@enforce_keys [
:id,
:type,
:status
:status,
:tracks,
:metadata
]
defstruct @enforce_keys

Expand Down Expand Up @@ -45,7 +48,9 @@ defmodule Jellyfish.Peer do
@type t :: %__MODULE__{
id: id(),
type: type(),
status: status()
status: status(),
tracks: [Track.t()],
metadata: any()
}

@doc false
Expand All @@ -55,12 +60,16 @@ defmodule Jellyfish.Peer do
%{
"id" => id,
"type" => type_str,
"status" => status_str
"status" => status_str,
"tracks" => tracks,
"metadata" => metadata
} ->
%__MODULE__{
id: id,
type: type_from_string(type_str),
status: status_from_string(status_str)
status: status_from_string(status_str),
tracks: Enum.map(tracks, &Track.from_json/1),
metadata: metadata
}

_other ->
Expand Down
9 changes: 6 additions & 3 deletions lib/jellyfish/room.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@ defmodule Jellyfish.Room do
...> config: %{max_peers: 10, video_codec: nil},
...> peers: []}
true
iex> assert {:ok,%Jellyfish.Peer{
iex> assert {:ok, %Jellyfish.Peer{
...> status: :disconnected,
...> type: Jellyfish.Peer.WebRTC
...> type: Jellyfish.Peer.WebRTC,
...> tracks: []
...> } = peer, _peer_token} = Jellyfish.Room.add_peer(client, room.id, Jellyfish.Peer.WebRTC)
iex> %Jellyfish.Peer{
...> id: peer.id,
...> status: :disconnected,
...> type: Jellyfish.Peer.WebRTC} == peer
...> type: Jellyfish.Peer.WebRTC,
...> tracks: [],
...> metadata: nil} == peer
true
iex> :ok = Jellyfish.Room.delete(client, room.id)
:ok
Expand Down
60 changes: 60 additions & 0 deletions lib/jellyfish/track.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
defmodule Jellyfish.Track do
@moduledoc """
Defines `t:Jellyfish.Track.t/0`.

It represents a single media track, either audio or video.
"""

alias Jellyfish.Exception.StructureError

@enforce_keys [
:id,
:type,
:metadata
]
defstruct @enforce_keys

@typedoc """
Id of the track, unique within the room.
"""
@type id :: String.t()

@typedoc """
Type of the track.
"""
@type type :: :audio | :video

@valid_type_string ["audio", "video"]

@typedoc """
Stores information about the track.
"""
@type t :: %__MODULE__{
id: id(),
type: type(),
metadata: any()
}

@doc false
@spec from_json(map()) :: t()
def from_json(response) do
case response do
%{
"id" => id,
"type" => type_str,
"metadata" => metadata
} ->
%__MODULE__{
id: id,
type: type_from_string(type_str),
metadata: metadata
}

_other ->
raise StructureError
end
end

defp type_from_string(type) when type in @valid_type_string,
do: String.to_atom(type)
end
11 changes: 4 additions & 7 deletions lib/jellyfish/webhook_notifier.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,22 @@ defmodule Jellyfish.WebhookNotifier do
Decodes received webhook to notification structs.

```
iex> %{
...> "notification" => <<18, 76, 10, 36, 102, 98, 102, 52, 49, 57, 48, 99, 45, 53, 99, 55, 54, 45, 52,
iex> <<18, 76, 10, 36, 102, 98, 102, 52, 49, 57, 48, 99, 45, 53, 99, 55, 54, 45, 52,
...> 49, 53, 99, 45, 56, 57, 51, 57, 45, 53, 50, 99, 54, 101, 100, 50, 48, 56, 54,
...> 56, 98, 18, 36, 99, 55, 50, 51, 54, 53, 56, 55, 45, 53, 100, 102, 56, 45, 52,
...> 98, 52, 49, 45, 98, 54, 101, 52, 45, 50, 54, 56, 101, 55, 49, 49, 51, 51, 101,
...> 101, 50>>
...> } |> Jellyfish.WebhookNotifier.receive()
...> |> Jellyfish.WebhookNotifier.receive()
%Jellyfish.Notification.PeerConnected{
room_id: "fbf4190c-5c76-415c-8939-52c6ed20868b",
peer_id: "c7236587-5df8-4b41-b6e4-268e71133ee2"
}
```
"""
@spec receive(term()) :: struct()
def receive(json) do
def receive(binary) do
%ServerMessage{content: {_type, notification}} =
json
|> Map.get("notification")
|> ServerMessage.decode()
ServerMessage.decode(binary)

Notification.to_notification(notification)
end
Expand Down
Loading