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#[derive(Debug)]
54enum InnerError {
55 Error(BaseError),
56
57 #[cfg(feature = "server")]
58 ErrorWithResponse(Box<dyn ErrorToResponse + Send + Sync>),
59}
60
61#[derive(Debug)]
63pub struct Error {
64 inner: InnerError,
65}
66
67impl Error {
68 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 #[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 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 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 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 #[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 #[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#[cfg(feature = "server")]
207#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
208pub trait ErrorToResponse: std::error::Error {
209 fn to_response(&self) -> OutgoingResponse;
211}
212
213#[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#[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#[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#[allow(clippy::upper_case_acronyms)]
381#[derive(Debug, Copy, Clone, Eq, PartialEq)]
382pub enum Scheme {
383 RTSP,
384}
385
386impl Scheme {
387 #[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}