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