Current section
Files
Jump to
Current section
Files
src/room_coordinator.erl
-module(room_coordinator).
-behaviour(gen_server).
-compile(nowarn_deprecated_catch).
-include_lib("n2o/include/n2o.hrl").
%% API
-export([start_link/1, join/2, leave/2, get_state/1,
ensure_started/1, post_chat/3, originate_video/3, sdp_answer/3, ice_candidate/3,
peer_left/2, terminate_room/1, active_participants/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-record(state, {
room_id :: binary(),
participants = [] :: list(),
publishers = [] :: list(),
media_broker = undefined :: pid() | undefined
}).
%% API Functions
start_link(RoomId) ->
gen_server:start_link(?MODULE, [RoomId], []).
ensure_started(RoomId) ->
RoomIdBin = case is_binary(RoomId) of
true -> RoomId;
false -> list_to_binary(RoomId)
end,
case syn:lookup(rooms, RoomIdBin) of
{Pid, _} -> {ok, Pid};
undefined ->
case gen_server:start(?MODULE, [RoomIdBin], []) of
{ok, Pid} -> {ok, Pid};
{error, {already_started, Pid}} -> {ok, Pid};
{error, already_exists} ->
case syn:lookup(rooms, RoomIdBin) of
{Pid, _} -> {ok, Pid};
undefined -> {error, failed_to_start}
end;
Else -> Else
end
end.
active_participants(RoomPid) ->
case gen_server:call(RoomPid, get_state) of
{ok, State} -> State#state.participants;
_ -> []
end.
join(RoomPid, Participant) ->
gen_server:call(RoomPid, {join, Participant}).
leave(RoomPid, ParticipantId) ->
gen_server:call(RoomPid, {leave, ParticipantId}).
post_chat(RoomPid, Sender, Message) ->
gen_server:call(RoomPid, {chat, Sender, Message}).
originate_video(RoomPid, PeerId, ClientPid) ->
gen_server:call(RoomPid, {originate_video, PeerId, ClientPid}).
sdp_answer(RoomPid, PeerId, Sdp) ->
gen_server:call(RoomPid, {sdp_answer, PeerId, Sdp}).
ice_candidate(RoomPid, PeerId, Candidate) ->
gen_server:call(RoomPid, {ice_candidate, PeerId, Candidate}).
peer_left(RoomPid, PeerId) ->
gen_server:call(RoomPid, {peer_left, PeerId}).
terminate_room(RoomPid) ->
gen_server:call(RoomPid, terminate_room).
get_state(RoomPid) ->
gen_server:call(RoomPid, get_state).
%% gen_server Callbacks
init([RoomId]) ->
case syn:register(rooms, RoomId, self()) of
ok ->
ok = mnesia_srv:create_room_table(RoomId),
{ok, #state{room_id = RoomId}};
{error, taken} ->
{stop, already_exists}
end.
handle_call({join, Participant}, _From, State) ->
CleanParticipants = [P || P <- State#state.participants, maps:get(id, P) =/= maps:get(id, Participant)],
NewParticipants = [Participant | CleanParticipants],
NewState = State#state{participants = NewParticipants},
syn:publish(rooms, State#state.room_id, {presence, join, Participant}),
{reply, {ok, NewState}, NewState};
handle_call({leave, ParticipantId}, _From, State) ->
NewParticipants = [P || P <- State#state.participants, maps:get(id, P) =/= ParticipantId],
NewState = State#state{participants = NewParticipants},
syn:publish(rooms, State#state.room_id, {presence, leave, ParticipantId}),
{reply, ok, NewState};
handle_call({chat, Sender, Message}, _From, State) ->
Timestamp = erlang:system_time(millisecond),
ok = mnesia_srv:save_message_to_room(State#state.room_id, Sender, Message, Timestamp),
Msg = {'$msg', kvs:seq([], []), [], [], Sender, Message},
n2o:send({topic, State#state.room_id}, #client{data = Msg}),
{reply, ok, State};
handle_call({originate_video, PeerId, ClientPid}, _From, State) ->
{NewState, BrokerPid} = case State#state.media_broker of
undefined ->
{ok, Pid} = media_broker_srv:start_link(),
{State#state{media_broker = Pid}, Pid};
Pid ->
{State, Pid}
end,
{Status, StartedAt} = media_broker_srv:peer_joined(BrokerPid, State#state.room_id, PeerId, ClientPid),
{reply, {Status, StartedAt}, NewState};
handle_call({sdp_answer, PeerId, Sdp}, _From, State) ->
case State#state.media_broker of
undefined -> ok;
BrokerPid -> media_broker_srv:sdp_answer(BrokerPid, State#state.room_id, PeerId, Sdp)
end,
{reply, ok, State};
handle_call({ice_candidate, PeerId, Candidate}, _From, State) ->
case State#state.media_broker of
undefined -> ok;
BrokerPid -> media_broker_srv:ice_candidate(BrokerPid, State#state.room_id, PeerId, Candidate)
end,
{reply, ok, State};
handle_call({peer_left, PeerId}, _From, State) ->
case State#state.media_broker of
undefined -> ok;
BrokerPid -> media_broker_srv:peer_left(BrokerPid, State#state.room_id, PeerId)
end,
{reply, ok, State};
handle_call(terminate_room, _From, State) ->
case State#state.media_broker of
undefined ->
{reply, {error, not_found}, State};
BrokerPid ->
Res = media_broker_srv:terminate_room(BrokerPid, State#state.room_id),
catch gen_server:stop(BrokerPid),
{reply, Res, State#state{media_broker = undefined}}
end;
handle_call(get_state, _From, State) ->
{reply, {ok, State}, State};
handle_call(_Request, _From, State) ->
{reply, {error, unknown_call}, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, State) ->
syn:unregister(rooms, State#state.room_id),
case State#state.media_broker of
undefined -> ok;
Pid -> catch gen_server:stop(Pid)
end,
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.