PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/apps/rtmp/src/rtmp_socket.erl

https://github.com/brahimalaya/publisher
Erlang | 782 lines | 449 code | 170 blank | 163 comment | 0 complexity | 6cdb4b7de22d72de21a683ef0f307b66 MD5 | raw file
Possible License(s): MIT, GPL-3.0
  1. %%% @author Max Lapshin <max@maxidoors.ru> [http://erlyvideo.org]
  2. %%% @copyright 2010 Max Lapshin
  3. %%% @doc RTMP socket module.
  4. %%% Designed to look like rtmp mode of usual TCP socket. If you have used {packet, http}, you will
  5. %%% find many common behaviours.
  6. %%%
  7. %%% When working on server side, you should accept Socket::port() with:
  8. %%% <pre><code>
  9. %%% {ok, RTMP} = rtmp_socket:accept(Socket).
  10. %%% receive
  11. %%% {rtmp, RTMP, connected} ->
  12. %%% rtmp_socket:setopts(RTMP, [{active, once}]),
  13. %%% loop(RTMP)
  14. %%% end
  15. %%% loop(RTMP) ->
  16. %%% receive
  17. %%% {rtmp, RTMP, disconnect, Statistics} ->
  18. %%% ok;
  19. %%% {rtmp, RTMP, #rtmp_message{} = Message} ->
  20. %%% io:format("Message: ~p~n", [Message]),
  21. %%% loop(RTMP)
  22. %%% end.
  23. %%% </code></pre>
  24. %%%
  25. %%% You are strongly advised to use {active, once} mode, because in any other case it is very easy
  26. %%% to crash whole your server with OOM killer.
  27. %%% @reference See <a href="http://erlyvideo.org/rtmp" target="_top">http://erlyvideo.org/rtmp</a> for more information
  28. %%% @end
  29. %%%
  30. %%% This file is part of erlang-rtmp.
  31. %%%
  32. %%% erlang-rtmp is free software: you can redistribute it and/or modify
  33. %%% it under the terms of the GNU General Public License as published by
  34. %%% the Free Software Foundation, either version 3 of the License, or
  35. %%% (at your option) any later version.
  36. %%%
  37. %%% erlang-rtmp is distributed in the hope that it will be useful,
  38. %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
  39. %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  40. %%% GNU General Public License for more details.
  41. %%%
  42. %%% You should have received a copy of the GNU General Public License
  43. %%% along with erlang-rtmp. If not, see <http://www.gnu.org/licenses/>.
  44. %%%
  45. %%%---------------------------------------------------------------------------------------
  46. -module(rtmp_socket).
  47. -author('Max Lapshin <max@maxidoors.ru>').
  48. -include("../include/rtmp.hrl").
  49. -include("rtmp_private.hrl").
  50. -version(1.1).
  51. -export([accept/1, connect/1, connect/2, start_link/1, getopts/2, setopts/2, getstat/2, getstat/1, send/2, get_socket/1]).
  52. -export([status/3, status/4, prepare_status/2, prepare_status/3, invoke/2, invoke/4, prepare_invoke/3, notify/4, prepare_notify/3]).
  53. -export([notify_audio/3, notify_video/3]).
  54. -export([start_socket/2, start_server/3, start_server/4, stop_server/1, set_socket/2]).
  55. -export([close/1]).
  56. %% gen_fsm callbacks
  57. -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
  58. -export([wait_for_socket_on_server/2, wait_for_socket_on_client/2, handshake_c1/2, handshake_c3/2, handshake_s1/2, loop/2, loop/3]).
  59. %% @spec (Port::integer(), Name::atom(), Callback::atom()) -> {ok, Pid::pid()}
  60. %% @doc Starts RTMP listener on port Port, registered under name Name with callback module Callback.
  61. %% Callback must export one function: create_client/1
  62. %% create_client(RTMPSocket::pid()) -> {ok, Pid::pid()}
  63. %%
  64. %% This function receives RTMPSocket, that is ready to send messages and after this callback function returns, this socket
  65. %% will send rtmp_message as it is defined in overview.
  66. %% @end
  67. -spec(start_server(Port::integer(), Name::atom(), Callback::atom()) -> {ok, Pid::pid()}).
  68. start_server(Port, Name, Callback) ->
  69. rtmp_sup:start_rtmp_listener(Port, Name, Callback, []).
  70. start_server(Port, Name, Callback, Args) ->
  71. rtmp_sup:start_rtmp_listener(Port, Name, Callback, Args).
  72. stop_server(Name) ->
  73. rtmp_sup:stop_rtmp_listener(Name).
  74. %% @spec (Socket::port()) -> {ok, RTMP::pid()}
  75. %% @doc Accepts client connection on socket Socket, starts RTMP decoder, passes socket to it
  76. %% and returns pid of newly created RTMP socket.
  77. %% @end
  78. -spec(accept(Socket::port()) -> {ok, RTMPSocket::pid()}).
  79. accept(Socket) ->
  80. {ok, Pid} = start_socket(accept, Socket),
  81. setopts(Pid, [{consumer,self()}]),
  82. {ok,Pid}.
  83. %% @spec (Socket::port()) -> {ok, RTMP::pid()}
  84. %% @doc Accepts client connection on socket Socket, starts RTMP decoder, passes socket to it
  85. %% and returns pid of newly created RTMP socket.
  86. %% @end
  87. -spec(connect(Socket::port()|string()) -> {ok, RTMPSocket::pid()}).
  88. connect(ServerSpec) ->
  89. connect(ServerSpec, [{timeout, 10000}]).
  90. -spec(connect(Socket::port()|string(), Options::list()) -> {ok, RTMPSocket::pid()}).
  91. connect(ServerSpec, Options) when is_binary(ServerSpec) ->
  92. connect(binary_to_list(ServerSpec), Options);
  93. connect(ServerSpec, Options) when is_list(ServerSpec) ->
  94. {_Proto,_Auth,Host,Port,_Path,_Query} = http_uri2:parse(ServerSpec),
  95. Timeout = proplists:get_value(timeout, Options, 10000),
  96. case gen_tcp:connect(Host, Port, [binary, {active, false}, {packet, raw}], Timeout) of
  97. {ok, Socket} ->
  98. inet:setopts(Socket, [{send_timeout,proplists:get_value(send_timeout,Options,Timeout)}]),
  99. {ok, Pid} = connect(Socket),
  100. setopts(Pid, [{url, ServerSpec}]),
  101. {ok, Pid};
  102. {error, Reason} ->
  103. {error, Reason}
  104. end;
  105. connect(Socket, _Options) when is_port(Socket) ->
  106. {ok, Pid} = start_socket(connect, Socket),
  107. setopts(Pid, [{consumer,self()}]),
  108. {ok,Pid}.
  109. %% @spec (Type::accept|connect, Socket::port) -> {ok, RTMP::pid()}
  110. %% @doc Starts RTMP socket with provided consumer and inititiate server or client connection
  111. %% @end
  112. -spec(start_socket(Type::connect|accept, Socket::port()) -> {ok, RTMP::pid()}).
  113. start_socket(Type, Socket) ->
  114. {ok, RTMP} = rtmp_sup:start_rtmp_socket(Type),
  115. case Socket of
  116. _ when is_port(Socket) -> gen_tcp:controlling_process(Socket, RTMP);
  117. _ -> ok
  118. end,
  119. set_socket(RTMP, Socket),
  120. {ok, RTMP}.
  121. set_socket(RTMP, Socket) ->
  122. gen_fsm:send_event(RTMP, {socket, Socket}).
  123. get_socket(RTMP) ->
  124. gen_fsm:sync_send_all_state_event(RTMP, get_socket, ?RTMP_TIMEOUT).
  125. notify_audio(RTMP, StreamId, DTS) ->
  126. gen_fsm:sync_send_all_state_event(RTMP, {notify_audio, StreamId, DTS}).
  127. notify_video(RTMP, StreamId, DTS) ->
  128. gen_fsm:sync_send_all_state_event(RTMP, {notify_video, StreamId, DTS}).
  129. close(Socket) ->
  130. Socket ! stop.
  131. %% @private
  132. start_link(Type) ->
  133. gen_fsm:start_link(?MODULE, [Type], []).
  134. %% @spec (RTMP::pid(), Options::[{Key, Value}]|{Key, Value}) -> ok
  135. %% @doc Just the same as {@link inet:setopts/2. inet:setopts/2} this function changes state of
  136. %% rtmp socket.<br/>
  137. %% Available options:
  138. %% <ul><li><code>chunk_size</code> - change outgoing chunk size</li>
  139. %% <li><code>window_size</code> - ask remote client to send read acknowlegement after WindowSize bytes</li>
  140. %% <li><code>amf_version</code> - change AMF0 to AMF3, but only before first call</li>
  141. %% <li><code>consumer</code> - change messages consumer</li>
  142. %% <li><code>debug = true|false</code> - dump all important packets or no</li>
  143. %% </ul>
  144. %% @end
  145. -spec(setopts(RTMP::rtmp_socket_pid(), Options::[{Key::atom(), Value::any()}]) -> ok).
  146. setopts(RTMP, Options) ->
  147. gen_fsm:sync_send_all_state_event(RTMP, {setopts, Options}).
  148. %% @spec (RTMP::pid(), Options::[{Key, Value}]|{Key, Value}) -> ok
  149. %% @doc Just the same as {@link inet:getopts/2. inet:getopts/2} this function gets state of
  150. %% rtmp socket.<br/>
  151. %% Available options:
  152. %% <ul>
  153. %% <li><code>chunk_size</code> - get outgoing chunk size</li>
  154. %% <li><code>window_size</code> - get remote client read acknowlegement window size bytes</li>
  155. %% <li><code>amf_version</code> - get AMF0 or AMF3</li>
  156. %% <li><code>consumer</code> - get messages consumer</li>
  157. %% <li><code>address</code> - get remote client IP and port</li>
  158. %% </ul>
  159. %% @end
  160. -spec(getopts(RTMP::rtmp_socket_pid(), Options::atom()|[Key::atom()]) -> any()).
  161. getopts(RTMP, Options) ->
  162. gen_fsm:sync_send_all_state_event(RTMP, {getopts, Options}, ?RTMP_TIMEOUT).
  163. %% @spec (RTMP::pid(), Stats::[Key]) -> Values::[{Key,Value}]
  164. %% @doc Just the same as {@link inet:getstats/2. inet:getstats/2} this function gets statistics of
  165. %% rtmp socket.<br/>
  166. %% Available options:
  167. %% <ul>
  168. %% <li><code>recv_oct</code> - number of bytes received to the socket.</li>
  169. %% <li><code>send_oct</code> - number of bytes sent from the socket</li>
  170. %% </ul>
  171. %% @end
  172. -spec(getstat(RTMP::rtmp_socket_pid(), Options::[Key::atom()]) -> ok).
  173. getstat(RTMP, Options) ->
  174. gen_fsm:sync_send_all_state_event(RTMP, {getstat, Options}, ?RTMP_TIMEOUT).
  175. %% @spec (RTMP::pid()) -> Values::[{Key,Value}]
  176. %% @doc Just the same as {@link inet:getstats/1. inet:getstats/1} this function gets statistics of
  177. %% rtmp socket.<br/>
  178. -spec(getstat(RTMP::rtmp_socket_pid()) -> ok).
  179. getstat(RTMP) ->
  180. gen_fsm:sync_send_all_state_event(RTMP, getstat, ?RTMP_TIMEOUT).
  181. %% @spec (RTMP::pid(), Message::rtmp_message()) -> ok
  182. %% @doc Sends message to client.
  183. %% @end
  184. -spec(send(RTMP::rtmp_socket_pid(), Message::rtmp_message()) -> ok).
  185. send(RTMP, Message) ->
  186. % io:format("Message ~p ~p ~p~n", [Message#rtmp_message.type, Message#rtmp_message.timestamp, Message#rtmp_message.stream_id]),
  187. % case process_info(RTMP, message_queue_len) of
  188. % {message_queue_len, Length} when Length < 20 -> RTMP ! Message;
  189. % {message_queue_len, Length} -> gen_fsm:sync_send_event(RTMP, Message, ?RTMP_TIMEOUT);
  190. % _ -> ok
  191. % end,
  192. RTMP ! Message,
  193. ok.
  194. notify(RTMP, StreamId, Name, Args) ->
  195. send(RTMP, prepare_notify(StreamId, Name, Args)).
  196. prepare_notify(StreamId, Name, Args) ->
  197. Arg = {object, lists:ukeymerge(1, [{level, <<"status">>}], lists:keysort(1, Args))},
  198. #rtmp_message{type = metadata, channel_id = rtmp_lib:channel_id(audio, StreamId), stream_id = StreamId, body = [Name, Arg], timestamp = same}.
  199. prepare_status(StreamId, Code) when is_list(Code) ->
  200. prepare_status(StreamId, list_to_binary(Code), <<"-">>);
  201. prepare_status(StreamId, Code) when is_binary(Code) ->
  202. prepare_status(StreamId, Code, <<"-">>).
  203. -spec(prepare_status(StreamId::non_neg_integer(), Code::any_string(), Description::any_string()) -> rtmp_message()).
  204. prepare_status(StreamId, Code, Description) ->
  205. Arg = {object, [
  206. {code, Code},
  207. {level, <<"status">>},
  208. {description, Description}
  209. ]},
  210. prepare_invoke(StreamId, onStatus, [Arg]).
  211. status(RTMP, StreamId, Code, Description) ->
  212. send(RTMP, prepare_status(StreamId, Code, Description)).
  213. -spec(status(RTMP::rtmp_socket_pid(), StreamId::integer(), Code::any_string()) -> ok).
  214. status(RTMP, StreamId, Code) when is_list(Code) ->
  215. status(RTMP, StreamId, list_to_binary(Code), <<"-">>);
  216. status(RTMP, StreamId, Code) when is_binary(Code) ->
  217. status(RTMP, StreamId, Code, <<"-">>).
  218. prepare_invoke(StreamId, Command, Args) when is_integer(StreamId) ->
  219. AMF = #rtmp_funcall{
  220. command = Command,
  221. type = invoke,
  222. id = 0,
  223. stream_id = StreamId,
  224. args = [null | Args ]},
  225. #rtmp_message{stream_id = StreamId, type = invoke, body = AMF}.
  226. invoke(RTMP, StreamId, Command, Args) ->
  227. send(RTMP, prepare_invoke(StreamId, Command, Args)).
  228. invoke(RTMP, #rtmp_funcall{stream_id = StreamId, id = undefined} = AMF) ->
  229. InvokeId = gen_fsm:sync_send_all_state_event(RTMP, next_invoke_id),
  230. send(RTMP, #rtmp_message{stream_id = StreamId, type = invoke, body = AMF#rtmp_funcall{id = InvokeId}}),
  231. {ok, InvokeId};
  232. invoke(RTMP, #rtmp_funcall{stream_id = StreamId, id = InvokeId} = AMF) ->
  233. send(RTMP, #rtmp_message{stream_id = StreamId, type = invoke, body = AMF}),
  234. {ok, InvokeId}.
  235. %% @private
  236. init([accept]) ->
  237. {ok, wait_for_socket_on_server, #rtmp_socket{channels = {}, out_channels = {}, active = false}, ?RTMP_TIMEOUT};
  238. init([connect]) ->
  239. {ok, wait_for_socket_on_client, #rtmp_socket{channels = {}, out_channels = {}, active = false}, ?RTMP_TIMEOUT}.
  240. %% @private
  241. wait_for_socket_on_server(timeout, State) ->
  242. {stop, normal, State};
  243. wait_for_socket_on_server({socket, Socket}, #rtmp_socket{} = State) when is_port(Socket) ->
  244. case inet:peername(Socket) of
  245. {ok, {IP, Port}} ->
  246. ok = inet:setopts(Socket, [{active, once}, {packet, raw}, binary, {send_timeout, 3000}]),
  247. {next_state, handshake_c1, State#rtmp_socket{socket = Socket, address = IP, port = Port}, ?RTMP_TIMEOUT};
  248. {error, _Error} ->
  249. {stop, normal, State}
  250. end;
  251. wait_for_socket_on_server({socket, Socket}, #rtmp_socket{} = State) when is_pid(Socket) ->
  252. erlang:monitor(process, Socket),
  253. {next_state, handshake_c1, State#rtmp_socket{socket = Socket}, ?RTMP_TIMEOUT}.
  254. %% @private
  255. -spec(wait_for_socket_on_client(Message::any(), Socket::rtmp_socket()) -> no_return()).
  256. wait_for_socket_on_client(timeout, State) ->
  257. {stop, normal, State};
  258. wait_for_socket_on_client({socket, Socket}, #rtmp_socket{} = State) ->
  259. inet:setopts(Socket, [{active, once}, {packet, raw}, binary]),
  260. {ok, {IP, Port}} = inet:peername(Socket),
  261. State1 = State#rtmp_socket{socket = Socket, address = IP, port = Port},
  262. send_data(State1, rtmp_handshake:c1()),
  263. {next_state, handshake_s1, State1, ?RTMP_TIMEOUT}.
  264. %% @private
  265. handshake_c1(timeout, State) ->
  266. {stop, normal, State}.
  267. %% @private
  268. handshake_c3(timeout, State) ->
  269. {stop, normal, State}.
  270. %% @private
  271. handshake_s1(timeout, State) ->
  272. {stop, normal, State}.
  273. %% @private
  274. loop(timeout, #rtmp_socket{pinged = false} = State) ->
  275. State1 = send_data(State, #rtmp_message{type = ping}),
  276. {next_state, loop, State1#rtmp_socket{pinged = true}, ?RTMP_TIMEOUT};
  277. loop(timeout, #rtmp_socket{consumer = Consumer} = State) ->
  278. Consumer ! {rtmp, self(), timeout},
  279. {stop, normal, State}.
  280. %% @private
  281. loop(#rtmp_message{} = Message, _From, State) ->
  282. State1 = send_data(State, Message),
  283. {reply, ok, loop, State1, ?RTMP_TIMEOUT}.
  284. -type(rtmp_option() ::active|amf_version|chunk_size|window_size|client_buffer|address).
  285. % -type(rtmp_option_value() ::{rtmp_option(), any()}).
  286. -spec(get_options(State::rtmp_socket(), Key::rtmp_option()|[rtmp_option()]) -> term()).
  287. get_options(State, active) ->
  288. {active, State#rtmp_socket.active};
  289. get_options(State, amf_version) ->
  290. {amf_version, State#rtmp_socket.amf_version};
  291. get_options(State, chunk_size) ->
  292. {chunk_size, State#rtmp_socket.server_chunk_size};
  293. get_options(State, window_size) ->
  294. {window_size, State#rtmp_socket.window_size};
  295. get_options(State, url) ->
  296. {url, State#rtmp_socket.url};
  297. get_options(State, client_buffer) ->
  298. {client_buffer, State#rtmp_socket.client_buffer};
  299. get_options(State, debug) ->
  300. {debug, State#rtmp_socket.debug};
  301. get_options(State, address) ->
  302. {address, {State#rtmp_socket.address, State#rtmp_socket.port}};
  303. get_options(_State, []) ->
  304. [];
  305. get_options(State, [Key | Options]) ->
  306. [get_options(State, Key) | get_options(State, Options)].
  307. get_stat(State) ->
  308. get_stat(State, [recv_oct, send_oct]).
  309. get_stat(State, recv_oct) ->
  310. {recv_oct, State#rtmp_socket.bytes_read};
  311. get_stat(State, send_oct) ->
  312. {send_oct, State#rtmp_socket.bytes_sent};
  313. get_stat(_State, []) ->
  314. [];
  315. get_stat(State, [Key | Options]) ->
  316. [get_stat(State, Key) | get_stat(State, Options)].
  317. set_options(State, [{amf_version, Version} | Options]) ->
  318. set_options(State#rtmp_socket{amf_version = Version}, Options);
  319. set_options(#rtmp_socket{socket = Socket, buffer = Data} = State, [{active, Active} | Options]) ->
  320. State1 = flush_send(State#rtmp_socket{active = Active}),
  321. State2 = case Active of
  322. false ->
  323. inet:setopts(Socket, [{active, false}]),
  324. State1;
  325. true ->
  326. inet:setopts(Socket, [{active, true}]),
  327. State1;
  328. once when size(Data) > 0 ->
  329. handle_rtmp_data(State1);
  330. once ->
  331. activate_socket(State),
  332. State1
  333. end,
  334. set_options(State2, Options);
  335. set_options(#rtmp_socket{} = State, [{debug, Debug} | Options]) ->
  336. io:format("Set debug to ~p~n", [Debug]),
  337. set_options(State#rtmp_socket{debug = Debug}, Options);
  338. set_options(#rtmp_socket{} = State, [{url, URL} | Options]) ->
  339. set_options(State#rtmp_socket{url = URL}, Options);
  340. set_options(#rtmp_socket{consumer = undefined} = State, [{consumer, Consumer} | Options]) ->
  341. erlang:monitor(process, Consumer),
  342. set_options(State#rtmp_socket{consumer = Consumer}, Options);
  343. set_options(State, [{chunk_size, ChunkSize} | Options]) ->
  344. State1 = send_data(State, #rtmp_message{type = chunk_size, body = ChunkSize}),
  345. set_options(State1#rtmp_socket{server_chunk_size = ChunkSize}, Options);
  346. set_options(State, []) -> State.
  347. %%-------------------------------------------------------------------------
  348. %% Func: handle_event/3
  349. %% Returns: {next_state, NextStateName, NextStateData} |
  350. %% {next_state, NextStateName, NextStateData, Timeout} |
  351. %% {stop, Reason, NewStateData}
  352. %% @private
  353. %%-------------------------------------------------------------------------
  354. handle_event({setopts, Options}, StateName, State) ->
  355. NewState = set_options(State, Options),
  356. {next_state, StateName, NewState, ?RTMP_TIMEOUT};
  357. handle_event(Event, StateName, StateData) ->
  358. {stop, {StateName, undefined_event, Event}, StateData}.
  359. %%-------------------------------------------------------------------------
  360. %% Func: handle_sync_event/4
  361. %% Returns: {next_state, NextStateName, NextStateData} |
  362. %% {next_state, NextStateName, NextStateData, Timeout} |
  363. %% {reply, Reply, NextStateName, NextStateData} |
  364. %% {reply, Reply, NextStateName, NextStateData, Timeout} |
  365. %% {stop, Reason, NewStateData} |
  366. %% {stop, Reason, Reply, NewStateData}
  367. %% @private
  368. %%-------------------------------------------------------------------------
  369. handle_sync_event(next_invoke_id, _From, StateName, #rtmp_socket{out_invoke_id = Id} = Socket) ->
  370. {reply, Id, StateName, Socket#rtmp_socket{out_invoke_id = Id + 1}, ?RTMP_TIMEOUT};
  371. handle_sync_event({getopts, Options}, _From, StateName, State) ->
  372. {reply, get_options(State, Options), StateName, State, ?RTMP_TIMEOUT};
  373. handle_sync_event({getstat, Options}, _From, StateName, State) ->
  374. {reply, get_stat(State, Options), StateName, State, ?RTMP_TIMEOUT};
  375. handle_sync_event(getstat, _From, StateName, State) ->
  376. {reply, get_stat(State), StateName, State, ?RTMP_TIMEOUT};
  377. handle_sync_event(get_socket, _From, StateName, #rtmp_socket{socket = Socket} = State) when is_port(Socket)->
  378. {reply, {rtmp,Socket}, StateName, State, ?RTMP_TIMEOUT};
  379. handle_sync_event(get_socket, _From, StateName, #rtmp_socket{socket = Socket} = State) when is_pid(Socket)->
  380. {reply, {rtmpt,undefined}, StateName, State, ?RTMP_TIMEOUT};
  381. handle_sync_event({setopts, Options}, _From, StateName, State) ->
  382. NewState = set_options(State, Options),
  383. {reply, ok, StateName, NewState, ?RTMP_TIMEOUT};
  384. handle_sync_event({notify_audio, StreamId, DTS}, _From, loop, Socket) ->
  385. {reply, ok, loop, send_audio_notify(Socket, StreamId, DTS), ?RTMP_TIMEOUT};
  386. handle_sync_event({notify_video, StreamId, DTS}, _From, loop, Socket) ->
  387. {reply, ok, loop, send_video_notify(Socket, StreamId, DTS), ?RTMP_TIMEOUT};
  388. handle_sync_event(Event, _From, StateName, StateData) ->
  389. {stop, {StateName, undefined_event, Event}, StateData}.
  390. %%-------------------------------------------------------------------------
  391. %% Func: handle_info/3
  392. %% Returns: {next_state, NextStateName, NextStateData} |
  393. %% {next_state, NextStateName, NextStateData, Timeout} |
  394. %% {stop, Reason, NewStateData}
  395. %% @private
  396. %%-------------------------------------------------------------------------
  397. handle_info({tcp, Socket, Data}, handshake_c1, #rtmp_socket{socket=Socket, buffer = Buffer, bytes_read = BytesRead} = State) when size(Buffer) + size(Data) < ?HS_BODY_LEN + 1 ->
  398. activate_socket(State),
  399. {next_state, handshake_c1, State#rtmp_socket{buffer = <<Buffer/binary, Data/binary>>, bytes_read = BytesRead + size(Data)}, ?RTMP_TIMEOUT};
  400. handle_info({tcp, Socket, Data}, handshake_c1, #rtmp_socket{socket=Socket, buffer = Buffer, bytes_read = BytesRead} = State) ->
  401. activate_socket(State),
  402. <<Handshake:(?HS_BODY_LEN+1)/binary, Rest/binary>> = <<Buffer/binary, Data/binary>>,
  403. State1 = case rtmp_handshake:server(Handshake) of
  404. {uncrypted, Reply} ->
  405. send_data(State, Reply);
  406. {crypted, Reply, KeyIn, KeyOut} ->
  407. NewState = send_data(State, Reply),
  408. ?D({"Established RTMPE connection"}),
  409. NewState#rtmp_socket{key_in = KeyIn, key_out = KeyOut}
  410. end,
  411. {next_state, 'handshake_c3', State1#rtmp_socket{buffer = Rest, bytes_read = BytesRead + size(Data)}, ?RTMP_TIMEOUT};
  412. handle_info({tcp, Socket, Data}, handshake_c3, #rtmp_socket{socket=Socket, buffer = Buffer, bytes_read = BytesRead} = State) when size(Buffer) + size(Data) < ?HS_BODY_LEN ->
  413. activate_socket(State),
  414. {next_state, handshake_c3, State#rtmp_socket{buffer = <<Buffer/binary, Data/binary>>, bytes_read = BytesRead + size(Data)}, ?RTMP_TIMEOUT};
  415. handle_info({tcp, Socket, Data}, handshake_c3, #rtmp_socket{socket=Socket, consumer = Consumer, buffer = Buffer, bytes_read = BytesRead, active = Active, key_in = KeyIn} = State) ->
  416. <<_HandShakeC3:?HS_BODY_LEN/binary, CryptedData/binary>> = <<Buffer/binary, Data/binary>>,
  417. Consumer ! {rtmp, self(), connected},
  418. case Active of
  419. true -> inet:setopts(Socket, [{active, true}]);
  420. _ -> ok
  421. end,
  422. {NewKeyIn, Rest} = case KeyIn of
  423. undefined -> {undefined, CryptedData};
  424. _ -> rtmpe:crypt(KeyIn, CryptedData)
  425. end,
  426. % ?D({decrypt, Rest, CryptedData == element(2, rtmpe:crypt(NewKeyIn, Rest))}),
  427. State1 = State#rtmp_socket{bytes_read = BytesRead + size(Data), key_in = NewKeyIn, buffer = Rest},
  428. case Rest of
  429. <<>> -> {next_state, loop, State1, ?RTMP_TIMEOUT};
  430. _ -> {next_state, loop, handle_rtmp_data(State1), ?RTMP_TIMEOUT}
  431. end;
  432. handle_info({tcp, Socket, Data}, handshake_s1, #rtmp_socket{socket=Socket, buffer = Buffer, bytes_read = BytesRead} = State) when size(Buffer) + size(Data) < ?HS_BODY_LEN*2 + 1 ->
  433. activate_socket(State),
  434. {next_state, handshake_s1, State#rtmp_socket{buffer = <<Buffer/binary, Data/binary>>, bytes_read = BytesRead + size(Data)}, ?RTMP_TIMEOUT};
  435. handle_info({tcp, Socket, Data}, handshake_s1, #rtmp_socket{socket=Socket, consumer = Consumer, buffer = Buffer, bytes_read = BytesRead, active = Active} = State) ->
  436. <<?HS_UNCRYPTED, S1:?HS_BODY_LEN/binary, _S2:?HS_BODY_LEN/binary, Rest/binary>> = <<Buffer/binary, Data/binary>>,
  437. send_data(State, rtmp_handshake:c2(S1)),
  438. Consumer ! {rtmp, self(), connected},
  439. case Active of
  440. true -> inet:setopts(Socket, [{active, true}]);
  441. _ -> ok
  442. end,
  443. {next_state, loop, handle_rtmp_data(State#rtmp_socket{bytes_read = BytesRead + size(Data), buffer = Rest}), ?RTMP_TIMEOUT};
  444. handle_info({tcp, Socket, CryptedData}, loop, #rtmp_socket{socket=Socket, buffer = Buffer, bytes_read = BytesRead, bytes_unack = BytesUnack, key_in = KeyIn} = State) ->
  445. State1 = flush_send(State),
  446. {NewKeyIn, Data} = case KeyIn of
  447. undefined -> {undefined, CryptedData};
  448. _ -> rtmpe:crypt(KeyIn, CryptedData)
  449. end,
  450. {next_state, loop, handle_rtmp_data(State1#rtmp_socket{bytes_read = BytesRead + size(Data), bytes_unack = BytesUnack + size(Data), key_in = NewKeyIn, buffer = <<Buffer/binary, Data/binary>>}), ?RTMP_TIMEOUT};
  451. handle_info({tcp_closed, Socket}, _StateName, #rtmp_socket{socket = Socket, consumer = Consumer} = StateData) ->
  452. Consumer ! {rtmp, self(), disconnect, get_stat(StateData)},
  453. {stop, normal, StateData};
  454. handle_info(#rtmp_message{} = Message, loop, State) ->
  455. State1 = send_data(State, Message),
  456. State2 = flush_send(State1),
  457. {next_state, loop, State2, ?RTMP_TIMEOUT};
  458. handle_info({rtmpt, RTMPT, alive}, StateName, #rtmp_socket{socket = RTMPT} = State) ->
  459. {next_state, StateName, State#rtmp_socket{pinged = false}, ?RTMP_TIMEOUT};
  460. handle_info({rtmpt, RTMPT, Data}, StateName, State) ->
  461. handle_info({tcp, RTMPT, Data}, StateName, State);
  462. handle_info({'DOWN', _, process, _Client, _Reason}, _StateName, State) ->
  463. {stop, normal, State};
  464. handle_info({tcp_paused, _Socket}, StateName, StateData) ->
  465. {next_state, StateName, StateData, ?RTMP_TIMEOUT};
  466. handle_info({tcp_resumed, _Socket}, StateName, StateData) ->
  467. {next_state, StateName, StateData, ?RTMP_TIMEOUT};
  468. handle_info(stop, _StateName, State) ->
  469. {stop, normal, State};
  470. handle_info(_Info, StateName, StateData) ->
  471. error_logger:error_msg("Unknown message to rtmp socket: ~p ~p ~p~n", [_Info, StateName, StateData]),
  472. {next_state, StateName, StateData, ?RTMP_TIMEOUT}.
  473. % flush_send(State) -> flush_send([], State).
  474. flush_send(State) ->
  475. receive
  476. #rtmp_message{} = Message ->
  477. NewState = send_data(State, Message),
  478. % ?D({out, Message}),
  479. % {NewState, Data} = rtmp:encode(State, Message),
  480. % flush_send([Data | Packet], NewState)
  481. flush_send(NewState)
  482. after
  483. 0 -> State
  484. end.
  485. flush_all() ->
  486. receive
  487. _Msg -> flush_all()
  488. after
  489. 0 -> ok
  490. end.
  491. activate_socket(#rtmp_socket{socket = Socket}) when is_port(Socket) ->
  492. inet:setopts(Socket, [{active, once}]);
  493. activate_socket(#rtmp_socket{socket = Socket}) when is_pid(Socket) ->
  494. ok.
  495. send_audio_notify(Socket, StreamId, DTS) ->
  496. send_data(Socket#rtmp_socket{sent_audio_notify = true}, rtmp_lib:empty_audio(StreamId, DTS)).
  497. send_video_notify(Socket, StreamId, DTS) ->
  498. Msg = [
  499. #rtmp_message{type = video, channel_id = rtmp_lib:channel_id(video, StreamId), timestamp = DTS, stream_id = StreamId, body = <<87,0>>, ts_type = new},
  500. #rtmp_message{type = video, channel_id = rtmp_lib:channel_id(video, StreamId), timestamp = DTS, stream_id = StreamId, body = <<23,2,0,0,0>>, ts_type = new},
  501. #rtmp_message{type = video, channel_id = rtmp_lib:channel_id(video, StreamId), timestamp = DTS, stream_id = StreamId, body = <<87,1>>, ts_type = delta}
  502. ],
  503. lists:foldl(fun(M, State1) ->
  504. send_data(State1, M)
  505. end, Socket#rtmp_socket{sent_video_notify = true}, Msg).
  506. send_data(#rtmp_socket{sent_audio_notify = false} = Socket,
  507. #rtmp_message{type = audio, timestamp = DTS, stream_id = StreamId, body = Body} = Message) when size(Body) > 0 ->
  508. State1 = send_audio_notify(Socket, StreamId, DTS),
  509. send_data(State1, Message#rtmp_message{ts_type = new});
  510. send_data(#rtmp_socket{sent_video_notify = false} = Socket, #rtmp_message{type = video, timestamp = DTS, stream_id = StreamId} = Message) ->
  511. State2 = send_video_notify(Socket, StreamId, DTS),
  512. send_data(State2, Message);
  513. send_data(#rtmp_socket{sent_audio_notify = true} = Socket, #rtmp_message{type = stream_end} = Message) ->
  514. send_data(Socket#rtmp_socket{sent_audio_notify = false}, Message);
  515. send_data(#rtmp_socket{sent_video_notify = true} = Socket, #rtmp_message{type = stream_end} = Message) ->
  516. send_data(Socket#rtmp_socket{sent_video_notify = false}, Message);
  517. send_data(#rtmp_socket{socket = Socket, key_out = KeyOut} = State, Message) ->
  518. case State#rtmp_socket.debug of
  519. true -> print_rtmp_message(out, Message);
  520. _ -> ok
  521. end,
  522. {NewState, Data} = case Message of
  523. #rtmp_message{} ->
  524. % ?D({Socket,Message#rtmp_message.type, Message#rtmp_message.timestamp, Message#rtmp_message.ts_type}),
  525. rtmp:encode(State, Message);
  526. _ ->
  527. {State, Message}
  528. end,
  529. {NewState1, Crypt} = case KeyOut of
  530. undefined -> {NewState, Data};
  531. _ ->
  532. {NewKeyOut, OutCrypt} = rtmpe:crypt(KeyOut, Data),
  533. {NewState#rtmp_socket{key_out = NewKeyOut}, OutCrypt}
  534. end,
  535. % (catch rtmp_stat_collector:out_bytes(self(), iolist_size(Crypt))),
  536. if
  537. is_port(Socket) ->
  538. case gen_tcp:send(Socket, Crypt) of
  539. ok -> ok;
  540. {error, timeout} -> flush_all(), throw({stop, network_timeout, NewState1});
  541. {error, Else} -> flush_all(), throw({stop, {network_error,Else}, NewState1})
  542. end;
  543. is_pid(Socket) ->
  544. rtmpt:write(Socket, Crypt)
  545. end,
  546. NewState1.
  547. handle_rtmp_data(#rtmp_socket{buffer = <<>>} = State) ->
  548. % ?D({empty_input, erlang:get_stacktrace()}),
  549. State;
  550. handle_rtmp_data(#rtmp_socket{bytes_unack = Bytes, window_size = Window} = State) when Bytes >= Window ->
  551. State1 = send_data(State, #rtmp_message{type = ack_read}),
  552. handle_rtmp_data(State1);
  553. handle_rtmp_data(#rtmp_socket{buffer = Data} = State) ->
  554. got_rtmp_message(rtmp:decode(State, Data)).
  555. print_rtmp_message(InOut, #rtmp_message{channel_id = Channel, ts_type = TSType, timestamp = TS, type = Type, stream_id = StreamId, body = Body}) ->
  556. DecodedBody = case Type of
  557. video when size(Body) > 10 -> erlang:setelement(5, flv:decode_video_tag(Body), size(Body));
  558. audio when size(Body) > 0 -> erlang:setelement(7, flv:decode_audio_tag(Body), size(Body));
  559. _ -> Body
  560. end,
  561. io:format("~p ~p ~p ~p ~p ~p ~p~n", [InOut, Channel, TSType, TS, Type, StreamId, DecodedBody]),
  562. ok;
  563. print_rtmp_message(InOut, Msg) ->
  564. io:format("~p ~p~n", [InOut, Msg]),
  565. ok.
  566. got_rtmp_message({#rtmp_socket{debug = true}, Msg, _} = Message) ->
  567. print_rtmp_message(in, Msg),
  568. handle_rtmp_message(Message);
  569. got_rtmp_message(Decoded) ->
  570. handle_rtmp_message(Decoded).
  571. handle_rtmp_message({#rtmp_socket{consumer = Consumer} = State, #rtmp_message{type = window_size, body = Size} = Message, Rest}) ->
  572. Consumer ! {rtmp, self(), Message},
  573. rtmp_message_sent(State#rtmp_socket{window_size = Size, buffer = Rest});
  574. handle_rtmp_message({#rtmp_socket{consumer = Consumer, pinged = true} = State, #rtmp_message{type = pong} = Message, Rest}) ->
  575. Consumer ! {rtmp, self(), Message},
  576. rtmp_message_sent(State#rtmp_socket{pinged = false, buffer = Rest});
  577. handle_rtmp_message({#rtmp_socket{consumer = Consumer} = State, #rtmp_message{type = ping, body = Timestamp} = Message, Rest}) ->
  578. Consumer ! {rtmp, self(), Message},
  579. send_data(State, #rtmp_message{type = pong, body = Timestamp}),
  580. rtmp_message_sent(State#rtmp_socket{buffer = Rest});
  581. handle_rtmp_message({#rtmp_socket{consumer = Consumer} = State, Message, Rest}) ->
  582. Consumer ! {rtmp, self(), Message},
  583. rtmp_message_sent(State#rtmp_socket{buffer = Rest});
  584. handle_rtmp_message({#rtmp_socket{} = State, Rest}) ->
  585. activate_socket(State),
  586. State#rtmp_socket{buffer = Rest}.
  587. rtmp_message_sent(#rtmp_socket{active = true} = State) ->
  588. activate_socket(State),
  589. handle_rtmp_data(State);
  590. rtmp_message_sent(#rtmp_socket{active = _} = State) ->
  591. handle_rtmp_data(State);
  592. rtmp_message_sent(State) ->
  593. State.
  594. %%-------------------------------------------------------------------------
  595. %% Func: terminate/3
  596. %% Purpose: Shutdown the fsm
  597. %% Returns: any
  598. %% @private
  599. %%-------------------------------------------------------------------------
  600. terminate(_Reason, _StateName, #rtmp_socket{socket=Socket}) when is_pid(Socket) ->
  601. gen_server:cast(Socket, close),
  602. ok;
  603. terminate(_Reason, _StateName, _State) ->
  604. ok.
  605. %%-------------------------------------------------------------------------
  606. %% Func: code_change/4
  607. %% Purpose: Convert process state when code is changed
  608. %% Returns: {ok, NewState, NewStateData}
  609. %% @private
  610. %%-------------------------------------------------------------------------
  611. code_change(_OldVersion, _StateName, _State, _Extra) ->
  612. ok.