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