Skip to main content

dvb_subtitle/segments/
display_definition.rs

1//! Display Definition Segment — ETSI EN 300 743 §7.2.1, Table 8 (segment_type 0x14).
2//!
3//! Defines the display resolution and optional display window for a subtitle service.
4
5use crate::error::{Error, Result};
6use broadcast_common::{Parse, Serialize};
7
8/// The display_definition_segment segment_type as listed in Table 7.
9pub const SEGMENT_TYPE: u8 = 0x14;
10/// Segment header is sync_byte(1) + segment_type(1) + page_id(2) + segment_length(2) = 6 bytes.
11pub const HEADER_LEN: usize = 6;
12/// Fixed segment body (after header): dds_version_number(4b)+display_window_flag(1b)+reserved(3b) + display_width(2) + display_height(2) = 5 bytes.
13pub const FIXED_BODY_LEN: usize = 5;
14/// Optional display window fields: 4 × u16 = 8 bytes.
15pub const WINDOW_LEN: usize = 8;
16
17/// Display Definition Segment (DDS).
18#[derive(Debug, Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize))]
20pub struct DisplayDefinitionSegment {
21    /// The page_id (from generic segment header).
22    pub page_id: u16,
23    /// Version of this DDS (modulo 16).
24    pub dds_version_number: u8,
25    /// Whether a window is defined (`true`) or the display set fills the full resolution (`false`).
26    pub display_window_flag: bool,
27    /// Reserved bits in body byte 0 (bits `[2:0]`).
28    pub reserved: u8,
29    /// Maximum horizontal width in pixels minus 1 (0..4095).
30    pub display_width: u16,
31    /// Maximum vertical height in lines minus 1 (0..4095).
32    pub display_height: u16,
33    /// Left-most pixel of the display window (present iff `display_window_flag`).
34    pub display_window_horizontal_position_minimum: Option<u16>,
35    /// Right-most pixel of the display window (present iff `display_window_flag`).
36    pub display_window_horizontal_position_maximum: Option<u16>,
37    /// Upper-most line of the display window (present iff `display_window_flag`).
38    pub display_window_vertical_position_minimum: Option<u16>,
39    /// Bottom line of the display window (present iff `display_window_flag`).
40    pub display_window_vertical_position_maximum: Option<u16>,
41}
42
43impl<'a> Parse<'a> for DisplayDefinitionSegment {
44    type Error = Error;
45
46    fn parse(bytes: &'a [u8]) -> Result<Self> {
47        let min = HEADER_LEN + FIXED_BODY_LEN;
48        if bytes.len() < min {
49            return Err(Error::BufferTooShort {
50                need: min,
51                have: bytes.len(),
52                what: "display_definition_segment",
53            });
54        }
55        if bytes[1] != SEGMENT_TYPE {
56            return Err(Error::UnknownSegmentType(bytes[1]));
57        }
58        let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
59        let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]) as usize;
60        let total = HEADER_LEN + segment_length;
61        if bytes.len() < total {
62            return Err(Error::BufferTooShort {
63                need: total,
64                have: bytes.len(),
65                what: "display_definition_segment data",
66            });
67        }
68        let body = &bytes[HEADER_LEN..HEADER_LEN + segment_length];
69        if body.len() < FIXED_BODY_LEN {
70            return Err(Error::BufferTooShort {
71                need: FIXED_BODY_LEN,
72                have: body.len(),
73                what: "display_definition_segment body",
74            });
75        }
76        let b0 = body[0];
77        let dds_version_number = b0 >> 4;
78        let display_window_flag = (b0 & 0x08) != 0;
79        let reserved = b0 & 0x07;
80        let display_width = u16::from_be_bytes([body[1], body[2]]);
81        let display_height = u16::from_be_bytes([body[3], body[4]]);
82
83        if display_window_flag {
84            if body.len() < FIXED_BODY_LEN + WINDOW_LEN {
85                return Err(Error::BufferTooShort {
86                    need: FIXED_BODY_LEN + WINDOW_LEN,
87                    have: body.len(),
88                    what: "display_definition_segment window",
89                });
90            }
91            let dw = &body[FIXED_BODY_LEN..FIXED_BODY_LEN + WINDOW_LEN];
92            Ok(DisplayDefinitionSegment {
93                page_id,
94                dds_version_number,
95                display_window_flag,
96                reserved,
97                display_width,
98                display_height,
99                display_window_horizontal_position_minimum: Some(u16::from_be_bytes([
100                    dw[0], dw[1],
101                ])),
102                display_window_horizontal_position_maximum: Some(u16::from_be_bytes([
103                    dw[2], dw[3],
104                ])),
105                display_window_vertical_position_minimum: Some(u16::from_be_bytes([dw[4], dw[5]])),
106                display_window_vertical_position_maximum: Some(u16::from_be_bytes([dw[6], dw[7]])),
107            })
108        } else {
109            Ok(DisplayDefinitionSegment {
110                page_id,
111                dds_version_number,
112                display_window_flag,
113                reserved,
114                display_width,
115                display_height,
116                display_window_horizontal_position_minimum: None,
117                display_window_horizontal_position_maximum: None,
118                display_window_vertical_position_minimum: None,
119                display_window_vertical_position_maximum: None,
120            })
121        }
122    }
123}
124
125impl Serialize for DisplayDefinitionSegment {
126    type Error = Error;
127
128    fn serialized_len(&self) -> usize {
129        HEADER_LEN
130            + FIXED_BODY_LEN
131            + if self.display_window_flag {
132                WINDOW_LEN
133            } else {
134                0
135            }
136    }
137
138    fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
139        let len = self.serialized_len();
140        if buf.len() < len {
141            return Err(Error::BufferTooShort {
142                need: len,
143                have: buf.len(),
144                what: "display_definition_segment serialize",
145            });
146        }
147        // Generic header
148        buf[0] = 0x0F; // sync_byte
149        buf[1] = SEGMENT_TYPE;
150        buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
151        let seg_len = (len - HEADER_LEN) as u16;
152        buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
153
154        // Fixed body byte 0: version(4b) | window_flag(1b) | reserved(3b)
155        buf[6] = (self.dds_version_number << 4)
156            | (u8::from(self.display_window_flag) << 3)
157            | (self.reserved & 0x07);
158        // Fixed body bytes 1-4: display_width, display_height
159        buf[7..9].copy_from_slice(&self.display_width.to_be_bytes());
160        buf[9..11].copy_from_slice(&self.display_height.to_be_bytes());
161
162        // Optional window
163        if self.display_window_flag {
164            let wmin = self.display_window_horizontal_position_minimum.unwrap_or(0);
165            let wmax = self.display_window_horizontal_position_maximum.unwrap_or(0);
166            let vmin = self.display_window_vertical_position_minimum.unwrap_or(0);
167            let vmax = self.display_window_vertical_position_maximum.unwrap_or(0);
168            buf[11..13].copy_from_slice(&wmin.to_be_bytes());
169            buf[13..15].copy_from_slice(&wmax.to_be_bytes());
170            buf[15..17].copy_from_slice(&vmin.to_be_bytes());
171            buf[17..19].copy_from_slice(&vmax.to_be_bytes());
172        }
173        Ok(len)
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180    use broadcast_common::{Parse, Serialize};
181
182    #[test]
183    fn round_trip_no_window() {
184        let bytes = [
185            0x0F, 0x14, 0x00, 0x01, 0x00, 0x05, 0x30, 0x02, 0xCF, 0x01, 0x1F,
186        ];
187        let seg = DisplayDefinitionSegment::parse(&bytes).unwrap();
188        assert_eq!(seg.dds_version_number, 3);
189        assert!(!seg.display_window_flag);
190        assert_eq!(seg.display_width, 719);
191        assert_eq!(seg.display_height, 287);
192        let out = seg.to_bytes();
193        assert_eq!(out, bytes);
194
195        // Biting test: mutation changes output
196        let mut seg2 = seg.clone();
197        seg2.display_width = 100;
198        let out2 = seg2.to_bytes();
199        assert_ne!(out2, bytes);
200        let reparse = DisplayDefinitionSegment::parse(&out2).unwrap();
201        assert_eq!(reparse.display_width, 100);
202    }
203
204    #[test]
205    fn round_trip_with_window() {
206        let bytes = [
207            0x0F, 0x14, 0x00, 0x01, 0x00, 0x0D, 0x48, 0x02, 0xCF, 0x01, 0x1F, 0x00, 0x32, 0x02,
208            0x9E, 0x00, 0x14, 0x01, 0x0C,
209        ];
210        let seg = DisplayDefinitionSegment::parse(&bytes).unwrap();
211        assert_eq!(seg.dds_version_number, 4);
212        assert!(seg.display_window_flag);
213        assert_eq!(seg.display_window_horizontal_position_minimum, Some(50));
214        assert_eq!(seg.display_window_horizontal_position_maximum, Some(670));
215        let out = seg.to_bytes();
216        assert_eq!(out, bytes);
217
218        // Biting test: clearing the flag drops window
219        let mut seg2 = seg.clone();
220        seg2.display_window_flag = false;
221        let out2 = seg2.to_bytes();
222        assert_ne!(out2.len(), out.len());
223        let reparse = DisplayDefinitionSegment::parse(&out2).unwrap();
224        assert!(!reparse.display_window_flag);
225    }
226}