ftl_protocol/protocol/
handshake.rs

1use super::FtlError;
2
3#[derive(Default, Debug, Clone)]
4pub struct Vendor {
5    pub name: Option<String>,
6    pub version: Option<String>,
7}
8
9#[derive(Default, Debug, Clone)]
10pub struct Video {
11    pub codec: Option<String>,
12    pub height: Option<isize>,
13    pub width: Option<isize>,
14    pub payload_type: Option<u8>,
15    pub ssrc: Option<u32>,
16}
17
18#[derive(Default, Debug, Clone)]
19pub struct Audio {
20    pub codec: Option<String>,
21    pub payload_type: Option<u8>,
22    pub ssrc: Option<u32>,
23}
24
25#[derive(Default, Debug, Clone)]
26pub struct FtlHandshake {
27    pub protocol_version: Option<(isize, isize)>,
28    pub vendor: Vendor,
29    pub video: Option<Video>,
30    pub audio: Option<Audio>,
31}
32
33impl FtlHandshake {
34    /// Given a FTL attribute, insert it into the Handshake structure.
35    pub fn insert(&mut self, key: String, value: String) -> Result<(), FtlError> {
36        // ! FIXME: causing panics here, try not doing that
37        match key.as_ref() {
38            "ProtocolVersion" => {
39                let mut parts = value.split('.');
40                self.protocol_version = Some((
41                    parts.next().unwrap().parse().unwrap(),
42                    parts.next().unwrap().parse().unwrap()
43                ));
44            }
45            "VendorName" => self.vendor.name = Some(value),
46            "VendorVersion" => self.vendor.version = Some(value),
47            "Video" |
48            "Audio" => match value.as_ref() {
49                "true" => {
50                    if key == "Video" {
51                        self.video = Some(Video::default());
52                    } else {
53                        self.audio = Some(Audio::default());
54                    }
55                },
56                "false" => {},
57                _ => panic!("Failed to deserialise boolean.")
58            }
59            "VideoCodec" |
60            "VideoHeight" |
61            "VideoWidth" |
62            "VideoPayloadType" |
63            "VideoIngestSSRC" => if let Some(mut video) = self.video.as_mut() {
64                match key.as_ref() {
65                    "VideoCodec" => video.codec = Some(value),
66                    "VideoHeight" => video.height = Some(value.parse().unwrap()),
67                    "VideoWidth" => video.width = Some(value.parse().unwrap()),
68                    "VideoPayloadType" => video.payload_type = Some(value.parse().unwrap()),
69                    "VideoIngestSSRC" => video.ssrc = Some(value.parse().unwrap()),
70                    _ => unreachable!()
71                }
72            }
73            "AudioCodec" |
74            "AudioPayloadType" |
75            "AudioIngestSSRC" => if let Some(mut audio) = self.audio.as_mut() {
76                match key.as_ref() {
77                    "AudioCodec" => audio.codec = Some(value),
78                    "AudioPayloadType" => audio.payload_type = Some(value.parse().unwrap()),
79                    "AudioIngestSSRC" => audio.ssrc = Some(value.parse().unwrap()),
80                    _ => unreachable!()
81                }
82            }
83            _ => {}
84        }
85
86        Ok(())
87    }
88}
89
90//#region Finalised Handshake.
91#[derive(Debug, Clone)]
92pub struct KnownVideo {
93    pub codec: String,
94    pub height: isize,
95    pub width: isize,
96    pub payload_type: u8,
97    pub ssrc: u32,
98}
99
100#[derive(Debug, Clone)]
101pub struct KnownAudio {
102    pub codec: String,
103    pub payload_type: u8,
104    pub ssrc: u32,
105}
106
107#[derive(Debug, Clone)]
108pub struct FtlHandshakeFinalised {
109    pub protocol_version: (isize, isize),
110    pub vendor: Vendor,
111    pub video: Option<KnownVideo>,
112    pub audio: Option<KnownAudio>,
113}
114
115impl FtlHandshake {
116    pub fn finalise(self) -> Result<FtlHandshakeFinalised, FtlError> {
117        Ok(FtlHandshakeFinalised {
118            protocol_version: if let Some((major, minor)) = self.protocol_version {
119                if major != 0 && minor != 9 {
120                    return Err(FtlError::UnsupportedProtocolVersion)
121                }
122
123                (major, minor)
124            } else {
125                return Err(FtlError::InvalidProtocolVersion)
126            },
127            vendor: self.vendor,
128            video: if let Some(video) = self.video {
129                match (video.codec, video.width, video.height, video.payload_type, video.ssrc) {
130                    (Some(codec), Some(width), Some(height), Some(payload_type), Some(ssrc)) =>
131                        Some(KnownVideo { codec, width, height, payload_type, ssrc }),
132                    _ => return Err(FtlError::MissingCodecInformation)
133                }
134            } else { None },
135            audio: if let Some(audio) = self.audio {
136                match (audio.codec, audio.payload_type, audio.ssrc) {
137                    (Some(codec), Some(payload_type), Some(ssrc)) =>
138                        Some(KnownAudio { codec, payload_type, ssrc }),
139                    _ => return Err(FtlError::MissingCodecInformation)
140                }
141            } else { None }
142        })
143    }
144}
145//#endregion
146
147#[cfg(test)]
148mod tests {
149    #[test]
150    fn full_test() {
151        use crate::protocol::{FtlCommand, FtlHandshake};
152
153        // Start constructing handshake somewhere in your code.
154        let mut handshake = FtlHandshake::default();
155
156        // Example incoming command.
157        let command = FtlCommand::Attribute {
158            key: "ProtocolVersion".to_string(),
159            value: "0.9".to_string()
160        };
161
162        // Match attribute and insert it into handshake.
163        if let FtlCommand::Attribute { key, value } = command {
164            handshake.insert(key, value).unwrap();
165            // You should handle any errors here,
166            // but we know this isn't going to fail.
167        }
168
169        // Once we have the minimum amount of information,
170        // (see the note under FTL handshakes on the protocol page)
171        // we can finalise the handshake, this verifies all data is
172        // correct, such as the protocol version and ensuring if A/V
173        // streams are enabled that they have all fields present.
174        let handshake = handshake.finalise().unwrap();
175        assert_eq!(handshake.protocol_version.1, 9);
176    }
177}