ogg_table/
ogg.rs

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// https://en.wikipedia.org/wiki/Ogg#Page_structure
43// https://xiph.org/ogg/doc/framing.html
44#[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    // The reader is always positioned at the beginning of the data for `segment_idx`.
104    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            // Skip to the last unfinished packet.
121            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}