Skip to main content

dvb_si/descriptors/
video_window.rs

1//! Video Window Descriptor — ISO/IEC 13818-1 §2.6.14 (tag 0x08).
2//!
3//! Describes the position and priority of a video window.
4
5use super::descriptor_body;
6use crate::error::{Error, Result};
7use dvb_common::{Parse, Serialize};
8
9/// Descriptor tag for video_window_descriptor.
10pub const TAG: u8 = 0x08;
11const HEADER_LEN: usize = 2;
12const BODY_LEN: u8 = 4;
13
14/// Video Window Descriptor.
15#[derive(Debug, Clone, PartialEq, Eq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize))]
17#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
18pub struct VideoWindowDescriptor {
19    /// Horizontal offset (14 bits).
20    pub horizontal_offset: u16,
21    /// Vertical offset (14 bits).
22    pub vertical_offset: u16,
23    /// Window priority (4 bits).
24    pub window_priority: u8,
25}
26
27impl<'a> Parse<'a> for VideoWindowDescriptor {
28    type Error = crate::error::Error;
29
30    fn parse(bytes: &'a [u8]) -> Result<Self> {
31        let body = descriptor_body(
32            bytes,
33            TAG,
34            "VideoWindowDescriptor",
35            "unexpected tag for video_window_descriptor",
36        )?;
37        if body.len() != BODY_LEN as usize {
38            return Err(Error::InvalidDescriptor {
39                tag: TAG,
40                reason: "video_window_descriptor length must equal 4",
41            });
42        }
43        let horizontal_offset = (u16::from(body[0]) << 6) | (u16::from(body[1]) >> 2);
44        let vertical_offset = ((u16::from(body[1]) & 0x03) << 12)
45            | (u16::from(body[2]) << 4)
46            | (u16::from(body[3]) >> 4);
47        let window_priority = body[3] & 0x0F;
48        Ok(Self {
49            horizontal_offset,
50            vertical_offset,
51            window_priority,
52        })
53    }
54}
55
56impl Serialize for VideoWindowDescriptor {
57    type Error = crate::error::Error;
58
59    fn serialized_len(&self) -> usize {
60        HEADER_LEN + (BODY_LEN as usize)
61    }
62
63    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
64        let len = self.serialized_len();
65        if buf.len() < len {
66            return Err(Error::OutputBufferTooSmall {
67                need: len,
68                have: buf.len(),
69            });
70        }
71        buf[0] = TAG;
72        buf[1] = BODY_LEN;
73        let ho = self.horizontal_offset;
74        buf[HEADER_LEN] = (ho >> 6) as u8;
75        buf[HEADER_LEN + 1] =
76            ((ho & 0x3F) << 2) as u8 | ((self.vertical_offset >> 12) as u8 & 0x03);
77        buf[HEADER_LEN + 2] = (self.vertical_offset >> 4) as u8;
78        buf[HEADER_LEN + 3] =
79            ((self.vertical_offset & 0x0F) << 4) as u8 | (self.window_priority & 0x0F);
80        Ok(len)
81    }
82}
83impl<'a> crate::traits::DescriptorDef<'a> for VideoWindowDescriptor {
84    const TAG: u8 = TAG;
85    const NAME: &'static str = "VIDEO_WINDOW";
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn parse() {
94        let bytes = [
95            TAG,
96            4,
97            0b0000_0100,
98            0b0000_0000, // horizontal_offset=256 (0x0100), low 2 bits = 0
99            0b0010_0000,
100            0b0000_0101, // vertical_offset=512 (0x0200), priority=5
101        ];
102        let d = VideoWindowDescriptor::parse(&bytes).unwrap();
103        assert_eq!(d.horizontal_offset, 0x0100);
104        assert_eq!(d.vertical_offset, 0x0200);
105        assert_eq!(d.window_priority, 5);
106    }
107
108    #[test]
109    fn serialize_round_trip() {
110        let d = VideoWindowDescriptor {
111            horizontal_offset: 640,
112            vertical_offset: 480,
113            window_priority: 7,
114        };
115        let mut buf = vec![0u8; d.serialized_len()];
116        d.serialize_into(&mut buf).unwrap();
117        let reparsed = VideoWindowDescriptor::parse(&buf).unwrap();
118        assert_eq!(d, reparsed);
119    }
120
121    #[test]
122    fn parse_rejects_wrong_tag() {
123        let err = VideoWindowDescriptor::parse(&[0x09, 4, 0, 0, 0, 0]).unwrap_err();
124        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x09, .. }));
125    }
126
127    #[test]
128    fn parse_rejects_wrong_length() {
129        let err = VideoWindowDescriptor::parse(&[TAG, 3, 0, 0, 0]).unwrap_err();
130        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
131    }
132
133    #[test]
134    fn serialize_rejects_small_buffer() {
135        let d = VideoWindowDescriptor {
136            horizontal_offset: 0,
137            vertical_offset: 0,
138            window_priority: 0,
139        };
140        let mut tiny = vec![0u8; 3];
141        let err = d.serialize_into(&mut tiny).unwrap_err();
142        assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
143    }
144}