Current section
Files
Jump to
Current section
Files
src/rtmpmsg.erl
%%% @doc RTMP messages and exported types
%%% @end
%%%
%%%
%%% Copyright (c) 2013, Takeru Ohta <phjgt308@gmail.com>
%%%
%%% The MIT License
%%%
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
%%% of this software and associated documentation files (the "Software"), to deal
%%% in the Software without restriction, including without limitation the rights
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
%%% copies of the Software, and to permit persons to whom the Software is
%%% furnished to do so, subject to the following conditions:
%%%
%%% The above copyright notice and this permission notice shall be included in
%%% all copies or substantial portions of the Software.
%%%
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
%%% THE SOFTWARE.
%%%
%%%---------------------------------------------------------------------------------------
-module(rtmpmsg).
-include("../include/rtmpmsg.hrl").
-include("../include/internal/rtmpmsg_internal.hrl").
%% RTMP Message Construct API
-export([
message/3,
protocol_control_message/1,
user_control_message/1,
set_chunk_size/1,
abort/1,
ack/1,
win_ack_size/1,
set_peer_bandwidth/2,
event_stream_begin/1,
event_stream_eof/1,
event_stream_dry/1,
event_set_buffer_length/2,
event_stream_is_recorded/1,
event_ping_request/1,
event_ping_response/1,
event_buffer_empty/1,
event_buffer_ready/1,
audio/3,
video/3,
command/6,
response_command/4,
notify_command/4, notify_command/5,
data/3, data/4,
shared_object/3,
aggregate/2
]).
%% Exported Types
-export_type([
byte_size/0,
milliseconds/0,
chunk_size/0,
chunk_stream_id/0,
message_stream_id/0,
message_type_id/0,
message_timestamp/0,
message/0,
message_body/0,
set_peer_bandwidth_limit_type/0,
message_body_protocol_control/0,
message_body_set_chunk_size/0,
message_body_abort/0,
message_body_ack/0,
message_body_win_ack_size/0,
message_body_set_peer_bandwidth/0,
message_body_user_control/0,
message_body_audio/0,
message_body_video/0,
message_body_command/0,
message_body_data/0,
message_body_shared_object/0,
message_body_aggregate/0,
message_body_unknown/0,
user_control_event/0,
event_stream_begin/0,
event_stream_eof/0,
event_stream_dry/0,
event_set_buffer_length/0,
event_stream_is_recorded/0,
event_ping_request/0,
event_ping_response/0,
event_buffer_empty/0,
event_buffer_ready/0,
event_unknown/0
]).
%% Internal Types
-export_type([
chunk/0
]).
%%================================================================================
%% Types
%%================================================================================
-type byte_size() :: non_neg_integer().
-type milliseconds() :: non_neg_integer().
-type chunk_size() :: 1..?CHUNK_SIZE_MAX.
-type chunk_stream_id() :: 2..65599.
-type message_stream_id() :: non_neg_integer().
-type message_type_id() :: byte().
-type message_timestamp() :: milliseconds().
-type message() :: #rtmpmsg{}.
-type message_body() :: message_body_protocol_control() |
message_body_audio() |
message_body_video() |
message_body_command() |
message_body_data() |
message_body_shared_object() |
message_body_aggregate() |
message_body_unknown().
-type message_body_protocol_control() :: message_body_set_chunk_size() |
message_body_abort() |
message_body_ack() |
message_body_win_ack_size() |
message_body_set_peer_bandwidth() |
message_body_user_control().
-type user_control_event() :: event_stream_begin() |
event_stream_eof() |
event_stream_dry() |
event_set_buffer_length() |
event_stream_is_recorded() |
event_ping_request() |
event_ping_response() |
event_buffer_empty() |
event_buffer_ready() |
event_unknown().
-type message_body_set_chunk_size() :: #rtmpmsg_set_chunk_size{}.
-type message_body_abort() :: #rtmpmsg_abort{}.
-type message_body_ack() :: #rtmpmsg_ack{}.
-type message_body_win_ack_size() :: #rtmpmsg_win_ack_size{}.
-type message_body_set_peer_bandwidth() :: #rtmpmsg_set_peer_bandwidth{}.
-type message_body_user_control() :: #rtmpmsg_user_control{}.
-type message_body_audio() :: #rtmpmsg_audio{}.
-type message_body_video() :: #rtmpmsg_video{}.
-type message_body_command() :: #rtmpmsg_command{}.
-type message_body_data() :: #rtmpmsg_data{}.
-type message_body_shared_object() :: #rtmpmsg_shared_object{}.
-type message_body_aggregate() :: #rtmpmsg_aggregate{}.
-type message_body_unknown() :: #rtmpmsg_unknown{}.
-type event_stream_begin() :: #rtmpmsg_event_stream_begin{}.
-type event_stream_eof() :: #rtmpmsg_event_stream_eof{}.
-type event_stream_dry() :: #rtmpmsg_event_stream_dry{}.
-type event_set_buffer_length() :: #rtmpmsg_event_set_buffer_length{}.
-type event_stream_is_recorded() :: #rtmpmsg_event_stream_is_recorded{}.
-type event_ping_request() :: #rtmpmsg_event_ping_request{}.
-type event_ping_response() :: #rtmpmsg_event_ping_response{}.
-type event_buffer_empty() :: #rtmpmsg_event_buffer_empty{}.
-type event_buffer_ready() :: #rtmpmsg_event_buffer_ready{}.
-type event_unknown() :: #rtmpmsg_event_unknown{}.
-type set_peer_bandwidth_limit_type() :: hard | soft | dynamic.
-type chunk() :: #chunk{}.
%%================================================================================
%% RTMP Message Construct API
%%================================================================================
%% @doc Make RTMP message
-spec message(message_stream_id(), message_timestamp(), message_body()) -> message().
message(StreamId, Timestamp, Body) ->
#rtmpmsg
{
type_id = get_type_id(Body),
stream_id = StreamId,
timestamp = Timestamp,
body = Body
}.
%% @doc Make RTMP Protocol Control Message
-spec protocol_control_message(message_body_protocol_control()) -> message().
protocol_control_message(Body) -> message(?PCM_MESSAGE_STREAM_ID, 0, Body).
%% @doc Make RTMP User Control Message
-spec user_control_message(user_control_event()) -> message().
user_control_message(Event) -> protocol_control_message(#rtmpmsg_user_control{event=Event}).
%% @doc Make SetChunkSize Message
-spec set_chunk_size(chunk_size()) -> message().
set_chunk_size(ChunkSize) -> protocol_control_message(#rtmpmsg_set_chunk_size{size=ChunkSize}).
%% @doc Make Abort Message
-spec abort(chunk_stream_id()) -> message().
abort(ChunkStreamId) -> protocol_control_message(#rtmpmsg_abort{chunk_stream_id=ChunkStreamId}).
%% @doc Make Acknowledgement Message
-spec ack(byte_size()) -> message().
ack(SequenceNumber) -> protocol_control_message(#rtmpmsg_ack{sequence_number=SequenceNumber}).
%% @doc Make WindowAcknowledgementSize Message
-spec win_ack_size(byte_size()) -> message().
win_ack_size(WindowSize) -> protocol_control_message(#rtmpmsg_win_ack_size{size=WindowSize}).
%% @doc Make SetPeerBandwidth Message
-spec set_peer_bandwidth(byte_size(), set_peer_bandwidth_limit_type()) -> message().
set_peer_bandwidth(WindowSize, LimitType) -> protocol_control_message(#rtmpmsg_set_peer_bandwidth{size=WindowSize, limit_type=LimitType}).
%% @doc Make StreamBeginEvent Message
-spec event_stream_begin(message_stream_id()) -> message().
event_stream_begin(StreamId) -> user_control_message(#rtmpmsg_event_stream_begin{stream_id=StreamId}).
%% @doc Make StreamEofEvent Message
-spec event_stream_eof(message_stream_id()) -> message().
event_stream_eof(StreamId) -> user_control_message(#rtmpmsg_event_stream_eof{stream_id=StreamId}).
%% @doc Make StreamDryEvent Message
-spec event_stream_dry(message_stream_id()) -> message().
event_stream_dry(StreamId) -> user_control_message(#rtmpmsg_event_stream_dry{stream_id=StreamId}).
%% @doc Make SetBufferLengthEvent Message
-spec event_set_buffer_length(message_stream_id(), milliseconds()) -> message().
event_set_buffer_length(StreamId, Length) -> user_control_message(#rtmpmsg_event_set_buffer_length{stream_id=StreamId, length=Length}).
%% @doc Make StreamIsRecordedEvent Message
-spec event_stream_is_recorded(message_stream_id()) -> message().
event_stream_is_recorded(StreamId) -> user_control_message(#rtmpmsg_event_stream_is_recorded{stream_id=StreamId}).
%% @doc Make PingRequestEvent Message
-spec event_ping_request(milliseconds()) -> message().
event_ping_request(Timestamp) -> user_control_message(#rtmpmsg_event_ping_request{timestamp=Timestamp}).
%% @doc Make PingResponseEvent Message
-spec event_ping_response(milliseconds()) -> message().
event_ping_response(Timestamp) -> user_control_message(#rtmpmsg_event_ping_response{timestamp=Timestamp}).
%% @doc Make BufferEmptyEvent Message
-spec event_buffer_empty(message_stream_id()) -> message().
event_buffer_empty(StreamId) -> user_control_message(#rtmpmsg_event_buffer_empty{stream_id=StreamId}).
%% @doc Make BufferReadyEvent Message
-spec event_buffer_ready(message_stream_id()) -> message().
event_buffer_ready(StreamId) -> user_control_message(#rtmpmsg_event_buffer_ready{stream_id=StreamId}).
%% @doc Make Audio Message
-spec audio(message_stream_id(), milliseconds(), binary()) -> message().
audio(StreamId, Timestamp, <<AudioData/binary>>) -> message(StreamId, Timestamp, #rtmpmsg_audio{data=AudioData}).
%% @doc Make Video Message
-spec video(message_stream_id(), milliseconds(), binary()) -> message().
video(StreamId, Timestamp, <<VideoData/binary>>) -> message(StreamId, Timestamp, #rtmpmsg_video{data=VideoData}).
%% @doc Make Command Message
-spec command(message_stream_id(), amf:amf_version(), amf:amf_string(), amf:amf_number(), amf:amf_value(), [amf:amf_value()]) -> message().
command(StreamId, AmfVersion, Name, TransactionId, Object, Args) ->
message(StreamId, 0, #rtmpmsg_command{amf_version=AmfVersion,
name=Name,
transaction_id=TransactionId,
object=Object,
args=Args}).
%% @doc Make Command Message (for response)
-spec response_command(message(), amf:amf_string(), amf:amf_value(), [amf:amf_value()]) -> message().
response_command(RequestMessage, Name, Object, Args) ->
#rtmpmsg{stream_id=StreamId, body=Cmd} = RequestMessage,
#rtmpmsg_command{amf_version=AmfVer, transaction_id=TransactionId} = Cmd,
command(StreamId, AmfVer, Name, TransactionId, Object, Args).
%% @doc Make Command Message (for notify)
-spec notify_command(message(), amf:amf_string(), amf:amf_value(), [amf:amf_value()]) -> message().
notify_command(RequestMessage, Name, Object, Args) ->
#rtmpmsg{stream_id=StreamId, body=Cmd} = RequestMessage,
#rtmpmsg_command{amf_version=AmfVer} = Cmd,
notify_command(StreamId, AmfVer, Name, Object, Args).
%% @doc Make Command Message (for notify)
-spec notify_command(message_stream_id(), amf:amf_version(), amf:amf_string(), amf:amf_value(), [amf:amf_value()]) -> message().
notify_command(StreamId, AmfVersion, Name, Object, Args) -> command(StreamId, AmfVersion, Name, 0, Object, Args).
%% @doc Make Data Message
%%
%% XXX: deprecated
-spec data(message_stream_id(), amf:amf_version(), [amf:amf_value()]) -> message().
data(StreamId, AmfVersion, Values) -> data(StreamId, 0, AmfVersion, Values).
%% @doc Make Data Message
-spec data(message_stream_id(), milliseconds(), amf:amf_version(), [amf:amf_value()]) -> message().
data(StreamId, Timestamp, AmfVersion, Values) -> message(StreamId, Timestamp, #rtmpmsg_data{amf_version=AmfVersion, values=Values}).
%% @doc Make SharedObject Message (experimental)
-spec shared_object(message_stream_id(), amf:amf_version(), binary()) -> message().
shared_object(StreamId, AmfVersion, Payload) -> message(StreamId, 0, #rtmpmsg_shared_object{amf_version=AmfVersion, payload=Payload}).
%% @doc Make Aggregate Message
-spec aggregate(message_stream_id(), [message()]) -> message().
aggregate(StreamId, [Head | _] = Messages) -> message(StreamId, Head#rtmpmsg.timestamp, #rtmpmsg_aggregate{messages = Messages}).
%%================================================================================
%% Internal Functions
%%================================================================================
-spec get_type_id(message_body()) -> message_type_id().
get_type_id(#rtmpmsg_set_chunk_size{}) -> ?TYPE_SET_CHUNK_SIZE;
get_type_id(#rtmpmsg_abort{}) -> ?TYPE_ABORT;
get_type_id(#rtmpmsg_ack{}) -> ?TYPE_ACK;
get_type_id(#rtmpmsg_win_ack_size{}) -> ?TYPE_WIN_ACK_SIZE;
get_type_id(#rtmpmsg_set_peer_bandwidth{}) -> ?TYPE_SET_PEER_BANDWIDTH;
get_type_id(#rtmpmsg_user_control{}) -> ?TYPE_USER_CONTROL;
get_type_id(#rtmpmsg_audio{}) -> ?TYPE_AUDIO;
get_type_id(#rtmpmsg_video{}) -> ?TYPE_VIDEO;
get_type_id(#rtmpmsg_command{amf_version=amf0}) -> ?TYPE_COMMAND_AMF0;
get_type_id(#rtmpmsg_command{amf_version=amf3}) -> ?TYPE_COMMAND_AMF3;
get_type_id(#rtmpmsg_data{amf_version=amf0}) -> ?TYPE_DATA_AMF0;
get_type_id(#rtmpmsg_data{amf_version=amf3}) -> ?TYPE_DATA_AMF3;
get_type_id(#rtmpmsg_shared_object{amf_version=amf0}) -> ?TYPE_SHARED_OBJECT_AMF0;
get_type_id(#rtmpmsg_shared_object{amf_version=amf3}) -> ?TYPE_SHARED_OBJECT_AMF3;
get_type_id(#rtmpmsg_aggregate{}) -> ?TYPE_AGGREGATE;
get_type_id(#rtmpmsg_unknown{type_id=TypeId}) -> TypeId.