PageRenderTime 85ms CodeModel.GetById 2ms app.highlight 75ms 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
  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
 52-export([accept/1, connect/1, connect/2, start_link/1, getopts/2, setopts/2, getstat/2, getstat/1, send/2, get_socket/1]).
 53-export([status/3, status/4, prepare_status/2, prepare_status/3, invoke/2, invoke/4, prepare_invoke/3, notify/4, prepare_notify/3]).
 54-export([notify_audio/3, notify_video/3]).
 55
 56-export([start_socket/2, start_server/3, start_server/4, stop_server/1, set_socket/2]).
 57-export([close/1]).
 58  
 59
 60
 61%% gen_fsm callbacks
 62-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
 63
 64-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]).
 65
 66%% @spec (Port::integer(), Name::atom(), Callback::atom()) -> {ok, Pid::pid()}
 67%% @doc Starts RTMP listener on port Port, registered under name Name with callback module Callback.
 68%% Callback must export one function: create_client/1
 69%% create_client(RTMPSocket::pid()) -> {ok, Pid::pid()}
 70%%
 71%% This function receives RTMPSocket, that is ready to send messages and after this callback function returns, this socket
 72%% will send rtmp_message as it is defined in overview.
 73%% @end
 74-spec(start_server(Port::integer(), Name::atom(), Callback::atom()) -> {ok, Pid::pid()}).
 75start_server(Port, Name, Callback) ->
 76  rtmp_sup:start_rtmp_listener(Port, Name, Callback, []).
 77
 78start_server(Port, Name, Callback, Args) ->
 79  rtmp_sup:start_rtmp_listener(Port, Name, Callback, Args).
 80
 81stop_server(Name) ->
 82  rtmp_sup:stop_rtmp_listener(Name).
 83
 84%% @spec (Socket::port()) -> {ok, RTMP::pid()}
 85%% @doc Accepts client connection on socket Socket, starts RTMP decoder, passes socket to it
 86%% and returns pid of newly created RTMP socket.
 87%% @end
 88-spec(accept(Socket::port()) -> {ok, RTMPSocket::pid()}).
 89accept(Socket) ->
 90  {ok, Pid} = start_socket(accept, Socket),
 91  setopts(Pid, [{consumer,self()}]),
 92  {ok,Pid}.
 93  
 94%% @spec (Socket::port()) -> {ok, RTMP::pid()}
 95%% @doc Accepts client connection on socket Socket, starts RTMP decoder, passes socket to it
 96%% and returns pid of newly created RTMP socket.
 97%% @end
 98-spec(connect(Socket::port()|string()) -> {ok, RTMPSocket::pid()}).
 99connect(ServerSpec) ->
