Skip to main content

libbitsub_core/pgs/
display_set.rs

1//! Display Set parsing and representation.
2
3use super::{
4    ObjectDefinitionSegment, PaletteDefinitionSegment, PresentationCompositionSegment, SegmentType,
5    WindowDefinition, WindowDefinitionSegment,
6};
7use crate::utils::BigEndianReader;
8
9/// A display set contains all segments for a single subtitle update.
10#[derive(Debug, Clone)]
11pub struct DisplaySet {
12    /// Presentation timestamp in 90kHz units
13    pub pts: u32,
14    /// Decoding timestamp in 90kHz units
15    pub dts: u32,
16    /// Presentation composition segment (defines what to render)
17    pub composition: Option<PresentationCompositionSegment>,
18    /// Palette definitions in this display set
19    pub palettes: Vec<PaletteDefinitionSegment>,
20    /// Object definitions in this display set
21    pub objects: Vec<ObjectDefinitionSegment>,
22    /// Window definitions in this display set
23    pub windows: Vec<WindowDefinitionSegment>,
24}
25
26impl DisplaySet {
27    /// Create a new empty display set.
28    pub fn new() -> Self {
29        Self {
30            pts: 0,
31            dts: 0,
32            composition: None,
33            palettes: Vec::new(),
34            objects: Vec::new(),
35            windows: Vec::new(),
36        }
37    }
38
39    /// Parse a display set from binary data.
40    /// Returns the display set and the number of bytes consumed.
41    pub fn parse(data: &[u8], include_header: bool) -> Option<(Self, usize)> {
42        let mut reader = BigEndianReader::new(data);
43        let mut display_set = Self::new();
44
45        loop {
46            let pts: u32;
47            let dts: u32;
48
49            if include_header {
50                // Read PGS header
51                let magic = reader.read_u16()?;
52                if magic != 0x5047 {
53                    return None; // Invalid magic number "PG"
54                }
55
56                pts = reader.read_u32()?;
57                dts = reader.read_u32()?;
58
59                if display_set.pts == 0 {
60                    display_set.pts = pts;
61                    display_set.dts = dts;
62                }
63            } else {
64                pts = 0;
65                dts = 0;
66            }
67
68            let segment_type = reader.read_u8()?;
69            let segment_size = reader.read_u16()? as usize;
70
71            // Check if we have enough data
72            if reader.remaining() < segment_size {
73                return None;
74            }
75
76            let start_pos = reader.position();
77
78            match SegmentType::try_from(segment_type) {
79                Ok(SegmentType::PaletteDefinition) => {
80                    if let Some(palette) =
81                        PaletteDefinitionSegment::parse(&mut reader, segment_size)
82                    {
83                        display_set.palettes.push(palette);
84                    }
85                }
86                Ok(SegmentType::ObjectDefinition) => {
87                    if let Some(object) = ObjectDefinitionSegment::parse(&mut reader, segment_size)
88                    {
89                        display_set.objects.push(object);
90                    }
91                }
92                Ok(SegmentType::PresentationComposition) => {
93                    if let Some(composition) =
94                        PresentationCompositionSegment::parse(&mut reader, segment_size)
95                    {
96                        display_set.pts = pts;
97                        display_set.dts = dts;
98                        display_set.composition = Some(composition);
99                    }
100                }
101                Ok(SegmentType::WindowDefinition) => {
102                    if let Some(window) = WindowDefinitionSegment::parse(&mut reader, segment_size)
103                    {
104                        display_set.windows.push(window);
105                    }
106                }
107                Ok(SegmentType::End) => {
108                    // End of display set
109                    break;
110                }
111                Err(_) => {
112                    // Unknown segment type - skip
113                    reader.skip(segment_size);
114                }
115            }
116
117            // Ensure we consumed the expected amount
118            let consumed = reader.position() - start_pos;
119            if consumed < segment_size {
120                reader.skip(segment_size - consumed);
121            }
122        }
123
124        Some((display_set, reader.position()))
125    }
126
127    /// Get the presentation timestamp in milliseconds.
128    #[inline]
129    pub fn pts_ms(&self) -> u32 {
130        self.pts / 90
131    }
132
133    /// Find a window definition by ID.
134    pub fn find_window(&self, id: u8) -> Option<&WindowDefinition> {
135        for wds in &self.windows {
136            for window in &wds.windows {
137                if window.id == id {
138                    return Some(window);
139                }
140            }
141        }
142        None
143    }
144
145    /// Find a palette by ID.
146    pub fn find_palette(&self, id: u8) -> Option<&PaletteDefinitionSegment> {
147        self.palettes.iter().find(|p| p.id == id)
148    }
149}
150
151impl Default for DisplaySet {
152    fn default() -> Self {
153        Self::new()
154    }
155}