Skip to main content

msf_rtsp/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3#[cfg(any(feature = "client", feature = "server"))]
4mod connection;
5
6#[cfg(any(feature = "client", feature = "server"))]
7mod interleaved;
8
9#[cfg(feature = "client")]
10#[cfg_attr(docsrs, doc(cfg(feature = "client")))]
11pub mod client;
12
13pub mod header;
14pub mod request;
15pub mod response;
16
17#[cfg(feature = "server")]
18#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
19pub mod server;
20
21#[cfg(any(feature = "client", feature = "server"))]
22#[cfg_attr(docsrs, doc(cfg(any(feature = "client", feature = "server"))))]
23pub mod udp;
24
25use std::{
26    convert::Infallible,
27    fmt::{self, Display, Formatter},
28    io,
29    str::FromStr,
30};
31
32use bytes::Bytes;
33use ttpkit::Error as BaseError;
34
35#[cfg(feature = "server")]
36use self::server::OutgoingResponse;
37
38pub use msf_rtp as rtp;
39pub use msf_sdp as sdp;
40
41pub use ttpkit::{self, error::CodecError};
42
43#[cfg(any(feature = "client", feature = "server"))]
44#[cfg_attr(docsrs, doc(cfg(any(feature = "client", feature = "server"))))]
45pub use ttpkit_url as url;
46
47pub use self::{
48    request::{Request, RequestHeader},
49    response::{Response, ResponseHeader, Status},
50};
51
52/// Inner error.
53#[derive(Debug)]
54enum InnerError {
55    Error(BaseError),
56
57    #[cfg(feature = "server")]
58    ErrorWithResponse(Box<dyn ErrorToResponse + Send + Sync>),
59}
60
61/// Error type.
62#[derive(Debug)]
63pub struct Error {
64    inner: InnerError,
65}
66
67impl Error {
68    /// Create a new error with a given message.
69    pub fn from_msg<T>(msg: T) -> Self
70    where
71        T: Into<String>,
72    {
73        Self {
74            inner: InnerError::Error(BaseError::from_msg(msg)),
75        }
76    }
77
78    /// Create a new error with a given message.
79    #[inline]
80    pub const fn from_static_msg(msg: &'static str) -> Self {
81        Self {
82            inner: InnerError::Error(BaseError::from_static_msg(msg)),
83        }
84    }
85
86    /// Create a new error with a given message and cause.
87    pub fn from_msg_and_cause<T, E>(msg: T, cause: E) -> Self
88    where
89        T: Into<String>,
90        E: Into<Box<dyn std::error::Error + Send + Sync>>,
91    {
92        Self {
93            inner: InnerError::Error(BaseError::from_msg_and_cause(msg, cause)),
94        }
95    }
96
97    /// Create a new error with a given message and cause.
98    pub fn from_static_msg_and_cause<E>(msg: &'static str, cause: E) -> Self
99    where
100        E: Into<Box<dyn std::error::Error + Send + Sync>>,
101    {
102        Self {
103            inner: InnerError::Error(BaseError::from_static_msg_and_cause(msg, cause)),
104        }
105    }
106
107    /// Create a new error from a given custom error.
108    pub fn from_other<T>(err: T) -> Self
109    where
110        T: Into<Box<dyn std::error::Error + Send + Sync>>,
111    {
112        Self {
113            inner: InnerError::Error(BaseError::from_cause(err)),
114        }
115    }
116
117    /// Create a new error from a given custom error.
118    #[cfg(feature = "server")]
119    #[cfg_attr(docsrs, doc(cfg(feature = "server")))]
120    pub fn from_other_with_response<T>(err: T) -> Self
121    where
122        T: ErrorToResponse + Send + Sync + 'static,
123    {
124        Self {
125            inner: InnerError::ErrorWithResponse(Box::new(err)),
126        }
127    }
128
129    /// Get error response (if supported).
130    #[cfg(feature = "server")]
131    #[cfg_attr(docsrs, doc(cfg(feature = "server")))]
132    pub fn to_response(&self) -> Option<OutgoingResponse> {
133        if let InnerError::ErrorWithResponse(err) = &self.inner {
134            Some(err.to_response())
135        } else {
136            None
137        }
138    }
139}
140
141impl Display for Error {
142    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
143        match &self.inner {
144            InnerError::Error(err) => Display::fmt(err, f),
145
146            #[cfg(feature = "server")]
147            InnerError::ErrorWithResponse(err) => Display::fmt(err, f),
148        }
149    }
150}
151
152impl std::error::Error for Error {
153    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
154        match &self.inner {
155            InnerError::Error(err) => err.source(),
156
157            #[cfg(feature = "server")]
158            InnerError::ErrorWithResponse(err) => err.source(),
159        }
160    }
161}
162
163impl From<Infallible> for Error {
164    #[inline]
165    fn from(_: Infallible) -> Self {
166        unreachable!()
167    }
168}
169
170impl From<io::Error> for Error {
171    #[inline]
172    fn from(err: io::Error) -> Self {
173        Self::from_msg_and_cause("IO", err)
174    }
175}
176
177impl From<str_reader::ParseError> for Error {
178    #[inline]
179    fn from(err: str_reader::ParseError) -> Self {
180        Self::from_other(err)
181    }
182}
183
184#[cfg(feature = "server")]
185impl<T> From<T> for Error
186where
187    T: ErrorToResponse + Send + Sync + 'static,
188{
189    fn from(err: T) -> Self {
190        Self::from_other_with_response(err)
191    }
192}
193
194impl From<Error> for ttpkit::Error {
195    fn from(err: Error) -> Self {
196        match err.inner {
197            InnerError::Error(err) => err,
198
199            #[cfg(feature = "server")]
200            InnerError::ErrorWithResponse(_) => ttpkit::Error::from_cause(err),
201        }
202    }
203}
204
205/// Trait for errors that can generate an error response.
206#[cfg(feature = "server")]
207#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
208pub trait ErrorToResponse: std::error::Error {
209    /// Create a custom RTSP error response.
210    fn to_response(&self) -> OutgoingResponse;
211}
212
213/// Type placeholder for RTSP protocol.
214#[derive(Debug, Copy, Clone, Eq, PartialEq)]
215struct Protocol;
216
217impl AsRef<[u8]> for Protocol {
218    #[inline]
219    fn as_ref(&self) -> &[u8] {
220        b"RTSP"
221    }
222}
223
224impl TryFrom<Bytes> for Protocol {
225    type Error = Error;
226
227    fn try_from(value: Bytes) -> Result<Self, Self::Error> {
228        if value.as_ref() == b"RTSP" {
229            Ok(Self)
230        } else {
231            Err(Error::from_msg(format!(
232                "invalid protocol string \"{}\"",
233                value.escape_ascii()
234            )))
235        }
236    }
237}
238
239/// RTSP version.
240#[derive(Debug, Copy, Clone, Eq, PartialEq)]
241pub enum Version {
242    Version10,
243    Version20,
244}
245
246impl AsRef<[u8]> for Version {
247    #[inline]
248    fn as_ref(&self) -> &[u8] {
249        match self {
250            Self::Version10 => b"1.0",
251            Self::Version20 => b"2.0",
252        }
253    }
254}
255
256impl AsRef<str> for Version {
257    #[inline]
258    fn as_ref(&self) -> &str {
259        match self {
260            Self::Version10 => "1.0",
261            Self::Version20 => "2.0",
262        }
263    }
264}
265
266impl Display for Version {
267    #[inline]
268    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
269        f.write_str(self.as_ref())
270    }
271}
272
273impl TryFrom<Bytes> for Version {
274    type Error = Error;
275
276    fn try_from(value: Bytes) -> Result<Self, Self::Error> {
277        let res = match value.as_ref() {
278            b"1.0" => Self::Version10,
279            b"2.0" => Self::Version20,
280            _ => {
281                return Err(Error::from_msg(format!(
282                    "unsupported RTSP protocol version: \"{}\"",
283                    value.escape_ascii()
284                )));
285            }
286        };
287
288        Ok(res)
289    }
290}
291
292/// RTSP method.
293#[derive(Debug, Copy, Clone, Eq, PartialEq)]
294pub enum Method {
295    Options,
296    Describe,
297    Announce,
298    Setup,
299    Play,
300    Pause,
301    Teardown,
302    GetParameter,
303    SetParameter,
304    Redirect,
305    Record,
306}
307
308impl AsRef<[u8]> for Method {
309    fn as_ref(&self) -> &[u8] {
310        match self {
311            Self::Options => b"OPTIONS",
312            Self::Describe => b"DESCRIBE",
313            Self::Announce => b"ANNOUNCE",
314            Self::Setup => b"SETUP",
315            Self::Play => b"PLAY",
316            Self::Pause => b"PAUSE",
317            Self::Teardown => b"TEARDOWN",
318            Self::GetParameter => b"GET_PARAMETER",
319            Self::SetParameter => b"SET_PARAMETER",
320            Self::Redirect => b"REDIRECT",
321            Self::Record => b"RECORD",
322        }
323    }
324}
325
326impl AsRef<str> for Method {
327    fn as_ref(&self) -> &str {
328        match self {
329            Self::Options => "OPTIONS",
330            Self::Describe => "DESCRIBE",
331            Self::Announce => "ANNOUNCE",
332            Self::Setup => "SETUP",
333            Self::Play => "PLAY",
334            Self::Pause => "PAUSE",
335            Self::Teardown => "TEARDOWN",
336            Self::GetParameter => "GET_PARAMETER",
337            Self::SetParameter => "SET_PARAMETER",
338            Self::Redirect => "REDIRECT",
339            Self::Record => "RECORD",
340        }
341    }
342}
343
344impl Display for Method {
345    #[inline]
346    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
347        f.write_str(self.as_ref())
348    }
349}
350
351impl TryFrom<Bytes> for Method {
352    type Error = Error;
353
354    fn try_from(value: Bytes) -> Result<Self, Self::Error> {
355        let res = match value.as_ref() {
356            b"OPTIONS" => Self::Options,
357            b"DESCRIBE" => Self::Describe,
358            b"ANNOUNCE" => Self::Announce,
359            b"SETUP" => Self::Setup,
360            b"PLAY" => Self::Play,
361            b"PAUSE" => Self::Pause,
362            b"TEARDOWN" => Self::Teardown,
363            b"GET_PARAMETER" => Self::GetParameter,
364            b"SET_PARAMETER" => Self::SetParameter,
365            b"REDIRECT" => Self::Redirect,
366            b"RECORD" => Self::Record,
367            _ => {
368                return Err(Error::from_msg(format!(
369                    "unsupported RTSP method: \"{}\"",
370                    value.escape_ascii()
371                )));
372            }
373        };
374
375        Ok(res)
376    }
377}
378
379/// Valid URL schemes.
380#[allow(clippy::upper_case_acronyms)]
381#[derive(Debug, Copy, Clone, Eq, PartialEq)]
382pub enum Scheme {
383    RTSP,
384}
385
386impl Scheme {
387    /// Get default port for this URL scheme.
388    #[inline]
389    pub fn default_port(self) -> u16 {
390        match self {
391            Self::RTSP => 554,
392        }
393    }
394}
395
396impl AsRef<str> for Scheme {
397    #[inline]
398    fn as_ref(&self) -> &str {
399        match self {
400            Self::RTSP => "rtsp",
401        }
402    }
403}
404
405impl Display for Scheme {
406    #[inline]
407    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
408        f.write_str(self.as_ref())
409    }
410}
411
412impl FromStr for Scheme {
413    type Err = Error;
414
415    fn from_str(value: &str) -> Result<Self, Self::Err> {
416        if value.eq_ignore_ascii_case("rtsp") {
417            Ok(Self::RTSP)
418        } else {
419            Err(Error::from_msg(format!("invalid URL scheme: \"{value}\"")))
420        }
421    }
422}