Skip to content
This repository has been archived by the owner on Feb 21, 2020. It is now read-only.

Commit

Permalink
Broken state of refactoring media structure
Browse files Browse the repository at this point in the history
  • Loading branch information
maxlapshin committed Nov 21, 2009
1 parent abc722f commit c2a26e1
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 101 deletions.
16 changes: 0 additions & 16 deletions include/ems.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -223,22 +223,6 @@
}).


-record(video_player, {
consumer,
media_info,
client_buffer = ?MIN_CLIENT_BUFFER,
sent_video_config = false,
sent_audio_config = false,
prepush = 0,
stream_id,
buffer,
timer_start,
timer_ref,
playing_from = 0,
ts_prev = 0,
pos = 0
}).



-record(file_frame, {
Expand Down
14 changes: 4 additions & 10 deletions src/formats/flv.erl
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
-include("../../include/flv.hrl").
-include("../../include/media_info.hrl").

-export([init/1, read_frame/2, read_frame_list/3, header/1]).
-export([init/1, read_frame/2, codec_config/2, read_frame_list/3, header/1]).
-behaviour(gen_format).


Expand Down Expand Up @@ -107,25 +107,19 @@ read_frame_list(#media_info{device = Device, frames = FrameTable} = MediaInfo, O
{error, Reason}
end.


codec_config(_, _) -> undefined.

% Reads a tag from IoDev for position Pos.
% @param IoDev
% @param Pos
% @return a valid video_frame record type
read_frame(#video_player{pos = undefined} = Player, #media_info{frames = FrameTable} = MediaInfo) ->
read_frame(Player#video_player{pos = ets:first(FrameTable)}, MediaInfo);

read_frame(#video_player{pos = '$end_of_table'}, _MediaInfo) ->
{ok, done};

read_frame(#video_player{pos = Key} = Player, #media_info{device = IoDev, frames = FrameTable}) ->
read_frame(#media_info{device = IoDev, frames = FrameTable}, Key) ->
[Frame] = ets:lookup(FrameTable, Key),
#file_frame{offset = Offset, size = Size} = Frame,
case file:pread(IoDev, Offset, Size) of
{ok, Data} ->
VideoFrame = video_frame(Frame, Data),
{ok, VideoFrame#video_frame{nextpos = ets:next(FrameTable, Key)}, Player};
{ok, VideoFrame#video_frame{nextpos = ets:next(FrameTable, Key)}};
eof ->
{ok, done};
{error, Reason} ->
Expand Down
2 changes: 1 addition & 1 deletion src/formats/gen_format.erl
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,5 @@
%% @hidden
%% @end
%%-------------------------------------------------------------------------
behaviour_info(callbacks) -> [{init, 1}, {read_frame, 2}];
behaviour_info(callbacks) -> [{init, 1}, {read_frame, 2}, {codec_config, 2}];
behaviour_info(_Other) -> ?D({"Behaviour", _Other}), undefined.
31 changes: 13 additions & 18 deletions src/formats/mp4.erl
Original file line number Diff line number Diff line change
Expand Up @@ -38,35 +38,29 @@
-include("../../include/mp4.hrl").
-include("../../include/media_info.hrl").

-export([init/1, read_frame/2, metadata/1]).
-export([init/1, read_frame/2, metadata/1, codec_config/2]).
-behaviour(gen_format).

% -export([read_header/1,read_frame/1,read_frame/2,to_tag/2,header/1, parse_meta/1, encodeTag/2]).



read_frame(#video_player{pos = '$end_of_table'}, _MediaInfo) ->
{ok, done};


read_frame(#video_player{sent_video_config = false} = Player, #media_info{frames = FrameTable} = MediaInfo) ->
codec_config(video, MediaInfo) ->
Config = decoder_config(video, MediaInfo),
?D({"Decoder config", Config}),
{ok, #video_frame{
?D({"Video config", Config}),
#video_frame{
type = ?FLV_TAG_TYPE_VIDEO,
decoder_config = true,
timestamp_abs = 0,
body = Config,
frame_type = ?FLV_VIDEO_FRAME_TYPE_KEYFRAME,
codec_id = ?FLV_VIDEO_CODEC_AVC,
raw_body = false,
nextpos = ets:first(FrameTable)
}, Player#video_player{sent_video_config = true}};
nextpos = 0
};

read_frame(#video_player{sent_audio_config = false} = Player, #media_info{frames = FrameTable} = MediaInfo) ->
codec_config(audio, MediaInfo) ->
Config = decoder_config(audio, MediaInfo),
?D({"Audio config", Config}),
{ok, #video_frame{
#video_frame{
type = ?FLV_TAG_TYPE_AUDIO,
decoder_config = true,
timestamp_abs = 0,
Expand All @@ -76,16 +70,17 @@ read_frame(#video_player{sent_audio_config = false} = Player, #media_info{frames
sound_size = ?FLV_AUDIO_SIZE_16BIT,
sound_rate = ?FLV_AUDIO_RATE_44,
raw_body = false,
nextpos = ets:first(FrameTable)
}, Player#video_player{sent_audio_config = true}};
nextpos = 0
}.


read_frame(#video_player{pos = Key} = Player, #media_info{frames = FrameTable} = MediaInfo) ->
read_frame(#media_info{frames = FrameTable} = MediaInfo, Key) ->
[Frame] = ets:lookup(FrameTable, Key),
#file_frame{offset = Offset, size = Size} = Frame,
case read_data(MediaInfo, Offset, Size) of
{ok, Data, _} ->
VideoFrame = video_frame(Frame, Data),
{ok, VideoFrame#video_frame{nextpos = ets:next(FrameTable, Key)}, Player};
{ok, VideoFrame#video_frame{nextpos = ets:next(FrameTable, Key)}};
eof ->
{ok, done};
{error, Reason} ->
Expand Down
2 changes: 2 additions & 0 deletions src/media/ems_play.erl
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@

send(undefined, _) -> {error, undefined_consumer};

send(_, undefined) -> ok;

send(Consumer, #video_frame{type = Type, streamid=StreamId,timestamp_abs = TimeStamp,body=Body, raw_body = false} = Frame) when is_binary(Body) ->
Channel = #channel{id = channel_id(Type, StreamId),timestamp=TimeStamp,length=size(Body),type=Type,stream=StreamId},
gen_fsm:send_event(Consumer, {send, {Channel, ems_flv:encode(Frame)}});
Expand Down
23 changes: 15 additions & 8 deletions src/media/file_media.erl
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,19 @@
-behaviour(gen_server).

%% External API
-export([start_link/2, first/1, read/2, file_name/1, seek/2, metadata/1]).
-export([start_link/2, codec_config/2, read_frame/2, file_name/1, seek/2, metadata/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).


start_link(Path, Type) ->
gen_server:start_link(?MODULE, [Path, Type], []).

first(Server) ->
gen_server:call(Server, {first}).

read(Server, Player) ->
gen_server:call(Server, {read, Player}).

codec_config(MediaEntry, Type) -> gen_server:call(MediaEntry, {codec_config, Type}).

read_frame(MediaEntry, Key) -> gen_server:call(MediaEntry, {read, Key}).

file_name(Server) ->
gen_server:call(Server, {file_name}).
Expand Down Expand Up @@ -68,9 +67,17 @@ handle_call({create_player, Options}, _From, #media_info{file_name = Name, clien
handle_call({first}, _From, #media_info{frames = FrameTable} = MediaInfo) ->
{reply, ets:first(FrameTable), MediaInfo};

handle_call({codec_config, Type}, _From, #media_info{format = FileFormat} = MediaInfo) ->
{reply, FileFormat:codec_config(Type, MediaInfo), MediaInfo};

handle_call({read, '$end_of_table'}, _From, MediaInfo) ->
{reply, {ok, done}, MediaInfo};

handle_call({read, undefined}, _From, #media_info{frames = FrameTable, format = FileFormat} = MediaInfo) ->
{reply, FileFormat:read_frame(MediaInfo, ets:first(FrameTable)), MediaInfo};

handle_call({read, State}, _From, #media_info{format = FileFormat} = MediaInfo) ->
{reply, FileFormat:read_frame(State, MediaInfo), MediaInfo};
handle_call({read, Key}, _From, #media_info{format = FileFormat} = MediaInfo) ->
{reply, FileFormat:read_frame(MediaInfo, Key), MediaInfo};

handle_call({file_name}, _From, #media_info{file_name = FileName} = MediaInfo) ->
{reply, FileName, MediaInfo};
Expand Down
124 changes: 77 additions & 47 deletions src/media/file_play.erl
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,26 @@
-export([init/2, ready/1]).



-record(file_player, {
consumer,
media_info,
client_buffer = ?MIN_CLIENT_BUFFER,
sent_video_config = false,
sent_audio_config = false,
prepush = 0,
stream_id,
buffer,
timer_start,
timer_ref,
playing_from = 0,
ts_prev = 0,
pos = 0,
paused = false
}).



start(MediaEntry) -> start(MediaEntry, []).

start(MediaEntry, Options) ->
Expand All @@ -52,96 +72,106 @@ start(MediaEntry, Options) ->

init(MediaEntry, Options) ->
?D({"Starting file play for consumer", proplists:get_value(consumer, Options)}),
ready(#video_player{consumer = proplists:get_value(consumer, Options),
ready(#file_player{consumer = proplists:get_value(consumer, Options),
stream_id = proplists:get_value(stream_id, Options, 1),
pos = undefined,
media_info = MediaEntry,
client_buffer = proplists:get_value(client_buffer, Options, 10000),
timer_start = erlang:now()}).


ready(#video_player{media_info = MediaInfo,
ready(#file_player{media_info = MediaInfo,
consumer = Consumer,
client_buffer = ClientBuffer,
timer_ref = Timer,
stream_id = StreamId} = State) ->
timer_ref = Timer} = State) ->
receive
{client_buffer, ClientBuffer} -> ready(State#video_player{client_buffer = ClientBuffer});
{client_buffer, ClientBuffer} ->
ready(State#file_player{client_buffer = ClientBuffer});
start ->
case media_entry:metadata(MediaInfo) of
case file_media:metadata(MediaInfo) of
undefined -> ok;
MetaData -> gen_fsm:send_event(Consumer, {metadata, ?AMF_COMMAND_ONMETADATA, MetaData, 1})
end,
self() ! play,
NextState = State#video_player{prepush = ClientBuffer},
?D({"Player starting with pid", self(), MediaInfo}),
?MODULE:ready(NextState);
?MODULE:ready(State#file_player{prepush = ClientBuffer, paused = false});

pause ->
?D("Player paused"),
timer:cancel(Timer),
?MODULE:ready(State);
?MODULE:ready(State#file_player{paused = true});

resume ->
?D("Player resumed"),
self() ! play,
?MODULE:ready(State);
?MODULE:ready(State#file_player{paused = false});

{seek, Timestamp} ->
{Pos, NewTimestamp} = media_entry:seek(MediaInfo, Timestamp),
{Pos, NewTimestamp} = file_media:seek(MediaInfo, Timestamp),
timer:cancel(Timer),
% ?D({"Player seek to", Timestamp, Pos, NewTimestamp}),
self() ! {play},
?MODULE:ready(State#video_player{pos = Pos, ts_prev = NewTimestamp, playing_from = NewTimestamp, prepush = ClientBuffer});
self() ! play,
?MODULE:ready(State#file_player{pos = Pos, ts_prev = NewTimestamp, playing_from = NewTimestamp, prepush = ClientBuffer});

stop ->
?D("Player stopping"),
timer:cancel(Timer),
?MODULE:ready(State#video_player{ts_prev = 0, pos = media_entry:first(MediaInfo), playing_from = 0});
?MODULE:ready(State#file_player{ts_prev = 0, pos = undefined, playing_from = 0});

exit ->
ok;

play ->
% {_, Sec1, MSec1} = erlang:now(),
case media_entry:read(MediaInfo, State) of
{ok, done} ->
?D("Video file finished"),
gen_fsm:send_event(Consumer, {status, ?NS_PLAY_COMPLETE, 1}),
?MODULE:ready(State);
{ok, #video_frame{type = _Type} = Frame, Player} ->
TimeStamp = Frame#video_frame.timestamp_abs - State#video_player.ts_prev,
ems_play:send(Consumer, Frame#video_frame{timestamp=TimeStamp, streamid = StreamId}),
{Timeout, Player1} = timeout(Frame, Player),
% ?D({"Frame", Frame#video_frame.timestamp_abs, Player#video_player.timer_start, TimeStamp, Timeout}),
NextState = Player1#video_player{
timer_ref = timer:send_after(Timeout, play),
ts_prev = Frame#video_frame.timestamp_abs,
pos = Frame#video_frame.nextpos},
% {_, Sec2, MSec2} = erlang:now(),
% _Delta = (Sec2*1000 + MSec2) - (Sec1*1000 + MSec1),
% ?D({"Read frame", Delta}),
?MODULE:ready(NextState);
{error, _Reason} ->
?D({"Ems player stopping", _Reason}),
erlang:error(_Reason)
end;
play(State);

{tcp_closed, _Socket} ->
error_logger:info_msg("~p Video player lost connection.\n", [self()]),
ok;
Else ->
?D({"Unknown message", Else}),
?MODULE:ready(State)
end.
{tcp_closed, _Socket} ->
error_logger:info_msg("~p Video player lost connection.\n", [self()]),
ok;
Else ->
?D({"Unknown message", Else}),
?MODULE:ready(State)
end.



play(#file_player{paused = true} = State) ->
?MODULE:ready(State);

% ready(file_name, _From, #video_player{media_info = MediaInfo} = State) ->
% {reply, media_entry:file_name(MediaInfo), ready, State}.

play(#file_player{sent_audio_config = false, media_info = MediaInfo, consumer = Consumer} = State) ->
ems_play:send(Consumer, file_media:codec_config(MediaInfo, audio)),
?D({"Sent audio config"}),
play(State#file_player{sent_audio_config = true});

play(#file_player{sent_video_config = false, media_info = MediaInfo, consumer = Consumer} = State) ->
ems_play:send(Consumer, file_media:codec_config(MediaInfo, video)),
?D({"Sent video config"}),
play(State#file_player{sent_video_config = true});


play(#file_player{media_info = MediaInfo, pos = Key} = Player) ->
send_frame(Player, file_media:read_frame(MediaInfo, Key)).

send_frame(#file_player{consumer = Consumer} = Player, {ok, done}) ->
?D("Video file finished"),
gen_fsm:send_event(Consumer, {status, ?NS_PLAY_COMPLETE, 1}),
?MODULE:ready(Player);

send_frame(#file_player{consumer = Consumer, stream_id = StreamId} = Player, {ok, #video_frame{nextpos = NextPos} = Frame}) ->
% ?D({"Frame", Key, Frame#video_frame.timestamp_abs, NextPos}),
TimeStamp = Frame#video_frame.timestamp_abs - Player#file_player.ts_prev,
ems_play:send(Consumer, Frame#video_frame{timestamp=TimeStamp, streamid = StreamId}),
{Timeout, Player1} = timeout(Frame, Player),
% ?D({"Frame", Consumer, Frame#video_frame.timestamp_abs, Player#file_player.timer_start, TimeStamp, Timeout}),
NextState = Player1#file_player{
timer_ref = timer:send_after(Timeout, play),
ts_prev = Frame#video_frame.timestamp_abs,
pos = NextPos},
% {_, Sec2, MSec2} = erlang:now(),
% _Delta = (Sec2*1000 + MSec2) - (Sec1*1000 + MSec1),
% ?D({"Read frame", Delta}),
?MODULE:ready(NextState).



Expand Down Expand Up @@ -174,13 +204,13 @@ file_format(Name) ->
%% @end
%%-------------------------------------------------------------------------

timeout(#video_frame{timestamp_abs = AbsTime}, #video_player{timer_start = TimerStart, client_buffer = ClientBuffer, playing_from = PlayingFrom, prepush = Prepush} = Player) ->
timeout(#video_frame{timestamp_abs = AbsTime}, #file_player{timer_start = TimerStart, client_buffer = ClientBuffer, playing_from = PlayingFrom, prepush = Prepush} = Player) ->
SeekTime = AbsTime - PlayingFrom,
Timeout = SeekTime - ClientBuffer - trunc(timer:now_diff(now(), TimerStart) / 1000),
% ?D({"Timeout", Timeout, SeekTime, ClientBuffer, trunc(timer:now_diff(now(), TimerStart) / 1000)}),
if
(Prepush > SeekTime) ->
{0, Player#video_player{prepush = Prepush - SeekTime}};
{0, Player#file_player{prepush = Prepush - SeekTime}};
(Timeout > 0) ->
{Timeout, Player};
true ->
Expand Down
2 changes: 1 addition & 1 deletion src/rtmp_client.erl
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ handle_info(_Info, StateName, StateData) ->
%%-------------------------------------------------------------------------
terminate(_Reason, _StateName, #rtmp_client{socket=Socket, video_player = Player}) ->
rtmp_server:logout(),
(catch gen_fsm:send_event(Player, {exit})),
Player ! exit,
(catch gen_tcp:close(Socket)),
ok.

Expand Down

0 comments on commit c2a26e1

Please sign in to comment.