sheave_core/messages.rs
1//! # The RTMP Messages
2//!
3//! After handshake, both are required to negotiate what are needed for sending/receiving.
4//! Currently, following messages are used in negotiation:
5//!
6//! * User Control
7//! * [`Window Acknowledgement Size`]
8//! * Command
9//!
10//! Every message has headers.
11//! See [`sheave_core::messages::headers`] about them.
12//!
13//! ## User Control
14//!
15//! User Control messages are additional informations for starting streaming.
16//! These consist of following format:
17//!
18//! |Field|Length (in bytes)|Description|
19//! | :- | -: | :- |
20//! |Event Type|2|A marker which indicates a kind of data sent.|
21//! |Event Data|Various|Actual data of this message.|
22//!
23//! ### Events
24//!
25//! Currently, following events are used:
26//!
27//! * [`Stream Begin`]
28//! * [`Set Buffer Length`]
29//!
30//! ## Command
31//!
32//! Command messages are contained basic informations actually required to negotiate.
33//! These encodes/decodes into/from [`the Action Message Format 0`].
34//! These consist of following format:
35//!
36//! |Field|AMF Type|Description|
37//! | :- | :- | :- |
38//! |Command Name|[`String`]|The command which is currently negotiated.|
39//! |Transaction ID|[`Number`]|A number which indicates a step of this negotiation.|
40//! |Setting Data|Various|Actually negotiated data.|
41//!
42//! Furthermore commands have following patterns:
43//!
44//! * NetConnection
45//! * NetStream
46//!
47//! ### NetConnection
48//!
49//! NetConnection commands are defined as following messages:
50//!
51//! * [`connect`]
52//! * [`releaseStream`]
53//! * [`FCPublish`]
54//! * [`createStream`]
55//! * [`FCSubscribe`]
56//! * [`getStreamLength`] (in FFmpeg) / [`set_playlist`] (in OBS) (note these mayn't be sent.)
57//!
58//! ### NetStream
59//!
60//! NetStream commands are defined as following messages:
61//!
62//! * [`publish`]
63//! * [`play`]
64//!
65//! ## Any data
66//!
67//! After negotiation, the server receives actual audio/video data from clients.
68//! However also FLV metadata is received as the AMF (v0).
69//! The message what contains FLV metadata is called [`@setDataFrame`].
70//! This consists of following format:
71//!
72//! |Field|AMF Type|Description|
73//! | :- | :- | :- |
74//! |Command Name (probably)|[`String`]|`"@setDataFrame"`|
75//! |Setting Data|Various|In this case, see [`ScriptDataTag`].|
76//!
77//! [the Action Message Format 0]: amf::v0
78//! [`sheave_core::messages::headers`]: headers
79//! [`Number`]: amf::v0::Number
80//! [`Boolean`]: amf::v0::Boolean
81//! [`String`]: amf::v0::AmfString
82//! [`Object`]: amf::v0::Object
83//! [`Null`]: amf::v0::Null
84//! [`EcmaArray`]: amf::v0::EcmaArray
85//! [`connect`]: Connect
86//! [`releaseStream`]: ReleaseStream
87//! [`FCPublish`]: FcPublish
88//! [`createStream`]: CreateStream
89//! [`publish`]: Publish
90//! [`Stream Begin`]: StreamBegin
91//! [`onStatus`]: OnStatus
92//! [`@setDataFrame`]: SetDataFrame
93//! [`ScriptDataTag`]: crate::flv::tags::ScriptDataTag
94//! [`Window Acknowledgement Size`]: WindowAcknowledgementSize
95//! [`FCSubscribe`]: FcSubscribe
96//! [`getStreamLength`]: GetStreamLength
97//! [`set_playlist`]: SetPlaylist
98//! [`play`]: Play
99//! [`Set Buffer Length`]: SetBufferLength
100
101pub mod headers;
102pub mod amf;
103mod inconsistent_command;
104mod connect;
105mod chunk_size;
106mod connect_result;
107mod release_stream;
108mod release_stream_result;
109mod fc_publish;
110mod on_fc_publish;
111mod create_stream;
112mod create_stream_result;
113mod publish;
114mod inconsistent_event_type;
115mod stream_begin;
116mod on_status;
117mod set_data_frame;
118mod audio;
119mod video;
120mod acknowledgement;
121mod window_acknowledgement_size;
122mod peer_bandwidth;
123mod fc_unpublish;
124mod delete_stream;
125mod fc_subscribe;
126mod get_stream_length;
127mod get_stream_length_result;
128mod set_playlist;
129mod playlist_ready;
130mod play;
131mod set_buffer_length;
132mod command_error;
133
134use std::cmp::Ordering;
135use self::headers::MessageType;
136pub use self::{
137 inconsistent_command::*,
138 connect::*,
139 chunk_size::*,
140 connect_result::*,
141 release_stream::*,
142 release_stream_result::*,
143 fc_publish::*,
144 on_fc_publish::*,
145 create_stream::*,
146 create_stream_result::*,
147 publish::*,
148 inconsistent_event_type::*,
149 stream_begin::*,
150 on_status::*,
151 set_data_frame::*,
152 audio::*,
153 video::*,
154 acknowledgement::*,
155 window_acknowledgement_size::*,
156 peer_bandwidth::*,
157 fc_unpublish::*,
158 delete_stream::*,
159 fc_subscribe::*,
160 get_stream_length::*,
161 get_stream_length_result::*,
162 set_playlist::*,
163 playlist_ready::*,
164 play::*,
165 set_buffer_length::*,
166 command_error::*
167};
168
169/// The IDs which are assigned every roles of chunks.
170/// This is mainly used for the [`BasicHeader`]'s chunk ID.
171///
172/// [`BasicHeader`]: headers::BasicHeader
173#[repr(u16)]
174#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
175pub enum Channel {
176 Network = 2,
177 System = 3,
178 Audio = 4,
179 Video = 6,
180 Source = 8,
181 Other = 0xffff
182}
183
184impl From<u16> for Channel {
185 fn from(channel: u16) -> Self {
186 use Channel::*;
187
188 match channel {
189 2 => Network,
190 3 => System,
191 4 => Audio,
192 6 => Video,
193 8 => Source,
194 _ => Other
195 }
196 }
197}
198
199impl From<Channel> for u16 {
200 fn from(channel: Channel) -> u16 {
201 channel as u16
202 }
203}
204
205/// Uniforms chunk data by a channel numbers and a message type.
206///
207/// When writing into streams, we are required to imprint a chunk ID and a message type ID into their headers.
208/// This makes you to reduce its cost.
209/// For example, [`write_chunk`] use this for writing chunks correctly.
210///
211/// [`write_chunk`]: crate::writers::write_chunk
212pub trait ChunkData {
213 const CHANNEL: Channel;
214 const MESSAGE_TYPE: MessageType;
215}
216
217pub trait Command {}
218
219/// The IDs which are types of user control messages.
220///
221/// Variants correspond to respectively following events:
222///
223/// |Patttern|Event Type|
224/// | :- | :- |
225/// |`StreamBegin`|[`StreamBegin`]|
226/// |`SetBufferLength`|[`SetBufferLength`]|
227/// |`Other`|other event type|
228///
229/// [`StreamBegin`]: StreamBegin
230/// [`SetBufferLength`]: SetBufferLength
231#[repr(u16)]
232#[derive(Debug, Clone, Copy, PartialEq, Eq)]
233pub enum EventType {
234 StreamBegin,
235 SetBufferLength = 3,
236 Other = 0xffff
237}
238
239impl From<u16> for EventType {
240 fn from(event_type: u16) -> Self {
241 use EventType::*;
242
243 match event_type {
244 0 => StreamBegin,
245 3 => SetBufferLength,
246 _ => Other
247 }
248 }
249}
250
251impl From<EventType> for u16 {
252 fn from(event_type: EventType) -> Self {
253 event_type as u16
254 }
255}
256
257/// Uniforms user control messages by an event type.
258///
259/// When reading from streams or writing into streams, we are required to imprint a event type ID into their messages.
260/// This makes you to reduce its cost.
261pub trait UserControl {
262 const EVENT_TYPE: EventType;
263}
264
265impl PartialEq<WindowAcknowledgementSize> for Acknowledgement {
266 fn eq(&self, other: &WindowAcknowledgementSize) -> bool {
267 self.get_inner().eq(&other.get_inner())
268 }
269}
270
271impl PartialOrd<WindowAcknowledgementSize> for Acknowledgement {
272 fn partial_cmp(&self, other: &WindowAcknowledgementSize) -> Option<Ordering> {
273 self.get_inner().partial_cmp(&other.get_inner())
274 }
275}
276
277impl PartialEq<Acknowledgement> for WindowAcknowledgementSize {
278 fn eq(&self, other: &Acknowledgement) -> bool {
279 self.get_inner().eq(&other.get_inner())
280 }
281}
282
283impl PartialOrd<Acknowledgement> for WindowAcknowledgementSize {
284 fn partial_cmp(&self, other: &Acknowledgement) -> Option<Ordering> {
285 self.get_inner().partial_cmp(&other.get_inner())
286 }
287}
288
289impl PartialEq<PeerBandwidth> for Acknowledgement {
290 fn eq(&self, other: &PeerBandwidth) -> bool {
291 self.get_inner().eq(&other.get_inner_bandwidth())
292 }
293}
294
295impl PartialOrd<PeerBandwidth> for Acknowledgement {
296 fn partial_cmp(&self, other: &PeerBandwidth) -> Option<Ordering> {
297 self.get_inner().partial_cmp(&other.get_inner_bandwidth())
298 }
299}
300
301impl PartialEq<Acknowledgement> for PeerBandwidth {
302 fn eq(&self, other: &Acknowledgement) -> bool {
303 self.get_inner_bandwidth().eq(&other.get_inner())
304 }
305}
306
307impl PartialOrd<Acknowledgement> for PeerBandwidth {
308 fn partial_cmp(&self, other: &Acknowledgement) -> Option<Ordering> {
309 self.get_inner_bandwidth().partial_cmp(&other.get_inner())
310 }
311}