openigtlink_rust/protocol/types/
videometa.rs

1//! VIDEOMETA (Video Metadata) message type implementation
2//!
3//! The VIDEOMETA message is used to transfer video stream metadata such as
4//! codec parameters, framerate, and bitrate information.
5
6use crate::error::{IgtlError, Result};
7use crate::protocol::message::Message;
8use bytes::{Buf, BufMut};
9
10use super::video::CodecType;
11
12/// VIDEOMETA message for video stream metadata
13///
14/// # OpenIGTLink Specification
15/// - Message type: "VIDEOMETA"
16/// - Format: CODEC (uint8) + WIDTH (uint16) + HEIGHT (uint16) + FRAMERATE (uint8) + BITRATE (uint32) + Reserved (uint16)
17/// - Size: 12 bytes
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct VideoMetaMessage {
20    /// Video codec type
21    pub codec: CodecType,
22    /// Frame width in pixels
23    pub width: u16,
24    /// Frame height in pixels
25    pub height: u16,
26    /// Frames per second
27    pub framerate: u8,
28    /// Bitrate in kbps
29    pub bitrate: u32,
30}
31
32impl VideoMetaMessage {
33    /// Create a new VIDEOMETA message
34    pub fn new(codec: CodecType, width: u16, height: u16, framerate: u8, bitrate: u32) -> Self {
35        VideoMetaMessage {
36            codec,
37            width,
38            height,
39            framerate,
40            bitrate,
41        }
42    }
43
44    /// Create with common HD 1080p settings
45    pub fn hd1080(codec: CodecType, framerate: u8, bitrate: u32) -> Self {
46        VideoMetaMessage {
47            codec,
48            width: 1920,
49            height: 1080,
50            framerate,
51            bitrate,
52        }
53    }
54
55    /// Create with common HD 720p settings
56    pub fn hd720(codec: CodecType, framerate: u8, bitrate: u32) -> Self {
57        VideoMetaMessage {
58            codec,
59            width: 1280,
60            height: 720,
61            framerate,
62            bitrate,
63        }
64    }
65
66    /// Create with SD settings
67    pub fn sd(codec: CodecType, framerate: u8, bitrate: u32) -> Self {
68        VideoMetaMessage {
69            codec,
70            width: 640,
71            height: 480,
72            framerate,
73            bitrate,
74        }
75    }
76
77    /// Get total pixels per frame
78    pub fn pixels_per_frame(&self) -> u32 {
79        self.width as u32 * self.height as u32
80    }
81
82    /// Get estimated bandwidth in bytes per second
83    pub fn bandwidth_bps(&self) -> u32 {
84        self.bitrate * 1000 / 8 // Convert kbps to bytes/sec
85    }
86}
87
88impl Message for VideoMetaMessage {
89    fn message_type() -> &'static str {
90        "VIDEOMETA"
91    }
92
93    fn encode_content(&self) -> Result<Vec<u8>> {
94        let mut buf = Vec::with_capacity(12);
95
96        // Encode CODEC (uint8)
97        buf.put_u8(self.codec as u8);
98
99        // Encode WIDTH (uint16)
100        buf.put_u16(self.width);
101
102        // Encode HEIGHT (uint16)
103        buf.put_u16(self.height);
104
105        // Encode FRAMERATE (uint8)
106        buf.put_u8(self.framerate);
107
108        // Encode BITRATE (uint32)
109        buf.put_u32(self.bitrate);
110
111        // Encode Reserved (uint16)
112        buf.put_u16(0);
113
114        Ok(buf)
115    }
116
117    fn decode_content(mut data: &[u8]) -> Result<Self> {
118        if data.len() != 12 {
119            return Err(IgtlError::InvalidSize {
120                expected: 12,
121                actual: data.len(),
122            });
123        }
124
125        // Decode CODEC (uint8)
126        let codec = CodecType::from_u8(data.get_u8())?;
127
128        // Decode WIDTH (uint16)
129        let width = data.get_u16();
130
131        // Decode HEIGHT (uint16)
132        let height = data.get_u16();
133
134        // Decode FRAMERATE (uint8)
135        let framerate = data.get_u8();
136
137        // Decode BITRATE (uint32)
138        let bitrate = data.get_u32();
139
140        // Decode Reserved (uint16)
141        let _reserved = data.get_u16();
142
143        Ok(VideoMetaMessage {
144            codec,
145            width,
146            height,
147            framerate,
148            bitrate,
149        })
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn test_message_type() {
159        assert_eq!(VideoMetaMessage::message_type(), "VIDEOMETA");
160    }
161
162    #[test]
163    fn test_new() {
164        let meta = VideoMetaMessage::new(CodecType::H264, 1920, 1080, 30, 5000);
165
166        assert_eq!(meta.codec, CodecType::H264);
167        assert_eq!(meta.width, 1920);
168        assert_eq!(meta.height, 1080);
169        assert_eq!(meta.framerate, 30);
170        assert_eq!(meta.bitrate, 5000);
171    }
172
173    #[test]
174    fn test_hd1080() {
175        let meta = VideoMetaMessage::hd1080(CodecType::H264, 60, 10000);
176
177        assert_eq!(meta.width, 1920);
178        assert_eq!(meta.height, 1080);
179        assert_eq!(meta.framerate, 60);
180    }
181
182    #[test]
183    fn test_hd720() {
184        let meta = VideoMetaMessage::hd720(CodecType::VP9, 30, 3000);
185
186        assert_eq!(meta.width, 1280);
187        assert_eq!(meta.height, 720);
188    }
189
190    #[test]
191    fn test_sd() {
192        let meta = VideoMetaMessage::sd(CodecType::MJPEG, 25, 1000);
193
194        assert_eq!(meta.width, 640);
195        assert_eq!(meta.height, 480);
196    }
197
198    #[test]
199    fn test_pixels_per_frame() {
200        let meta = VideoMetaMessage::new(CodecType::H264, 100, 100, 30, 1000);
201        assert_eq!(meta.pixels_per_frame(), 10000);
202    }
203
204    #[test]
205    fn test_bandwidth_bps() {
206        let meta = VideoMetaMessage::new(CodecType::H264, 1920, 1080, 30, 8000);
207        // 8000 kbps = 8000 * 1000 / 8 = 1000000 bytes/sec
208        assert_eq!(meta.bandwidth_bps(), 1000000);
209    }
210
211    #[test]
212    fn test_encode() {
213        let meta = VideoMetaMessage::new(CodecType::H264, 1920, 1080, 30, 5000);
214        let encoded = meta.encode_content().unwrap();
215
216        assert_eq!(encoded.len(), 12);
217        assert_eq!(encoded[0], CodecType::H264 as u8);
218    }
219
220    #[test]
221    fn test_roundtrip() {
222        let original = VideoMetaMessage::new(CodecType::H264, 1920, 1080, 30, 5000);
223
224        let encoded = original.encode_content().unwrap();
225        let decoded = VideoMetaMessage::decode_content(&encoded).unwrap();
226
227        assert_eq!(decoded.codec, original.codec);
228        assert_eq!(decoded.width, original.width);
229        assert_eq!(decoded.height, original.height);
230        assert_eq!(decoded.framerate, original.framerate);
231        assert_eq!(decoded.bitrate, original.bitrate);
232    }
233
234    #[test]
235    fn test_roundtrip_vp9() {
236        let original = VideoMetaMessage::hd720(CodecType::VP9, 60, 4000);
237
238        let encoded = original.encode_content().unwrap();
239        let decoded = VideoMetaMessage::decode_content(&encoded).unwrap();
240
241        assert_eq!(decoded.codec, CodecType::VP9);
242        assert_eq!(decoded.width, 1280);
243        assert_eq!(decoded.height, 720);
244        assert_eq!(decoded.framerate, 60);
245    }
246
247    #[test]
248    fn test_decode_invalid_size() {
249        let data = vec![0u8; 11]; // One byte short
250        let result = VideoMetaMessage::decode_content(&data);
251        assert!(result.is_err());
252    }
253
254    #[test]
255    fn test_decode_too_long() {
256        let data = vec![0u8; 13]; // One byte too long
257        let result = VideoMetaMessage::decode_content(&data);
258        assert!(result.is_err());
259    }
260
261    #[test]
262    fn test_decode_invalid_codec() {
263        let mut data = vec![0u8; 12];
264        data[0] = 99; // Invalid codec
265        let result = VideoMetaMessage::decode_content(&data);
266        assert!(result.is_err());
267    }
268}