1use anyhow::Result;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4pub enum HeaderTypeFlag {
5 Continuation,
6 Bos,
7 Eos,
8}
9
10#[derive(Clone, Copy, PartialEq, Eq, Hash)]
11pub struct HeaderType(u8);
12
13impl HeaderType {
14 pub fn has_flag(&self, flag: HeaderTypeFlag) -> bool {
15 let flag = match flag {
16 HeaderTypeFlag::Continuation => 0x01,
17 HeaderTypeFlag::Bos => 0x02,
18 HeaderTypeFlag::Eos => 0x04,
19 };
20 (flag & self.0) != 0
21 }
22}
23
24impl std::fmt::Debug for HeaderType {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 if self.has_flag(HeaderTypeFlag::Continuation) {
27 write!(f, "Cont")?
28 }
29 if self.has_flag(HeaderTypeFlag::Bos) {
30 write!(f, "Bos")?
31 }
32 if self.has_flag(HeaderTypeFlag::Eos) {
33 write!(f, "Eos")?
34 }
35 if self.0 == 0 {
36 write!(f, "None")?
37 }
38 Ok(())
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct Header {
46 pub header_type: HeaderType,
47 pub granule_position: u64,
48 pub bitstream_serial_number: u32,
49 pub page_sequence_number: u32,
50 pub segment_table: Vec<u8>,
51}
52
53impl Header {
54 pub fn from_reader<R: std::io::Read>(rdr: &mut R) -> Result<Option<Self>> {
55 use byteorder::{LittleEndian, ReadBytesExt};
56
57 let mut capture_pattern = [0u8; 4];
58 if rdr.read_exact(&mut capture_pattern).is_err() {
59 return Ok(None);
60 };
61 if capture_pattern != *b"OggS" {
62 anyhow::bail!("unexpected capture pattern {capture_pattern:?}")
63 }
64 let version = rdr.read_u8()?;
65 if version != 0 {
66 anyhow::bail!("unexpected version {version}")
67 }
68 let header_type = HeaderType(rdr.read_u8()?);
69 let granule_position = rdr.read_u64::<LittleEndian>()?;
70 let bitstream_serial_number = rdr.read_u32::<LittleEndian>()?;
71 let page_sequence_number = rdr.read_u32::<LittleEndian>()?;
72 let _checksum = rdr.read_u32::<LittleEndian>()?;
73 let segments = rdr.read_u8()?;
74 let mut segment_table = vec![0u8; segments as usize];
75 rdr.read_exact(&mut segment_table)?;
76 Ok(Some(Self {
77 header_type,
78 granule_position,
79 bitstream_serial_number,
80 page_sequence_number,
81 segment_table,
82 }))
83 }
84}
85
86pub fn all_headers<R: std::io::Read + std::io::Seek>(rdr: &mut R) -> Result<Vec<(u64, Header)>> {
87 rdr.seek(std::io::SeekFrom::Start(0))?;
88 let mut headers = vec![];
89 loop {
90 let pos = rdr.stream_position()?;
91 let header = match Header::from_reader(rdr)? {
92 None => break,
93 Some(header) => header,
94 };
95 let to_skip = header.segment_table.iter().map(|v| *v as i64).sum::<i64>();
96 headers.push((pos, header));
97 rdr.seek(std::io::SeekFrom::Current(to_skip))?;
98 }
99 Ok(headers)
100}
101
102pub struct PacketReader<R: std::io::Read> {
103 reader: R,
105 current_header: Header,
106 segment_idx: usize,
107}
108
109impl<R: std::io::Read + std::io::Seek> PacketReader<R> {
110 pub fn seek(&mut self, header_pos: u64, move_to_last_segment: bool) -> Result<u64> {
111 self.reader.seek(std::io::SeekFrom::Start(header_pos))?;
112 self.current_header = match Header::from_reader(&mut self.reader)? {
113 None => anyhow::bail!("no data left"),
114 Some(header) => header,
115 };
116 if self.current_header.header_type.has_flag(HeaderTypeFlag::Continuation) {
117 anyhow::bail!("continuations are not handled properly when seeking")
118 }
119 if move_to_last_segment {
120 let mut to_skip = 0usize;
122 let mut n_to_skip = 0usize;
123 let mut is_finished = false;
124 for &l in self.current_header.segment_table.iter().rev() {
125 if l != 255 {
126 is_finished = true
127 }
128 if is_finished {
129 to_skip += l as usize;
130 n_to_skip += 1;
131 }
132 }
133 self.segment_idx = n_to_skip;
134 self.reader.seek(std::io::SeekFrom::Current(to_skip as i64))?;
135 }
136 Ok(self.current_header.granule_position)
137 }
138
139 pub fn seek_granule_position(
140 &mut self,
141 target_granule_pos: u64,
142 move_to_last_segment: bool,
143 ) -> Result<u64> {
144 self.reader.seek(std::io::SeekFrom::Start(0))?;
145 let mut last_header_pos = 0;
146 loop {
147 let header_pos = self.reader.stream_position()?;
148 let header = match Header::from_reader(&mut self.reader)? {
149 None => break,
150 Some(header) => header,
151 };
152 if header.granule_position >= target_granule_pos && header.granule_position > 0 {
153 break;
154 }
155 last_header_pos = header_pos;
156 let to_skip = header.segment_table.iter().map(|v| *v as i64).sum::<i64>();
157 self.reader.seek(std::io::SeekFrom::Current(to_skip))?;
158 }
159 self.seek(last_header_pos, move_to_last_segment)
160 }
161}
162
163impl<R: std::io::Read> PacketReader<R> {
164 pub fn new(mut reader: R) -> Result<Self> {
165 let current_header = match Header::from_reader(&mut reader)? {
166 None => anyhow::bail!("empty file"),
167 Some(header) => header,
168 };
169 Ok(Self { reader, current_header, segment_idx: 0 })
170 }
171
172 pub fn next_packet(&mut self) -> Result<Option<Vec<u8>>> {
173 let mut packet_data = Vec::new();
174 loop {
175 if self.segment_idx < self.current_header.segment_table.len() {
176 let len = self.current_header.segment_table[self.segment_idx];
177 self.segment_idx += 1;
178 if len != 0 {
179 let mut data = vec![0u8; len as usize];
180 self.reader.read_exact(&mut data)?;
181 packet_data.push(data);
182 }
183 if len != 255 {
184 let packet_data = packet_data.concat();
185 return Ok(Some(packet_data));
186 }
187 } else {
188 match Header::from_reader(&mut self.reader)? {
189 None => {
190 if packet_data.is_empty() {
191 return Ok(None);
192 }
193 let packet_data = packet_data.concat();
194 return Ok(Some(packet_data));
195 }
196 Some(header) => {
197 self.segment_idx = 0;
198 self.current_header = header
199 }
200 }
201 }
202 }
203 }
204
205 pub fn into_inner(self) -> R {
206 self.reader
207 }
208}