skyway_webrtc_gateway_api/media/
formats.rs

1use std::fmt;
2
3use serde::de::{self, Visitor};
4use serde::{Deserialize, Deserializer, Serialize};
5
6use crate::common::formats::{PhantomId, SerializableId, SocketInfo};
7use crate::error;
8use crate::prelude::{PeerId, Token};
9
10/// Identifier for source socket of media
11#[derive(Serialize, Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
12pub struct MediaId(String);
13
14impl SerializableId for MediaId {
15    fn try_create(media_id: impl Into<String>) -> Result<Self, error::Error>
16    where
17        Self: Sized,
18    {
19        // peer token's prefix is composed of a UUID and a prefix "pt-".
20        let media_id = media_id.into();
21        if !(media_id.starts_with("vi-") || media_id.starts_with("au-")) {
22            return Err(error::Error::create_local_error(
23                "media_id\'s prefix is \"vi-\" or \"au-\"",
24            ));
25        }
26        if media_id.len() != 39 {
27            // It's length is 39(UUID: 36 + prefix: 3).
28            return Err(error::Error::create_local_error(
29                "token str's length should be 39",
30            ));
31        }
32        if !media_id.is_ascii() {
33            return Err(error::Error::create_local_error(
34                "token str should be ascii",
35            ));
36        }
37
38        Ok(MediaId(media_id))
39    }
40
41    fn as_str(&self) -> &str {
42        self.0.as_str()
43    }
44
45    fn id(&self) -> String {
46        self.0.clone()
47    }
48
49    fn key(&self) -> &'static str {
50        "media_id"
51    }
52}
53
54struct MediaIdVisitor;
55
56impl<'de> Visitor<'de> for MediaIdVisitor {
57    type Value = MediaId;
58
59    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
60        formatter.write_str("a 39 length str")
61    }
62
63    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
64    where
65        E: de::Error,
66    {
67        let media_id = MediaId::try_create(value);
68        if let Err(error::Error::LocalError(err)) = media_id {
69            return Err(E::custom(format!("fail to deserialize MediaId: {}", err)));
70        } else if let Err(_) = media_id {
71            return Err(E::custom(format!("fail to deserialize MediaId")));
72        }
73
74        Ok(media_id.unwrap())
75    }
76
77    fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
78    where
79        E: de::Error,
80    {
81        let media_id = MediaId::try_create(value);
82        if let Err(error::Error::LocalError(err)) = media_id {
83            return Err(E::custom(format!("fail to deserialize MediaId: {}", err)));
84        } else if let Err(_) = media_id {
85            return Err(E::custom(format!("fail to deserialize MediaId")));
86        }
87
88        Ok(media_id.unwrap())
89    }
90}
91
92impl<'de> Deserialize<'de> for MediaId {
93    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
94    where
95        D: Deserializer<'de>,
96    {
97        deserializer.deserialize_identifier(MediaIdVisitor)
98    }
99}
100
101/// Identifier for source socket of rtcp
102#[derive(Serialize, Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
103pub struct RtcpId(String);
104
105impl SerializableId for RtcpId {
106    fn try_create(rtcp_id: impl Into<String>) -> Result<Self, error::Error>
107    where
108        Self: Sized,
109    {
110        // peer token's prefix is composed of a UUID and a prefix "pt-".
111        let rtcp_id = rtcp_id.into();
112        if !rtcp_id.starts_with("rc-") {
113            return Err(error::Error::create_local_error(
114                "rtcp_id\'s prefix is \"rc-\"",
115            ));
116        }
117        if rtcp_id.len() != 39 {
118            // It's length is 39(UUID: 36 + prefix: 3).
119            return Err(error::Error::create_local_error(
120                "token str's length should be 39",
121            ));
122        }
123        if !rtcp_id.is_ascii() {
124            return Err(error::Error::create_local_error(
125                "token str should be ascii",
126            ));
127        }
128
129        Ok(RtcpId(rtcp_id))
130    }
131
132    fn as_str(&self) -> &str {
133        self.0.as_str()
134    }
135
136    fn id(&self) -> String {
137        self.0.clone()
138    }
139
140    fn key(&self) -> &'static str {
141        "rtcp_id"
142    }
143}
144
145struct RtcpIdVisitor;
146
147impl<'de> Visitor<'de> for RtcpIdVisitor {
148    type Value = RtcpId;
149
150    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
151        formatter.write_str("a 39 length str")
152    }
153
154    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
155    where
156        E: de::Error,
157    {
158        let media_id = RtcpId::try_create(value);
159        if let Err(error::Error::LocalError(err)) = media_id {
160            return Err(E::custom(format!("fail to deserialize RtcpId: {}", err)));
161        } else if let Err(_) = media_id {
162            return Err(E::custom(format!("fail to deserialize RtcpId")));
163        }
164
165        Ok(media_id.unwrap())
166    }
167
168    fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
169    where
170        E: de::Error,
171    {
172        let media_id = RtcpId::try_create(value);
173        if let Err(error::Error::LocalError(err)) = media_id {
174            return Err(E::custom(format!("fail to deserialize RtcpId: {}", err)));
175        } else if let Err(_) = media_id {
176            return Err(E::custom(format!("fail to deserialize RtcpId")));
177        }
178
179        Ok(media_id.unwrap())
180    }
181}
182
183impl<'de> Deserialize<'de> for RtcpId {
184    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
185    where
186        D: Deserializer<'de>,
187    {
188        deserializer.deserialize_identifier(RtcpIdVisitor)
189    }
190}
191
192/// Identifier for MediaConnection
193#[derive(Serialize, Debug, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
194pub struct MediaConnectionId(String);
195
196impl MediaConnectionId {
197    pub fn as_str(&self) -> &str {
198        self.0.as_str()
199    }
200
201    pub fn try_create(media_connection_id: impl Into<String>) -> Result<Self, error::Error>
202    where
203        Self: Sized,
204    {
205        // peer token's prefix is composed of a UUID and a prefix "pt-".
206        let media_connection_id = media_connection_id.into();
207        if !media_connection_id.starts_with("mc-") {
208            return Err(error::Error::create_local_error(
209                "media_connection_id\'s prefix is \"mc-\"",
210            ));
211        }
212        if media_connection_id.len() != 39 {
213            // It's length is 39(UUID: 36 + prefix: 3).
214            return Err(error::Error::create_local_error(
215                "token str's length should be 39",
216            ));
217        }
218        if !media_connection_id.is_ascii() {
219            return Err(error::Error::create_local_error(
220                "token str should be ascii",
221            ));
222        }
223
224        Ok(MediaConnectionId(media_connection_id))
225    }
226}
227
228struct MediaConnectionIdVisitor;
229
230impl<'de> Visitor<'de> for MediaConnectionIdVisitor {
231    type Value = MediaConnectionId;
232
233    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
234        formatter.write_str("a 39 length str")
235    }
236
237    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
238    where
239        E: de::Error,
240    {
241        let media_connection_id = MediaConnectionId::try_create(value);
242        if let Err(error::Error::LocalError(err)) = media_connection_id {
243            return Err(E::custom(format!("fail to deserialize MediaId: {}", err)));
244        } else if let Err(_) = media_connection_id {
245            return Err(E::custom(format!("fail to deserialize MediaId")));
246        }
247
248        Ok(media_connection_id.unwrap())
249    }
250
251    fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
252    where
253        E: de::Error,
254    {
255        let media_connection_id = MediaConnectionId::try_create(value);
256        if let Err(error::Error::LocalError(err)) = media_connection_id {
257            return Err(E::custom(format!("fail to deserialize MediaId: {}", err)));
258        } else if let Err(_) = media_connection_id {
259            return Err(E::custom(format!("fail to deserialize MediaId")));
260        }
261
262        Ok(media_connection_id.unwrap())
263    }
264}
265
266impl<'de> Deserialize<'de> for MediaConnectionId {
267    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
268    where
269        D: Deserializer<'de>,
270    {
271        deserializer.deserialize_identifier(MediaConnectionIdVisitor)
272    }
273}
274
275#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
276pub(crate) struct CreateMediaOptions {
277    pub is_video: bool,
278}
279
280/// Query parameter for POST /media/connections
281///
282/// [API](http://35.200.46.204/#/3.media/media_connection_create)
283#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
284pub struct CallQuery {
285    /// to identify which PeerObject calls to neighbour
286    pub peer_id: PeerId,
287    /// to show that this program has permission to control PeerObject
288    pub token: Token,
289    /// connect to the neighbour which has this PeerId
290    pub target_id: PeerId,
291    /// Parameters for MediaConnection
292    /// It contains source socket. If the field is None, this MediaConnection works as RecvOnly.
293    #[serde(skip_serializing_if = "Option::is_none")]
294    pub constraints: Option<Constraints>,
295    /// Shows destiation socket to which received data is redirected
296    /// If this field is not set, DataConnection works as SendOnly.
297    #[serde(skip_serializing_if = "Option::is_none")]
298    pub redirect_params: Option<RedirectParameters>,
299}
300
301/// Parameters for MediaConnection
302#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
303#[allow(non_snake_case)]
304pub struct Constraints {
305    /// Shows whether this connection sends video or not
306    pub video: bool,
307    /// Shows whether this connection receives video or not
308    #[serde(skip_serializing_if = "Option::is_none")]
309    pub videoReceiveEnabled: Option<bool>,
310    /// Shows whether this connection sends audio or not
311    pub audio: bool,
312    /// Shows whether this connection receives audio or not
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub audioReceiveEnabled: Option<bool>,
315    /// Parameters for sending video
316    #[serde(skip_serializing_if = "Option::is_none")]
317    pub video_params: Option<MediaParams>,
318    /// Parameters for sending audio
319    #[serde(skip_serializing_if = "Option::is_none")]
320    pub audio_params: Option<MediaParams>,
321    /// metadata sent to a neighbour.
322    #[serde(skip_serializing_if = "Option::is_none")]
323    pub metadata: Option<String>,
324}
325
326/// Parameters for sending media
327#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
328pub struct MediaParams {
329    /// band width between Peers
330    pub band_width: usize,
331    /// Codec which caller side want to use. Video: `"H264"` or `"VP8"`, Audio: `"OPUS"` or `"G711"`. It will be used in SDP.
332    pub codec: String,
333    /// Identify which media should be redirected
334    pub media_id: MediaId,
335    /// Identify which rtcp should be redirected
336    #[serde(skip_serializing_if = "Option::is_none")]
337    pub rtcp_id: Option<RtcpId>,
338    /// Payload type which caller side want to use. It will be used in SDP.
339    #[serde(skip_serializing_if = "Option::is_none")]
340    pub payload_type: Option<u16>,
341    /// Sampling rate which media uses
342    #[serde(skip_serializing_if = "Option::is_none")]
343    pub sampling_rate: Option<usize>,
344}
345
346/// Shows to which socket media should be redirected.
347#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
348pub struct RedirectParameters {
349    /// video is redirected to this socket
350    #[serde(skip_serializing_if = "Option::is_none")]
351    pub video: Option<SocketInfo<PhantomId>>,
352    /// video rtcp is redirected to this socket
353    #[serde(skip_serializing_if = "Option::is_none")]
354    pub video_rtcp: Option<SocketInfo<PhantomId>>,
355    /// audio is redirected to this socket
356    #[serde(skip_serializing_if = "Option::is_none")]
357    pub audio: Option<SocketInfo<PhantomId>>,
358    /// audio rtcp is redirected to this socket
359    #[serde(skip_serializing_if = "Option::is_none")]
360    pub audio_rtcp: Option<SocketInfo<PhantomId>>,
361}
362
363/// Response from POST /media/connections
364///
365/// [API](http://35.200.46.204/#/3.media/media_connection_create)
366#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
367pub struct CallResponse {
368    /// Fixed value as `"PEERS_CALL"`.
369    pub command_type: String,
370    /// Identifier for MediaConnection
371    pub params: MediaConnectionIdWrapper,
372}
373
374/// Wrapper for serializing JSON
375#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, PartialOrd)]
376pub struct MediaConnectionIdWrapper {
377    /// Identifier for MediaConnection
378    pub media_connection_id: MediaConnectionId,
379}
380
381/// Query parameter for POST /media/connections
382///
383/// [API](http://35.200.46.204/#/3.media/media_connection_answer)
384#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
385pub struct AnswerQuery {
386    /// Parameters for MediaConnection
387    /// It contains source socket. If the field is None, this MediaConnection works as RecvOnly.
388    pub constraints: Constraints,
389    /// Shows destiation socket to which received data is redirected
390    /// If this field is not set, DataConnection works as SendOnly.
391    #[serde(skip_serializing_if = "Option::is_none")]
392    pub redirect_params: Option<RedirectParameters>,
393}
394
395/// Response from POST /media/connections
396///
397/// [API](http://35.200.46.204/#/3.media/media_connection_answer)
398#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
399pub struct AnswerResponse {
400    /// Fixed value as `"MEDIA_CONNECTION_ANSWER"`.
401    pub command_type: String,
402    /// Shows media_ids used in this MediaConnection
403    pub params: AnswerResponseParams,
404}
405
406/// Shows media_ids used in this MediaConnection
407#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
408pub struct AnswerResponseParams {
409    #[serde(skip_serializing_if = "Option::is_none")]
410    pub video_id: Option<MediaId>,
411    #[serde(skip_serializing_if = "Option::is_none")]
412    pub audio_id: Option<MediaId>,
413}
414
415/// Events from GET /media/events API.
416/// It includes TIMEOUT, but the event is not needed for end-user-programs.
417/// So it's used internally.
418#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
419#[serde(tag = "event")]
420pub(crate) enum EventEnum {
421    READY,
422    STREAM,
423    CLOSE,
424    ERROR { error_message: String },
425    TIMEOUT,
426}
427
428/// Status of MediaConnection
429#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
430pub struct MediaConnectionStatus {
431    /// Metadata associated with the connection, passed in by whoever initiated the connection.
432    pub metadata: String,
433    /// Shows whether this MediaConnection is working or not.
434    pub open: bool,
435    /// Shows neighbour id
436    pub remote_id: PeerId,
437    /// Shows ssrc(Synchrozination Source) information
438    #[serde(skip_serializing_if = "Option::is_none")]
439    pub ssrc: Option<Vec<SsrcPair>>,
440}
441
442/// Shows ssrc(Synchrozination Source) information
443#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
444pub struct SsrcPair {
445    /// Identify Media
446    pub media_id: MediaId,
447    /// SSRC
448    pub ssrc: usize,
449}