-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Don't accept streams until server is ready to read them #94
Changes from 8 commits
e358e26
a9166de
85e49c2
7018f7a
44f1690
6089407
52ff895
db779f3
2ddc8f3
6b79c2a
54b6e50
b61aad8
fcffb0d
8283bdc
614ec22
d068af2
0579cfb
ff54122
b3a0720
9bd3632
859550b
29709ae
c3357ce
9aa29b7
032b5e2
76d45d1
ca181ea
b5f8263
938fb06
ed16832
0fa80b1
b31bbd3
822aa2e
81d30d9
b2a21c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,12 @@ | ||
defmodule Membrane.RTMP.Server do | ||
@moduledoc """ | ||
A simple RTMP server, which handles each new incoming connection. | ||
|
||
When new client connects to the server, it goes into :client_waiting_queue and its RTMP handshake will remanin unfinished. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this part is valid anymore |
||
Only when pipeline tries to pull data from client, its handshake will be finished, and client will be registered. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't mention the "pipeline" - the |
||
|
||
Also when new client connects, optional, annonymous function defined by user is triggered. | ||
The lambda function is given PID of parent server, app and stream key. | ||
""" | ||
use GenServer | ||
|
||
|
@@ -15,7 +21,8 @@ defmodule Membrane.RTMP.Server do | |
handler: ClientHandlerBehaviour.t(), | ||
port: :inet.port_number(), | ||
use_ssl?: boolean(), | ||
name: atom() | nil | ||
name: atom() | nil, | ||
lambda: function() | nil | ||
varsill marked this conversation as resolved.
Show resolved
Hide resolved
|
||
] | ||
|
||
@type server_identifier :: pid() | atom() | ||
|
@@ -31,27 +38,26 @@ defmodule Membrane.RTMP.Server do | |
end | ||
|
||
@doc """ | ||
Subscribes for the given app and stream key. | ||
When a client connects (or has already connected) to the server with given app and stream key, | ||
the subscriber will be informed. | ||
Subscribes for any app/stream_key. | ||
When a new client connects, subscriber will be informed, if currently is awaiting client_ref for a given app/stream_key | ||
""" | ||
@spec subscribe(server_identifier(), String.t(), String.t()) :: :ok | ||
def subscribe(server_identifier, app, stream_key) do | ||
GenServer.cast(server_identifier, {:subscribe, app, stream_key, self()}) | ||
@spec subscribe_any(server_identifier()) :: :ok | ||
def subscribe_any(server_identifier) do | ||
GenServer.cast(server_identifier, {:subscribe_any, self()}) | ||
:ok | ||
end | ||
|
||
@doc """ | ||
Awaits for the client reference of the connection to which the user has previously subscribed. | ||
Awaits for the client reference of the connection to specified app/stream_key | ||
|
||
Note: this function call is blocking! | ||
Note: first you need to call `#{__MODULE__}.subscribe/3` to subscribe | ||
for a given `app` and `stream_key`. | ||
Note: first you need to call `#{__MODULE__}.subscribe_any/1`. | ||
""" | ||
@spec await_subscription(String.t(), String.t(), non_neg_integer()) :: {:ok, pid()} | :error | ||
def await_subscription(app, stream_key, timeout \\ 5_000) do | ||
@spec await_client_ref(String.t(), String.t(), non_neg_integer()) :: {:ok, pid()} | :error | ||
def await_client_ref(app, stream_key, timeout \\ 5_000) do | ||
receive do | ||
{:client_ref, client_ref, ^app, ^stream_key} -> {:ok, client_ref} | ||
{:client_ref, client_ref, ^app, ^stream_key} -> | ||
{:ok, client_ref} | ||
after | ||
timeout -> :error | ||
end | ||
|
@@ -74,12 +80,14 @@ defmodule Membrane.RTMP.Server do | |
|
||
{:ok, | ||
%{ | ||
subscriptions: %{}, | ||
subscriptions_any: [], | ||
client_reference_mapping: %{}, | ||
client_waiting_queue: %{}, | ||
listener: pid, | ||
port: nil, | ||
to_reply: [], | ||
use_ssl?: server_options.use_ssl? | ||
use_ssl?: server_options.use_ssl?, | ||
lambda: server_options.lambda | ||
}} | ||
end | ||
|
||
|
@@ -93,16 +101,37 @@ defmodule Membrane.RTMP.Server do | |
end | ||
|
||
@impl true | ||
def handle_cast({:subscribe, app, stream_key, subscriber_pid}, state) do | ||
state = put_in(state, [:subscriptions, {app, stream_key}], subscriber_pid) | ||
maybe_send_subscription(app, stream_key, state) | ||
def handle_cast({:subscribe_any, subscriber_pid}, state) do | ||
subs = [state.subscriptions_any ++ subscriber_pid] | ||
state = %{state | subscriptions_any: subs} | ||
|
||
# try to send all client_refs from :client_waiting_queue to this subscriber, maybe is already awaiting one of /app/stream_keys | ||
state.client_waiting_queue | ||
|> Enum.each(fn {{app, stream_key}, client_ref} -> | ||
send(subscriber_pid, {:client_ref, client_ref, app, stream_key}) | ||
end) | ||
|
||
{:noreply, state} | ||
end | ||
|
||
@impl true | ||
def handle_info({:register_client, app, stream_key, client_reference_pid}, state) do | ||
state = put_in(state, [:client_reference_mapping, {app, stream_key}], client_reference_pid) | ||
maybe_send_subscription(app, stream_key, state) | ||
client_waiting_queue = state.client_waiting_queue |> Map.delete({app, stream_key}) | ||
{:noreply, %{state | client_waiting_queue: client_waiting_queue}} | ||
end | ||
|
||
@impl true | ||
def handle_info({:register_client_in_queue, app, stream_key, client_ref}, state) do | ||
if is_function(state.lambda) do | ||
state.lambda.(self(), app, stream_key) | ||
end | ||
|
||
state = put_in(state, [:client_waiting_queue, {app, stream_key}], client_ref) | ||
# send client ref_to anyone possibly awaiting it | ||
state.subscriptions_any | ||
|> Enum.each(fn subscriber -> send(subscriber, {:client_ref, client_ref, app, stream_key}) end) | ||
|
||
{:noreply, state} | ||
end | ||
|
||
|
@@ -112,13 +141,9 @@ defmodule Membrane.RTMP.Server do | |
{:noreply, %{state | port: port, to_reply: []}} | ||
end | ||
|
||
defp maybe_send_subscription(app, stream_key, state) do | ||
if state.subscriptions[{app, stream_key}] != nil and | ||
state.client_reference_mapping[{app, stream_key}] != nil do | ||
send( | ||
state.subscriptions[{app, stream_key}], | ||
{:client_ref, state.client_reference_mapping[{app, stream_key}], app, stream_key} | ||
) | ||
end | ||
@impl true | ||
def handle_info({:lambda, message}, state) do | ||
IO.inspect(message, label: "new message from lambda") | ||
varsill marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{:noreply, state} | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we really want this one in .gitignore?