100  connect(ServerSpec, [{timeout, 10000}]).
101
102-spec(connect(Socket::port()|string(), Options::list()) -> {ok, RTMPSocket::pid()}).
103connect(ServerSpec, Options) when is_binary(ServerSpec) ->
104  connect(binary_to_list(ServerSpec), Options);
105
106connect(ServerSpec, Options) when is_list(ServerSpec) ->
107  {_Proto,_Auth,Host,Port,_Path,_Query} = http_uri2:parse(ServerSpec),
108  Timeout = proplists:get_value(timeout, Options, 10000),
109  case gen_tcp:connect(Host, Port, [binary, {active, false}, {packet, raw}], Timeout) of
110    {ok, Socket} ->
111      inet:setopts(Socket, [{send_timeout,proplists:get_value(send_timeout,Options,Timeout)}]),
112      {ok, Pid} = connect(Socket),
113      setopts(Pid, [{url, ServerSpec}]),
114      {ok, Pid};
115    {error, Reason} ->
116      {error, Reason}
117  end;
118
119connect(Socket, _Options) when is_port(Socket) ->
120  {ok, Pid} = start_socket(connect, Socket),
121  setopts(Pid, [{consumer,self()}]),
122  {ok,Pid}.
123
124%% @spec (Type::accept|connect, Socket::port) -> {ok, RTMP::pid()}
125%% @doc Starts RTMP socket with provided consumer and inititiate server or client connection
126%% @end
127-spec(start_socket(Type::connect|accept, Socket::port()) -> {ok, RTMP::pid()}).
128start_socket(Type, Socket) ->
129  {ok, RTMP} = rtmp_sup:start_rtmp_socket(Type),
130  case Socket of
131    _ when is_port(Socket) -> gen_tcp:controlling_process(Socket, RTMP);
132    _ -> ok
133  end,
134  set_socket(RTMP, Socket),
135  {ok, RTMP}.
136
137
138set_socket(RTMP, Socket) ->
139    gen_fsm:send_event(RTMP, {socket, Socket}).
140
141get_socket(RTMP) -> 
142      gen_fsm:sync_send_all_state_event(RTMP, get_socket, ?RTMP_TIMEOUT).
143
144notify_audio(RTMP, StreamId, DTS) ->
145  gen_fsm:sync_send_all_state_event(RTMP, {notify_audio, StreamId, DTS}).
146
147notify_video(RTMP, StreamId, DTS) ->
148  gen_fsm:sync_send_all_state_event(RTMP, {notify_video, StreamId, DTS}).
149
150close(Socket) ->
151  Socket ! stop.
152  
153%% @private
154start_link(Type) ->
155  gen_fsm:start_link(?MODULE, [Type], []).
156  
157  
158%% @spec (RTMP::pid(), Options::[{Key, Value}]|{Key, Value}) -> ok
159%% @doc Just the same as {@link inet:setopts/2. inet:setopts/2} this function changes state of 
160%% rtmp socket.<br/>
161%%  Available options:
162%%  <ul><li><code>chunk_size</code> - change outgoing chunk size</li>
163%%  <li><code>window_size</code> - ask remote client to send read acknowlegement after WindowSize bytes</li>
164%%  <li><code>amf_version</code> - change AMF0 to AMF3, but only before first call</li>
165%%  <li><code>consumer</code> - change messages consumer</li>
166%%  <li><code>debug = true|false</code> - dump all important packets or no</li>
167%% </ul>
168%% @end
169-spec(setopts(RTMP::rtmp_socket_pid(), Options::[{Key::atom(), Value::any()}]) -> ok).
170setopts(RTMP, Options) ->
171  gen_fsm:sync_send_all_state_event(RTMP, {setopts, Options}).
172
173%% @spec (RTMP::pid(), Options::[{Key, Value}]|{Key, Value}) -> ok
174%% @doc Just the same as {@link inet:getopts/2. inet:getopts/2} this function gets state of 
175%% rtmp socket.<br/>
176%%  Available options:
177%%  <ul>
178%%  <li><code>chunk_size</code> - get outgoing chunk size</li>
179%%  <li><code>window_size</code> - get remote client read acknowlegement window size bytes</li>
180%%  <li><code>amf_version</code> - get AMF0 or AMF3</li>
181%%  <li><code>consumer</code> - get messages consumer</li>
182%%  <li><code>address</code> - get remote client IP and port</li>
183%% </ul>
184%% @end
185-spec(getopts(RTMP::rtmp_socket_pid(), Options::atom()|[Key::atom()]) -> any()).
186getopts(RTMP, Options) ->
187  gen_fsm:sync_send_all_state_event(RTMP, {getopts, Options}, ?RTMP_TIMEOUT).
188
189%% @spec (RTMP::pid(), Stats::[Key]) -> Values::[{Key,Value}]
190%% @doc Just the same as {@link inet:getstats/2. inet:getstats/2} this function gets statistics of 
191%% rtmp socket.<br/>
192%%  Available options:
193%%  <ul>
194%%  <li><code>recv_oct</code> - number of bytes received to the socket.</li>
195%%  <li><code>send_oct</code> - number of bytes sent from the socket</li>
196%% </ul>
197%% @end
198-spec(getstat(RTMP::rtmp_socket_pid(), Options::[Key::atom()]) -> ok).
199getstat(RTMP, Options) ->
200  gen_fsm:sync_send_all_state_event(RTMP, {getstat, Options}, ?RTMP_TIMEOUT).
201
202%% @spec (RTMP::pid()) -> Values::[{Key,Value}]
203%% @doc Just the same as {@link inet:getstats/1. inet:getstats/1} this function gets statistics of 
204%% rtmp socket.<br/>
205-spec(getstat(RTMP::rtmp_socket_pid()) -> ok).
206getstat(RTMP) ->
207  gen_fsm:sync_send_all_state_event(RTMP, getstat, ?RTMP_TIMEOUT).
208
209  
210%% @spec (RTMP::pid(), Message::rtmp_message()) -> ok
211%% @doc Sends message to client.
212%% @end
213-spec(send(RTMP::rtmp_socket_pid(), Message::rtmp_message()) -> ok).
214send(RTMP, Message) ->
215  % io:format("Message ~p ~p ~p~n", [Message#rtmp_message.type, Message#rtmp_message.timestamp, Message#rtmp_message.stream_id]),
216  % case process_info(RTMP, message_queue_len) of
217  %   {message_queue_len, Length} when Length < 20 -> RTMP ! Message;
218  %   {message_queue_len, Length} -> gen_fsm:sync_send_event(RTMP, Message, ?RTMP_TIMEOUT);
219  %   _ -> ok
220  % end,
221  RTMP ! Message,
222  ok.
223  
224
225
226notify(RTMP, StreamId, Name, Args) ->
227  send(RTMP, prepare_notify(StreamId, Name, Args)).
228
229prepare_notify(StreamId, Name, Args) ->
230  Arg = {object, lists:ukeymerge(1, [{level, <<"status">>}], lists:keysort(1, Args))},
231  #rtmp_message{type = metadata, channel_id = rtmp_lib:channel_id(audio, StreamId), stream_id = StreamId, body = [Name, Arg], timestamp = same}.
232
233
234prepare_status(StreamId, Code) when is_list(Code) ->
235  prepare_status(StreamId, list_to_binary(Code), <<"-">>);
236
237prepare_status(StreamId, Code) when is_binary(Code) ->
238  prepare_status(StreamId, Code, <<"-">>).
239
240
241-spec(prepare_status(StreamId::non_neg_integer(), Code::any_string(), Description::any_string()) -> rtmp_message()).
242prepare_status(StreamId, Code, Description) ->
243  Arg = {object, [
244    {code, Code}, 
245    {level, <<"status">>}, 
246    {description, Description}
247  ]},
248  prepare_invoke(StreamId, onStatus, [Arg]).
249
250
251status(RTMP, StreamId, Code, Description) ->
252  send(RTMP, prepare_status(StreamId, Code, Description)).
253
254-spec(status(RTMP::rtmp_socket_pid(), StreamId::integer(), Code::any_string()) -> ok).
255status(RTMP, StreamId, Code) when is_list(Code) ->
256  status(RTMP, StreamId, list_to_binary(Code), <<"-">>);
257
258status(RTMP, StreamId, Code) when is_binary(Code) ->
259  status(RTMP, StreamId, Code, <<"-">>).
260
261  
262prepare_invoke(StreamId, Command, Args) when is_integer(StreamId) ->
263  AMF = #rtmp_funcall{
264      command = Command,
265        type = invoke,
266        id = 0,
267        stream_id = StreamId,
268        args = [null | Args ]},
269  #rtmp_message{stream_id = StreamId, type = invoke, body = AMF}.
270
271
272invoke(RTMP, StreamId, Command, Args) ->
273  send(RTMP, prepare_invoke(StreamId, Command, Args)).
274
275invoke(RTMP, #rtmp_funcall{stream_id = StreamId, id = undefined} = AMF) ->
276  InvokeId = gen_fsm:sync_send_all_state_event(RTMP, next_invoke_id),
277  send(RTMP, #rtmp_message{stream_id = StreamId, type = invoke, body = AMF#rtmp_funcall{id = InvokeId}}),
278  {ok, InvokeId};
279
280invoke(RTMP, #rtmp_funcall{stream_id = StreamId, id = InvokeId} = AMF) ->
281  send(RTMP, #rtmp_message{stream_id = StreamId, type = invoke, body = AMF}),
282  {ok, InvokeId}.
283
284  
285%% @private  
286init([accept]) ->
287  {ok, wait_for_socket_on_server, #rtmp_socket{channels = {}, out_channels = {}, active = false}, ?RTMP_TIMEOUT};
288
289init([connect]) ->
290  {ok, wait_for_socket_on_client, #rtmp_socket{channels = {}, out_channels = {}, active = false}, ?RTMP_TIMEOUT}.
291
292
293
294%% @private 
295
296wait_for_socket_on_server(timeout, State) ->
297  {stop, normal, State};
298
299wait_for_socket_on_server({socket, Socket}, #rtmp_socket{} = State) when is_port(Socket) ->
300  case inet:peername(Socket) of
301    {ok, {IP, Port}} ->
302      ok =  inet:setopts(Socket, [{active, once}, {packet, raw}, binary, {send_timeout, 3000}]),
303      {next_state, handshake_c1, State#rtmp_socket{socket = Socket, address = IP, port = Port}, ?RTMP_TIMEOUT};
304    {error, _Error} ->
305      {stop, normal, State}
306  end;
307
308wait_for_socket_on_server({socket, Socket}, #rtmp_socket{} = State) when is_pid(Socket) ->
309  erlang:monitor(process, Socket),
310  {next_state, handshake_c1, State#rtmp_socket{socket = Socket}, ?RTMP_TIMEOUT}.
311
312
313%% @private  
314-spec(wait_for_socket_on_client(Message::any(), Socket::rtmp_socket()) -> no_return()).
315
316wait_for_socket_on_client(timeout, State) ->
317  {stop, normal, State};
318
319wait_for_socket_on_client({socket, Socket}, #rtmp_socket{} = State) ->
320  inet:setopts(Socket, [{active, once}, {packet, raw}, binary]),
321  {ok, {IP, Port}} = inet:peername(Socket),
322  State1 = State#rtmp_socket{socket = Socket, address = IP, port = Port},
323  send_data(State1, rtmp_handshake:c1()),
324  {next_state, handshake_s1, State1, ?RTMP_TIMEOUT}.
325
326%% @private  
327handshake_c1(timeout, State) ->
328  {stop, normal, State}.
329
330%% @private  
331handshake_c3(timeout, State) ->
332  {stop, normal, State}.
333
334%% @private  
335handshake_s1(timeout, State) ->
336  {stop, normal, State}.
337  
338%% @private  
339loop(timeout, #rtmp_socket{pinged = false} = State) ->
340  State1 = send_data(State, #rtmp_message{type = ping}),
341  {next_state, loop, State1#rtmp_socket{pinged = true}, ?RTMP_TIMEOUT};
342  
343loop(timeout, #rtmp_socket{consumer = Consumer} = State) ->
344  Consumer ! {rtmp, self(), timeout},
345  {stop, normal, State}.
346
347
348%% @private
349loop(#rtmp_message{} = Message, _From, State) ->
350  State1 = send_data(State, Message),
351  {reply, ok, loop, State1, ?RTMP_TIMEOUT}.
352
353
354-type(rtmp_option() ::active|amf_version|chunk_size|window_size|client_buffer|address).
355% -type(rtmp_option_value() ::{rtmp_option(), any()}).
356-spec(get_options(State::rtmp_socket(), Key::rtmp_option()|[rtmp_option()]) -> term()).
357
358get_options(State, active) ->
359  {active, State#rtmp_socket.active};
360
361get_options(State, amf_version) ->
362  {amf_version, State#rtmp_socket.amf_version};
363
364get_options(State, chunk_size) ->
365  {chunk_size, State#rtmp_socket.server_chunk_size};
366
367get_options(State, window_size) ->
368  {window_size, State#rtmp_socket.window_size};
369
370get_options(State, url) ->
371  {url, State#rtmp_socket.url};
372
373get_options(State, client_buffer) ->
374  {client_buffer, State#rtmp_socket.client_buffer};
375
376get_options(State, debug) ->
377  {debug, State#rtmp_socket.debug};
378
379get_options(State, address) ->
380  {address, {State#rtmp_socket.address, State#rtmp_socket.port}};
381
382get_options(_State, []) ->
383  [];
384  
385get_options(State, [Key | Options]) ->
386  [get_options(State, Key) | get_options(State, Options)].
387
388get_stat(State) ->
389  get_stat(State, [recv_oct, send_oct]).
390
391get_stat(State, recv_oct) ->
392  {recv_oct, State#rtmp_socket.bytes_read};
393
394get_stat(State, send_oct) ->
395  {send_oct, State#rtmp_socket.bytes_sent};
396
397get_stat(_State, []) ->
398  [];
399
400get_stat(State, [Key | Options]) ->
401  [get_stat(State, Key) | get_stat(State, Options)].
402
403set_options(State, [{amf_version, Version} | Options]) ->
404  set_options(State#rtmp_socket{amf_version = Version}, Options);
405
406set_options(#rtmp_socket{socket = Socket, buffer = Data} = State, [{active, Active} | Options]) ->
407  State1 = flush_send(State#rtmp_socket{active = Active}),
408  State2 = case Active of
409    false -> 
410      inet:setopts(Socket, [{active, false}]),
411      State1;
412    true ->
413      inet:setopts(Socket, [{active, true}]),
414      State1;
415    once when size(Data) > 0 ->
416      handle_rtmp_data(State1);
417    once ->
418      activate_socket(State),
419      State1
420  end,
421  set_options(State2, Options);
422
423set_options(#rtmp_socket{} = State, [{debug, Debug} | Options]) ->
424  io:format("Set debug to ~p~n", [Debug]),
425  set_options(State#rtmp_socket{debug = Debug}, Options);
426
427set_options(#rtmp_socket{} = State, [{url, URL} | Options]) ->
428  set_options(State#rtmp_socket{url = URL}, Options);
429
430set_options(#rtmp_socket{consumer = undefined} = State, [{consumer, Consumer} | Options]) ->
431  erlang:monitor(process, Consumer),
432  set_options(State#rtmp_socket{consumer = Consumer}, Options);
433
434set_options(State, [{chunk_size, ChunkSize} | Options]) ->
435  State1 = send_data(State, #rtmp_message{type = chunk_size, body = ChunkSize}),
436  set_options(State1#rtmp_socket{server_chunk_size = ChunkSize}, Options);
437
438set_options(State, []) -> State.
439  
440%%-------------------------------------------------------------------------
441%% Func: handle_event/3
442%% Returns: {next_state, NextStateName, NextStateData}          |
443%%          {next_state, NextStateName, NextStateData, Timeout} |
444%%          {stop, Reason, NewStateData}
445%% @private
446%%-------------------------------------------------------------------------
447handle_event({setopts, Options}, StateName, State) ->
448  NewState = set_options(State, Options),
449  {next_state, StateName, NewState, ?RTMP_TIMEOUT};
450
451handle_event(Event, StateName, StateData) ->
452  {stop, {StateName, undefined_event, Event}, StateData}.
453
454
455%%-------------------------------------------------------------------------
456%% Func: handle_sync_event/4
457%% Returns: {next_state, NextStateName, NextStateData}            |
458%%          {next_state, NextStateName, NextStateData, Timeout}   |
459%%          {reply, Reply, NextStateName, NextStateData}          |
460%%          {reply, Reply, NextStateName, NextStateData, Timeout} |
461%%          {stop, Reason, NewStateData}                          |
462%%          {stop, Reason, Reply, NewStateData}
463%% @private
464%%-------------------------------------------------------------------------
465
466handle_sync_event(next_invoke_id, _From, StateName, #rtmp_socket{out_invoke_id = Id} = Socket) ->
467  {reply, Id, StateName, Socket#rtmp_socket{out_invoke_id = Id + 1}, ?RTMP_TIMEOUT};
468
469handle_sync_event({getopts, Options}, _From, StateName, State) ->
470  {reply, get_options(State, Options), StateName, State, ?RTMP_TIMEOUT};
471
472handle_sync_event({getstat, Options}, _From, StateName, State) ->
473  {reply, get_stat(State, Options), StateName, State, ?RTMP_TIMEOUT};
474
475handle_sync_event(getstat, _From, StateName, State) ->
476  {reply, get_stat(State), StateName, State, ?RTMP_TIMEOUT};
477
478handle_sync_event(get_socket, _From, StateName, #rtmp_socket{socket = Socket} = State) when is_port(Socket)->
479  {reply, {rtmp,Socket}, StateName, State, ?RTMP_TIMEOUT};
480
481handle_sync_event(get_socket, _From, StateName, #rtmp_socket{socket = Socket} = State) when is_pid(Socket)->
482  {reply, {rtmpt,undefined}, StateName, State, ?RTMP_TIMEOUT};
483
484handle_sync_event({setopts, Options}, _From, StateName, State) ->
485  NewState = set_options(State, Options),
486  {reply, ok, StateName, NewState, ?RTMP_TIMEOUT};
487
488handle_sync_event({notify_audio, StreamId, DTS}, _From, loop, Socket) ->
489  {reply, ok, loop, send_audio_notify(Socket, StreamId, DTS), ?RTMP_TIMEOUT};
490
491handle_sync_event({notify_video, StreamId, DTS}, _From, loop, Socket) ->
492  {reply, ok, loop, send_video_notify(Socket, StreamId, DTS), ?RTMP_TIMEOUT};
493
494handle_sync_event(Event, _From, StateName, StateData) ->
495  {stop, {StateName, undefined_event, Event}, StateData}.
496
497
498%%-------------------------------------------------------------------------
499%% Func: handle_info/3
500%% Returns: {next_state, NextStateName, NextStateData}          |
501%%          {next_state, NextStateName, NextStateData, Timeout} |
502%%          {stop, Reason, NewStateData}
503%% @private
504%%-------------------------------------------------------------------------
505handle_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 ->
506  activate_socket(State),
507  {next_state, handshake_c1, State#rtmp_socket{buffer = <<Buffer/binary, Data/binary>>, bytes_read = BytesRead + size(Data)}, ?RTMP_TIMEOUT};
508  
509handle_info({tcp, Socket, Data}, handshake_c1, #rtmp_socket{socket=Socket, buffer = Buffer, bytes_read = BytesRead} = State) ->
510  activate_socket(State),
511  <<Handshake:(?HS_BODY_LEN+1)/binary, Rest/binary>> = <<Buffer/binary, Data/binary>>,
512  State1 = case rtmp_handshake:server(Handshake) of
513    {uncrypted, Reply} -> 
514  	  send_data(State, Reply);
515    {crypted, Reply, KeyIn, KeyOut} ->
516  	  NewState = send_data(State, Reply),
517  	  ?D({"Established RTMPE connection"}),
518      NewState#rtmp_socket{key_in = KeyIn, key_out = KeyOut}
519  end,
520	{next_state, 'handshake_c3', State1#rtmp_socket{buffer = Rest, bytes_read = BytesRead + size(Data)}, ?RTMP_TIMEOUT};
521
522
523handle_info({tcp, Socket, Data}, handshake_c3, #rtmp_socket{socket=Socket, buffer = Buffer, bytes_read = BytesRead} = State) when size(Buffer) + size(Data) < ?HS_BODY_LEN ->
524  activate_socket(State),
525  {next_state, handshake_c3, State#rtmp_socket{buffer = <<Buffer/binary, Data/binary>>, bytes_read = BytesRead + size(Data)}, ?RTMP_TIMEOUT};
526  
527handle_info({tcp, Socket, Data}, handshake_c3, #rtmp_socket{socket=Socket, consumer = Consumer, buffer = Buffer, bytes_read = BytesRead, active = Active, key_in = KeyIn} = State) ->
528  <<_HandShakeC3:?HS_BODY_LEN/binary, CryptedData/binary>> = <<Buffer/binary, Data/binary>>,
529  Consumer ! {rtmp, self(), connected},
530  case Active of
531    true -> inet:setopts(Socket, [{active, true}]);
532    _ -> ok
533  end,
534
535  {NewKeyIn, Rest} = case KeyIn of
536    undefined -> {undefined, CryptedData};
537    _ -> rtmpe:crypt(KeyIn, CryptedData)
538  end,
539  % ?D({decrypt, Rest, CryptedData == element(2, rtmpe:crypt(NewKeyIn, Rest))}),
540  
541  State1 = State#rtmp_socket{bytes_read = BytesRead + size(Data), key_in = NewKeyIn, buffer = Rest},
542  case Rest of
543    <<>> -> {next_state, loop, State1, ?RTMP_TIMEOUT};
544    _ -> {next_state, loop, handle_rtmp_data(State1), ?RTMP_TIMEOUT}
545  end;
546
547handle_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 ->
548  activate_socket(State),
549  {next_state, handshake_s1, State#rtmp_socket{buffer = <<Buffer/binary, Data/binary>>, bytes_read = BytesRead + size(Data)}, ?RTMP_TIMEOUT};
550
551handle_info({tcp, Socket, Data}, handshake_s1, #rtmp_socket{socket=Socket, consumer = Consumer, buffer = Buffer, bytes_read = BytesRead, active = Active} = State) ->
552  <<?HS_UNCRYPTED, S1:?HS_BODY_LEN/binary, _S2:?HS_BODY_LEN/binary, Rest/binary>> = <<Buffer/binary, Data/binary>>,
553  send_data(State, rtmp_handshake:c2(S1)),
554  Consumer ! {rtmp, self(), connected},
555  case Active of
556    true -> inet:setopts(Socket, [{active, true}]);
557    _ -> ok
558  end,
559  
560  {next_state, loop, handle_rtmp_data(State#rtmp_socket{bytes_read = BytesRead + size(Data), buffer = Rest}), ?RTMP_TIMEOUT};
561
562
563
564handle_info({tcp, Socket, CryptedData}, loop, #rtmp_socket{socket=Socket, buffer = Buffer, bytes_read = BytesRead, bytes_unack = BytesUnack, key_in = KeyIn} = State) ->
565  State1 = flush_send(State),
566  {NewKeyIn, Data} = case KeyIn of
567    undefined -> {undefined, CryptedData};
568    _ -> rtmpe:crypt(KeyIn, CryptedData)
569  end,
570  {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};
571
572handle_info({tcp_closed, Socket}, _StateName, #rtmp_socket{socket = Socket, consumer = Consumer} = StateData) ->
573  Consumer ! {rtmp, self(), disconnect, get_stat(StateData)},
574  {stop, normal, StateData};
575
576handle_info(#rtmp_message{} = Message, loop, State) ->
577  State1 = send_data(State, Message),
578  State2 = flush_send(State1),
579  {next_state, loop, State2, ?RTMP_TIMEOUT};
580
581handle_info({rtmpt, RTMPT, alive}, StateName, #rtmp_socket{socket = RTMPT} = State) ->
582  {next_state, StateName, State#rtmp_socket{pinged = false}, ?RTMP_TIMEOUT};
583
584handle_info({rtmpt, RTMPT, Data}, StateName, State) ->
585  handle_info({tcp, RTMPT, Data}, StateName, State);
586
587handle_info({'DOWN', _, process, _Client, _Reason}, _StateName, State) ->
588  {stop, normal, State};
589
590handle_info({tcp_paused, _Socket}, StateName, StateData) ->
591  {next_state, StateName, StateData, ?RTMP_TIMEOUT};
592
593handle_info({tcp_resumed, _Socket}, StateName, StateData) ->
594  {next_state, StateName, StateData, ?RTMP_TIMEOUT};
595
596handle_info(stop, _StateName, State) ->
597  {stop, normal, State};
598
599handle_info(_Info, StateName, StateData) ->
600  error_logger:error_msg("Unknown message to rtmp socket: ~p ~p ~p~n", [_Info, StateName, StateData]),
601  {next_state, StateName, StateData, ?RTMP_TIMEOUT}.
602
603% flush_send(State) -> flush_send([], State).
604
605flush_send(State) ->
606  receive
607    #rtmp_message{} = Message ->
608      NewState = send_data(State, Message),
609      % ?D({out, Message}),
610      % {NewState, Data} = rtmp:encode(State, Message),
611      % flush_send([Data | Packet], NewState)
612      flush_send(NewState)
613  after
614    0 -> State
615  end.
616  
617flush_all() ->
618  receive
619    _Msg -> flush_all()
620  after
621    0 -> ok
622  end.
623      
624  
625activate_socket(#rtmp_socket{socket = Socket}) when is_port(Socket) ->
626  inet:setopts(Socket, [{active, once}]);
627activate_socket(#rtmp_socket{socket = Socket}) when is_pid(Socket) ->
628  ok.
629
630
631send_audio_notify(Socket, StreamId, DTS) ->
632  send_data(Socket#rtmp_socket{sent_audio_notify = true}, rtmp_lib:empty_audio(StreamId, DTS)).
633
634send_video_notify(Socket, StreamId, DTS) ->
635  Msg = [
636    #rtmp_message{type = video, channel_id = rtmp_lib:channel_id(video, StreamId), timestamp = DTS, stream_id = StreamId, body = <<87,0>>, ts_type = new},
637    #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},
638    #rtmp_message{type = video, channel_id = rtmp_lib:channel_id(video, StreamId), timestamp = DTS, stream_id = StreamId, body = <<87,1>>, ts_type = delta}
639  ],
640  
641  lists:foldl(fun(M, State1) ->
642    send_data(State1, M)
643  end, Socket#rtmp_socket{sent_video_notify = true}, Msg).
644      
645
646send_data(#rtmp_socket{sent_audio_notify = false} = Socket, 
647          #rtmp_message{type = audio, timestamp = DTS, stream_id = StreamId, body = Body} = Message) when size(Body) > 0 ->
648  State1 = send_audio_notify(Socket, StreamId, DTS),
649  send_data(State1, Message#rtmp_message{ts_type = new});
650
651send_data(#rtmp_socket{sent_video_notify = false} = Socket, #rtmp_message{type = video, timestamp = DTS, stream_id = StreamId} = Message) ->
652  State2 = send_video_notify(Socket, StreamId, DTS),
653  send_data(State2, Message);
654  
655send_data(#rtmp_socket{sent_audio_notify = true} = Socket, #rtmp_message{type = stream_end} = Message) ->
656  send_data(Socket#rtmp_socket{sent_audio_notify = false}, Message);
657
658send_data(#rtmp_socket{sent_video_notify = true} = Socket, #rtmp_message{type = stream_end} = Message) ->
659  send_data(Socket#rtmp_socket{sent_video_notify = false}, Message);
660
661send_data(#rtmp_socket{socket = Socket, key_out = KeyOut} = State, Message) ->
662  case State#rtmp_socket.debug of
663    true -> print_rtmp_message(out, Message);
664    _ -> ok
665  end,
666  {NewState, Data} = case Message of
667    #rtmp_message{} ->
668      % ?D({Socket,Message#rtmp_message.type, Message#rtmp_message.timestamp, Message#rtmp_message.ts_type}),
669      rtmp:encode(State, Message);
670    _ -> 
671      {State, Message}
672  end,
673  {NewState1, Crypt} = case KeyOut of
674    undefined -> {NewState, Data};
675    _ -> 
676      {NewKeyOut, OutCrypt} = rtmpe:crypt(KeyOut, Data),
677      {NewState#rtmp_socket{key_out = NewKeyOut}, OutCrypt}
678  end,
679  % (catch rtmp_stat_collector:out_bytes(self(), iolist_size(Crypt))),
680  if
681    is_port(Socket) ->
682      case gen_tcp:send(Socket, Crypt) of
683        ok -> ok;
684        {error, timeout} -> flush_all(), throw({stop, network_timeout, NewState1});
685        {error, Else} -> flush_all(), throw({stop, {network_error,Else}, NewState1})
686      end;
687    is_pid(Socket) ->
688      rtmpt:write(Socket, Crypt)
689  end,
690  NewState1.
691
692
693
694handle_rtmp_data(#rtmp_socket{buffer = <<>>} = State) ->
695  % ?D({empty_input, erlang:get_stacktrace()}),
696  State;
697
698handle_rtmp_data(#rtmp_socket{bytes_unack = Bytes, window_size = Window} = State) when Bytes >= Window ->
699  State1 = send_data(State, #rtmp_message{type = ack_read}),
700  handle_rtmp_data(State1);
701
702handle_rtmp_data(#rtmp_socket{buffer = Data} = State) ->
703  got_rtmp_message(rtmp:decode(State, Data)).
704
705
706print_rtmp_message(InOut, #rtmp_message{channel_id = Channel, ts_type = TSType, timestamp = TS, type = Type, stream_id = StreamId, body = Body}) ->
707  DecodedBody = case Type of
708    video when size(Body) > 10 -> erlang:setelement(5, flv:decode_video_tag(Body), size(Body));
709    audio when size(Body) > 0 -> erlang:setelement(7, flv:decode_audio_tag(Body), size(Body));
710    _ -> Body
711  end,
712  io:format("~p ~p ~p ~p ~p ~p ~p~n", [InOut, Channel, TSType, TS, Type, StreamId, DecodedBody]),
713  ok;
714
715print_rtmp_message(InOut, Msg) ->
716  io:format("~p ~p~n", [InOut, Msg]),
717  ok.
718  
719
720
721got_rtmp_message({#rtmp_socket{debug = true}, Msg, _} = Message) ->
722  print_rtmp_message(in, Msg),
723  handle_rtmp_message(Message);
724
725got_rtmp_message(Decoded) ->
726  handle_rtmp_message(Decoded).
727
728
729handle_rtmp_message({#rtmp_socket{consumer = Consumer} = State, #rtmp_message{type = window_size, body = Size} = Message, Rest}) ->
730  Consumer ! {rtmp, self(), Message},
731  rtmp_message_sent(State#rtmp_socket{window_size = Size, buffer = Rest});
732
733handle_rtmp_message({#rtmp_socket{consumer = Consumer, pinged = true} = State, #rtmp_message{type = pong} = Message, Rest}) ->
734  Consumer ! {rtmp, self(), Message},
735  rtmp_message_sent(State#rtmp_socket{pinged = false, buffer = Rest});
736
737handle_rtmp_message({#rtmp_socket{consumer = Consumer} = State, #rtmp_message{type = ping, body = Timestamp} = Message, Rest}) ->
738  Consumer ! {rtmp, self(), Message},
739  send_data(State, #rtmp_message{type = pong, body = Timestamp}),
740  rtmp_message_sent(State#rtmp_socket{buffer = Rest});
741
742handle_rtmp_message({#rtmp_socket{consumer = Consumer} = State, Message, Rest}) ->
743  Consumer ! {rtmp, self(), Message},
744  rtmp_message_sent(State#rtmp_socket{buffer = Rest});
745
746handle_rtmp_message({#rtmp_socket{} = State, Rest}) -> 
747  activate_socket(State),
748  State#rtmp_socket{buffer = Rest}.
749
750rtmp_message_sent(#rtmp_socket{active = true} = State) ->
751  activate_socket(State),
752  handle_rtmp_data(State);
753
754rtmp_message_sent(#rtmp_socket{active = _} = State) ->
755  handle_rtmp_data(State);
756
757rtmp_message_sent(State) -> 
758  State.
759
760
761%%-------------------------------------------------------------------------
762%% Func: terminate/3
763%% Purpose: Shutdown the fsm
764%% Returns: any
765%% @private
766%%-------------------------------------------------------------------------
767terminate(_Reason, _StateName, #rtmp_socket{socket=Socket}) when is_pid(Socket) ->
768  gen_server:cast(Socket, close),
769  ok;
770  
771terminate(_Reason, _StateName, _State) ->
772  ok.
773
774
775%%-------------------------------------------------------------------------
776%% Func: code_change/4
777%% Purpose: Convert process state when code is changed
778%% Returns: {ok, NewState, NewStateData}
779%% @private
780%%-------------------------------------------------------------------------
781code_change(_OldVersion, _StateName, _State, _Extra) ->
782  ok.