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/// Variants correspond to respectively following chunks:
173///
174/// |Patttern|Message Type|
175/// | :- | :- |
176/// |`Network`|[`ChunkSize`]|
177/// |`System`|[`Command`]|
178/// |`Audio`|[`SetDataFrame`], [`Audio`]|
179/// |`Video`|[`Video`]|
180/// |`Other`|other chunks|
181///
182/// [`ChunkSize`]: ChunkSize
183/// [`Command`]: Command
184/// [`SetDataFrame`]: SetDataFrame
185/// [`Audio`]: Audio
186/// [`Video`]: Video
187#[repr(u16)]
188#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
189pub enum Channel {
190 Network = 2,
191 System = 3,
192 Audio = 4,
193 Video = 6,
194 Source = 8,
195 Other = 0xffff
196}
197
198impl From<u16> for Channel {
199 fn from(channel: u16) -> Self {
200 use Channel::*;
201
202 match channel {
203 2 => Network,
204 3 => System,
205 4 => Audio,
206 6 => Video,
207 8 => Source,
208 _ => Other
209 }
210 }
211}
212
213impl From<Channel> for u16 {
214 fn from(channel: Channel) -> u16 {
215 channel as u16
216 }
217}
218
219/// Uniforms chunk data by a channel numbers and a message type.
220///
221/// When writing into streams, we are required to imprint a chunk ID and a message type ID into their headers.
222/// This makes you to reduce its cost.
223/// For example, [`write_chunk`] use this for writing chunks correctly.
224///
225/// [`write_chunk`]: crate::writers::write_chunk
226pub trait ChunkData {
227 const CHANNEL: Channel;
228 const MESSAGE_TYPE: MessageType;
229}
230
231pub trait Command {}
232
233/// The IDs which are types of user control messages.
234///
235/// Variants correspond to respectively following events:
236///
237/// |Patttern|Message Type|
238/// | :- | :- |
239/// |`StreamBegin`|[`StreamBegin`]|
240/// |`SetBufferLength`|[`SetBufferLength`]|
241/// |`Other`|other event type|
242///
243/// [`StreamBegin`]: StreamBegin
244/// [`SetBufferLength`]: SetBufferLength
245#[repr(u16)]
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub enum EventType {
248 StreamBegin,
249 SetBufferLength = 3,
250 Other = 0xffff
251}
252
253impl From<u16> for EventType {
254 fn from(event_type: u16) -> Self {
255 use EventType::*;
256
257 match event_type {
258 0 => StreamBegin,
259 3 => SetBufferLength,
260 _ => Other
261 }
262 }
263}
264
265impl From<EventType> for u16 {
266 fn from(event_type: EventType) -> Self {
267 event_type as u16
268 }
269}
270
271/// Uniforms user control messages by an event type.
272///
273/// When reading from streams or writing into streams, we are required to imprint a event type ID into their messages.
274/// This makes you to reduce its cost.
275pub trait UserControl {
276 const EVENT_TYPE: EventType;
277}
278
279impl PartialEq<WindowAcknowledgementSize> for Acknowledgement {
280 fn eq(&self, other: &WindowAcknowledgementSize) -> bool {
281 self.get_inner().eq(&other.get_inner())
282 }
283}
284
285impl PartialOrd<WindowAcknowledgementSize> for Acknowledgement {
286 fn partial_cmp(&self, other: &WindowAcknowledgementSize) -> Option<Ordering> {
287 self.get_inner().partial_cmp(&other.get_inner())
288 }
289}
290
291impl PartialEq<Acknowledgement> for WindowAcknowledgementSize {
292 fn eq(&self, other: &Acknowledgement) -> bool {
293 self.get_inner().eq(&other.get_inner())
294 }
295}
296
297impl PartialOrd<Acknowledgement> for WindowAcknowledgementSize {
298 fn partial_cmp(&self, other: &Acknowledgement) -> Option<Ordering> {
299 self.get_inner().partial_cmp(&other.get_inner())
300 }
301}
302
303impl PartialEq<PeerBandwidth> for Acknowledgement {
304 fn eq(&self, other: &PeerBandwidth) -> bool {
305 self.get_inner().eq(&other.get_inner_bandwidth())
306 }
307}
308
309impl PartialOrd<PeerBandwidth> for Acknowledgement {
310 fn partial_cmp(&self, other: &PeerBandwidth) -> Option<Ordering> {
311 self.get_inner().partial_cmp(&other.get_inner_bandwidth())
312 }
313}
314
315impl PartialEq<Acknowledgement> for PeerBandwidth {
316 fn eq(&self, other: &Acknowledgement) -> bool {
317 self.get_inner_bandwidth().eq(&other.get_inner())
318 }
319}
320
321impl PartialOrd<Acknowledgement> for PeerBandwidth {
322 fn partial_cmp(&self, other: &Acknowledgement) -> Option<Ordering> {
323 self.get_inner_bandwidth().partial_cmp(&other.get_inner())
324 }
325}