symphonia-format-ogg-0.5.2/.cargo_vcs_info.json0000644000000001620000000000100151060ustar { "git": { "sha1": "412f44daab39920beeb81d78b0e4271b263d33e9" }, "path_in_vcs": "symphonia-format-ogg" }symphonia-format-ogg-0.5.2/Cargo.toml0000644000000022420000000000100131050ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.53" name = "symphonia-format-ogg" version = "0.5.2" authors = ["Philip Deljanov "] description = "Pure Rust OGG demuxer (a part of project Symphonia)." homepage = "https://github.com/pdeljanov/Symphonia" readme = "README.md" keywords = [ "audio", "media", "demuxer", "ogg", ] categories = [ "multimedia", "multimedia::audio", "multimedia::encoding", ] license = "MPL-2.0" repository = "https://github.com/pdeljanov/Symphonia" [dependencies.log] version = "0.4" [dependencies.symphonia-core] version = "0.5.2" [dependencies.symphonia-metadata] version = "0.5.2" [dependencies.symphonia-utils-xiph] version = "0.5.2" symphonia-format-ogg-0.5.2/Cargo.toml.orig000064400000000000000000000013441046102023000165700ustar 00000000000000[package] name = "symphonia-format-ogg" version = "0.5.2" description = "Pure Rust OGG demuxer (a part of project Symphonia)." homepage = "https://github.com/pdeljanov/Symphonia" repository = "https://github.com/pdeljanov/Symphonia" authors = ["Philip Deljanov "] license = "MPL-2.0" readme = "README.md" categories = ["multimedia", "multimedia::audio", "multimedia::encoding"] keywords = ["audio", "media", "demuxer", "ogg"] edition = "2018" rust-version = "1.53" [dependencies] log = "0.4" symphonia-core = { version = "0.5.2", path = "../symphonia-core" } symphonia-metadata = { version = "0.5.2", path = "../symphonia-metadata" } symphonia-utils-xiph = { version = "0.5.2", path = "../symphonia-utils-xiph" }symphonia-format-ogg-0.5.2/README.md000064400000000000000000000013601046102023000151560ustar 00000000000000# Symphonia OGG Demuxer [![Docs](https://docs.rs/symphonia-format-ogg/badge.svg)](https://docs.rs/symphonia-format-ogg) OGG demuxer for Project Symphonia. **Note:** This crate is part of Symphonia. Please use the [`symphonia`](https://crates.io/crates/symphonia) crate instead of this one directly. ## License Symphonia is provided under the MPL v2.0 license. Please refer to the LICENSE file for more details. ## Contributing Symphonia is an open-source project and contributions are very welcome! If you would like to make a large contribution, please raise an issue ahead of time to make sure your efforts fit into the project goals, and that no duplication of efforts occurs. All contributors will be credited within the CONTRIBUTORS file. symphonia-format-ogg-0.5.2/src/common.rs000064400000000000000000000006241046102023000163260ustar 00000000000000// Symphonia // Copyright (c) 2019-2022 The Project Symphonia Developers. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use symphonia_core::meta::MetadataRevision; /// Side data variants. pub enum SideData { Metadata(MetadataRevision), } symphonia-format-ogg-0.5.2/src/demuxer.rs000064400000000000000000000422521046102023000165120ustar 00000000000000// Symphonia // Copyright (c) 2019-2022 The Project Symphonia Developers. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use std::collections::BTreeMap; use std::io::{Seek, SeekFrom}; use symphonia_core::errors::{reset_error, seek_error, unsupported_error}; use symphonia_core::errors::{Error, Result, SeekErrorKind}; use symphonia_core::formats::prelude::*; use symphonia_core::io::{MediaSource, MediaSourceStream, ReadBytes, SeekBuffered}; use symphonia_core::meta::{Metadata, MetadataLog}; use symphonia_core::probe::{Descriptor, Instantiate, QueryDescriptor}; use symphonia_core::support_format; use log::{debug, info, warn}; use super::common::SideData; use super::logical::LogicalStream; use super::mappings; use super::page::*; use super::physical; /// OGG demultiplexer. /// /// `OggReader` implements a demuxer for Xiph's OGG container format. pub struct OggReader { reader: MediaSourceStream, tracks: Vec, cues: Vec, metadata: MetadataLog, options: FormatOptions, /// The page reader. pages: PageReader, /// `LogicalStream` for each serial. streams: BTreeMap, /// The position of the first byte of the current physical stream. phys_byte_range_start: u64, /// The position of the first byte of the next physical stream, if available. phys_byte_range_end: Option, } impl OggReader { fn read_page(&mut self) -> Result<()> { // Try reading pages until a page is successfully read, or an IO error. loop { match self.pages.try_next_page(&mut self.reader) { Ok(_) => break, Err(Error::IoError(e)) => return Err(Error::from(e)), Err(e) => { warn!("{}", e); } } } let page = self.pages.page(); // If the page is marked as a first page, then try to start a new physical stream. if page.header.is_first_page { self.start_new_physical_stream()?; return reset_error(); } if let Some(stream) = self.streams.get_mut(&page.header.serial) { // TODO: Process side data. let _side_data = stream.read_page(&page)?; } else { // If there is no associated logical stream with this page, then this is a // completely random page within the physical stream. Discard it. } Ok(()) } fn peek_logical_packet(&self) -> Option<&Packet> { let page = self.pages.page(); if let Some(stream) = self.streams.get(&page.header.serial) { stream.peek_packet() } else { None } } fn discard_logical_packet(&mut self) { let page = self.pages.page(); // Consume a packet from the logical stream belonging to the current page. if let Some(stream) = self.streams.get_mut(&page.header.serial) { stream.consume_packet(); } } fn next_logical_packet(&mut self) -> Result { loop { let page = self.pages.page(); // Read the next packet. Packets are only ever buffered in the logical stream of the // current page. if let Some(stream) = self.streams.get_mut(&page.header.serial) { if let Some(packet) = stream.next_packet() { return Ok(packet); } } self.read_page()?; } } fn do_seek(&mut self, serial: u32, required_ts: u64) -> Result { // If the reader is seekable, then use the bisection method to coarsely seek to the nearest // page that ends before the required timestamp. if self.reader.is_seekable() { let stream = self.streams.get_mut(&serial).unwrap(); // The end of the physical stream. let physical_end = self.phys_byte_range_end.unwrap(); let mut start_byte_pos = self.phys_byte_range_start; let mut end_byte_pos = physical_end; // Bisection method. loop { // Find the middle of the upper and lower byte search range. let mid_byte_pos = (start_byte_pos + end_byte_pos) / 2; // Seek to the middle of the byte range. self.reader.seek(SeekFrom::Start(mid_byte_pos))?; // Read the next page. match self.pages.next_page_for_serial(&mut self.reader, serial) { Ok(_) => (), _ => return seek_error(SeekErrorKind::OutOfRange), } // Probe the page to get the start and end timestamp. let (start_ts, end_ts) = stream.inspect_page(&self.pages.page()); debug!( "seek: bisect step: page={{ start={}, end={} }} byte_range=[{}..{}], mid={}", start_ts, end_ts, start_byte_pos, end_byte_pos, mid_byte_pos, ); if required_ts < start_ts { // The required timestamp is less-than the timestamp of the first sample in // page1. Update the upper bound and bisect again. end_byte_pos = mid_byte_pos; } else if required_ts > end_ts { // The required timestamp is greater-than the timestamp of the final sample in // the in page1. Update the lower bound and bisect again. start_byte_pos = mid_byte_pos; } else { // The sample with the required timestamp is contained in page1. Return the // byte position for page0, and the timestamp of the first sample in page1, so // that when packets from page1 are read, those packets will have a non-zero // base timestamp. break; } // Prevent infinite iteration and too many seeks when the search range is less // than 2x the maximum page size. if end_byte_pos - start_byte_pos <= 2 * OGG_PAGE_MAX_SIZE as u64 { self.reader.seek(SeekFrom::Start(start_byte_pos))?; match self.pages.next_page_for_serial(&mut self.reader, serial) { Ok(_) => (), _ => return seek_error(SeekErrorKind::OutOfRange), } break; } } // Reset all logical bitstreams since the physical stream will be reading from a new // location now. for (&s, stream) in self.streams.iter_mut() { stream.reset(); // Read in the current page since it contains our timestamp. if s == serial { stream.read_page(&self.pages.page())?; } } } // Consume packets until reaching the desired timestamp. let actual_ts = loop { match self.peek_logical_packet() { Some(packet) => { if packet.track_id() == serial && packet.ts + packet.dur >= required_ts { break packet.ts; } self.discard_logical_packet(); } _ => self.read_page()?, } }; debug!( "seeked track={:#x} to packet_ts={} (delta={})", serial, actual_ts, actual_ts as i64 - required_ts as i64 ); Ok(SeekedTo { track_id: serial, actual_ts, required_ts }) } fn start_new_physical_stream(&mut self) -> Result<()> { // The new mapper set. let mut streams = BTreeMap::::new(); // The start of page position. let mut byte_range_start = self.reader.pos(); // Pre-condition: This function is only called when the current page is marked as a // first page. assert!(self.pages.header().is_first_page); info!("starting new physical stream"); // The first page of each logical stream, marked with the first page flag, must contain the // identification packet for the encapsulated codec bitstream. The first page for each // logical stream from the current logical stream group must appear before any other pages. // That is to say, if there are N logical streams, then the first N pages must contain the // identification packets for each respective logical stream. loop { let header = self.pages.header(); if !header.is_first_page { break; } byte_range_start = self.reader.pos(); // There should only be a single packet, the identification packet, in the first page. if let Some(pkt) = self.pages.first_packet() { // If a stream mapper has been detected, create a logical stream with it. if let Some(mapper) = mappings::detect(pkt)? { info!( "selected {} mapper for stream with serial={:#x}", mapper.name(), header.serial ); let stream = LogicalStream::new(mapper, self.options.enable_gapless); streams.insert(header.serial, stream); } } // Read the next page. self.pages.try_next_page(&mut self.reader)?; } // Each logical stream may contain additional header packets after the identification packet // that contains format-relevant information such as setup and metadata. These packets, // for all logical streams, should be grouped together after the identification packets. // Reading pages consumes these headers and returns any relevant data as side data. Read // pages until all headers are consumed and the first bitstream packets are buffered. loop { let page = self.pages.page(); if let Some(stream) = streams.get_mut(&page.header.serial) { let side_data = stream.read_page(&page)?; // Consume each piece of side data. for data in side_data { match data { SideData::Metadata(rev) => self.metadata.push(rev), } } if stream.has_packets() { break; } } // The current page has been consumed and we're committed to reading a new one. Record // the end of the current page. byte_range_start = self.reader.pos(); self.pages.try_next_page(&mut self.reader)?; } // Probe the logical streams for their start and end pages. physical::probe_stream_start(&mut self.reader, &mut self.pages, &mut streams); let mut byte_range_end = Default::default(); // If the media source stream is seekable, then try to determine the duration of each // logical stream, and the length in bytes of the physical stream. if self.reader.is_seekable() { if let Some(total_len) = self.reader.byte_len() { byte_range_end = physical::probe_stream_end( &mut self.reader, &mut self.pages, &mut streams, byte_range_start, total_len, )?; } } // At this point it can safely be assumed that a new physical stream is starting. // First, clear the existing track listing. self.tracks.clear(); // Second, add a track for all streams. for (&serial, stream) in streams.iter() { // Warn if the track is not ready. This should not happen if the physical stream was // muxed properly. if !stream.is_ready() { warn!("track for serial={:#x} may not be ready", serial); } self.tracks.push(Track::new(serial, stream.codec_params().clone())); } // Third, replace all logical streams with the new set. self.streams = streams; // Last, store the lower and upper byte boundaries of the physical stream for seeking. self.phys_byte_range_start = byte_range_start; self.phys_byte_range_end = byte_range_end; Ok(()) } } impl QueryDescriptor for OggReader { fn query() -> &'static [Descriptor] { &[support_format!( "ogg", "OGG", &["ogg", "ogv", "oga", "ogx", "ogm", "spx", "opus"], &["video/ogg", "audio/ogg", "application/ogg"], &[b"OggS"] )] } fn score(_context: &[u8]) -> u8 { 255 } } impl FormatReader for OggReader { fn try_new(mut source: MediaSourceStream, options: &FormatOptions) -> Result { // A seekback buffer equal to the maximum OGG page size is required for this reader. source.ensure_seekback_buffer(OGG_PAGE_MAX_SIZE); let pages = PageReader::try_new(&mut source)?; if !pages.header().is_first_page { return unsupported_error("ogg: page is not marked as first"); } let mut ogg = OggReader { reader: source, tracks: Default::default(), cues: Default::default(), metadata: Default::default(), streams: Default::default(), options: *options, pages, phys_byte_range_start: 0, phys_byte_range_end: None, }; ogg.start_new_physical_stream()?; Ok(ogg) } fn next_packet(&mut self) -> Result { self.next_logical_packet() } fn metadata(&mut self) -> Metadata<'_> { self.metadata.metadata() } fn cues(&self) -> &[Cue] { &self.cues } fn tracks(&self) -> &[Track] { &self.tracks } fn seek(&mut self, _mode: SeekMode, to: SeekTo) -> Result { // Get the timestamp of the desired audio frame. let (required_ts, serial) = match to { // Frame timestamp given. SeekTo::TimeStamp { ts, track_id } => { // Check if the user provided an invalid track ID. if let Some(stream) = self.streams.get(&track_id) { let params = stream.codec_params(); // Timestamp lower-bound out-of-range. if ts < params.start_ts { return seek_error(SeekErrorKind::OutOfRange); } // Timestamp upper-bound out-of-range. if let Some(dur) = params.n_frames { if ts > dur + params.start_ts { return seek_error(SeekErrorKind::OutOfRange); } } } else { return seek_error(SeekErrorKind::InvalidTrack); } (ts, track_id) } // Time value given, calculate frame timestamp from sample rate. SeekTo::Time { time, track_id } => { // Get the track serial. let serial = if let Some(serial) = track_id { serial } else if let Some(default_track) = self.default_track() { default_track.id } else { // No tracks. return seek_error(SeekErrorKind::Unseekable); }; // Convert the time to a timestamp. let ts = if let Some(stream) = self.streams.get(&serial) { let params = stream.codec_params(); let ts = if let Some(sample_rate) = params.sample_rate { TimeBase::new(1, sample_rate).calc_timestamp(time) } else { // No sample rate. This should never happen. return seek_error(SeekErrorKind::Unseekable); }; // Timestamp lower-bound out-of-range. if ts < params.start_ts { return seek_error(SeekErrorKind::OutOfRange); } // Timestamp upper-bound out-of-range. if let Some(dur) = params.n_frames { if ts > dur + params.start_ts { return seek_error(SeekErrorKind::OutOfRange); } } ts } else { // No mapper for track. The user provided a bad track ID. return seek_error(SeekErrorKind::InvalidTrack); }; (ts, serial) } }; debug!("seeking track={:#x} to frame_ts={}", serial, required_ts); // Do the actual seek. self.do_seek(serial, required_ts) } fn into_inner(self: Box) -> MediaSourceStream { self.reader } } symphonia-format-ogg-0.5.2/src/lib.rs000064400000000000000000000012701046102023000156020ustar 00000000000000// Symphonia // Copyright (c) 2019-2022 The Project Symphonia Developers. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. #![warn(rust_2018_idioms)] #![forbid(unsafe_code)] // The following lints are allowed in all Symphonia crates. Please see clippy.toml for their // justification. #![allow(clippy::comparison_chain)] #![allow(clippy::excessive_precision)] #![allow(clippy::identity_op)] #![allow(clippy::manual_range_contains)] mod common; mod demuxer; mod logical; mod mappings; mod page; mod physical; pub use demuxer::OggReader; symphonia-format-ogg-0.5.2/src/logical.rs000064400000000000000000000376031046102023000164570ustar 00000000000000// Symphonia // Copyright (c) 2019-2022 The Project Symphonia Developers. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use std::collections::VecDeque; use symphonia_core::codecs::CodecParameters; use symphonia_core::errors::{decode_error, Result}; use symphonia_core::formats::Packet; use super::common::SideData; use super::mappings::Mapper; use super::mappings::{MapResult, PacketParser}; use super::page::Page; use log::{debug, warn}; #[allow(dead_code)] #[derive(Copy, Clone, Debug)] struct Bound { seq: u32, ts: u64, delay: u64, } #[allow(dead_code)] #[derive(Copy, Clone)] struct PageInfo { seq: u32, absgp: u64, } #[derive(Default)] pub struct InspectState { bound: Option, parser: Option>, } pub struct LogicalStream { mapper: Box, packets: VecDeque, part_buf: Vec, part_len: usize, prev_page_info: Option, start_bound: Option, end_bound: Option, gapless: bool, } impl LogicalStream { const MAX_PACKET_LEN: usize = 8 * 1024 * 1024; pub fn new(mapper: Box, gapless: bool) -> Self { LogicalStream { mapper, packets: Default::default(), part_buf: Default::default(), part_len: 0, prev_page_info: None, start_bound: None, end_bound: None, gapless, } } /// Reset the logical stream after a page discontinuity. pub fn reset(&mut self) { self.part_len = 0; self.prev_page_info = None; self.packets.clear(); self.mapper.reset(); } /// Returns true if the stream is ready. pub fn is_ready(&self) -> bool { self.mapper.is_ready() } /// Get the `CodecParameters` for the logical stream. pub fn codec_params(&self) -> &CodecParameters { self.mapper.codec_params() } /// Reads a page. pub fn read_page(&mut self, page: &Page<'_>) -> Result> { // Side data vector. This will not allocate unless data is pushed to it (normal case). let mut side_data = Vec::new(); // If the last sequence number is available, detect non-monotonicity and discontinuities // in the stream. In these cases, clear any partial packet data. if let Some(last_ts) = &self.prev_page_info { if page.header.sequence < last_ts.seq { warn!("detected stream page non-monotonicity"); self.part_len = 0; } else if page.header.sequence - last_ts.seq > 1 { warn!( "detected stream discontinuity of {} page(s)", page.header.sequence - last_ts.seq ); self.part_len = 0; } } self.prev_page_info = Some(PageInfo { seq: page.header.sequence, absgp: page.header.absgp }); let mut iter = page.packets(); // If there is partial packet data buffered, a continuation page is expected. if !page.header.is_continuation && self.part_len > 0 { warn!("expected a continuation page"); // Clear partial packet data. self.part_len = 0; } // If there is no partial packet data buffered, a continuation page is not expected. if page.header.is_continuation && self.part_len == 0 { // If the continuation page contains packets, drop the first packet since it would // require partial packet data to be complete. Otherwise, ignore this page entirely. if page.num_packets() > 0 { warn!("unexpected continuation page, ignoring incomplete first packet"); iter.next(); } else { warn!("unexpected continuation page, ignoring page"); return Ok(side_data); } } let num_prev_packets = self.packets.len(); for buf in &mut iter { // Get a packet with data from the partial packet buffer, the page, or both. let data = self.get_packet(buf); // Perform packet mapping. If the packet contains stream data, queue it onto the packet // queue. If it contains side data, then add it to the side data list. Ignore other // types of packet data. match self.mapper.map_packet(&data) { Ok(MapResult::StreamData { dur }) => { // Create a packet. self.packets.push_back(Packet::new_from_boxed_slice( page.header.serial, 0, dur, data, )); } Ok(MapResult::SideData { data }) => side_data.push(data), Err(e) => { warn!("mapping packet failed ({}), skipping", e.to_string()) } _ => (), } } // If the page contains partial packet data, then save the partial packet data for later // as the packet will be completed on a later page. if let Some(buf) = iter.partial_packet() { self.save_partial_packet(buf)?; } // The number of packets from this page that were queued. let num_new_packets = self.packets.len() - num_prev_packets; if num_new_packets > 0 { // Get the start delay. let start_delay = self.start_bound.as_ref().map_or(0, |b| b.delay); // Assign timestamps by first calculating the timestamp of one past the last sample in // in the last packet of this page, add the start delay. let mut page_end_ts = self.mapper.absgp_to_ts(page.header.absgp).saturating_add(start_delay); // If this is the last page, then add the end delay to the timestamp. if page.header.is_last_page { let end_delay = self.end_bound.as_ref().map_or(0, |b| b.delay); page_end_ts = page_end_ts.saturating_add(end_delay); } // Then, iterate over the newly added packets in reverse order and subtract their // cumulative duration at each iteration to get the timestamp of the first sample // in each packet. let mut page_dur = 0u64; for packet in self.packets.iter_mut().rev().take(num_new_packets) { page_dur = page_dur.saturating_add(packet.dur); packet.ts = page_end_ts.saturating_sub(page_dur); } if self.gapless { for packet in self.packets.iter_mut().rev().take(num_new_packets) { symphonia_core::formats::util::trim_packet( packet, start_delay as u32, self.end_bound.as_ref().map(|b| b.ts), ); } } } Ok(side_data) } /// Returns true if the logical stream has packets buffered. pub fn has_packets(&self) -> bool { !self.packets.is_empty() } /// Examine, but do not consume, the next packet. pub fn peek_packet(&self) -> Option<&Packet> { self.packets.front() } /// Consumes and returns the next packet. pub fn next_packet(&mut self) -> Option { self.packets.pop_front() } /// Consumes the next packet. pub fn consume_packet(&mut self) { self.packets.pop_front(); } /// Examine the first page of the non-setup codec bitstream to obtain the start time and start /// delay parameters. pub fn inspect_start_page(&mut self, page: &Page<'_>) { if self.start_bound.is_some() { debug!("start page already found"); return; } let mut parser = match self.mapper.make_parser() { Some(parser) => parser, _ => { debug!("failed to make start bound packet parser"); return; } }; // Calculate the page duration. let mut page_dur = 0u64; for buf in page.packets() { page_dur = page_dur.saturating_add(parser.parse_next_packet_dur(buf)); } let page_end_ts = self.mapper.absgp_to_ts(page.header.absgp); // If the page timestamp is >= the page duration, then the stream starts at timestamp 0 or // a positive start time. let bound = if page_end_ts >= page_dur { Bound { seq: page.header.sequence, ts: page_end_ts - page_dur, delay: 0 } } else { // If the page timestamp < the page duration, then the difference is the start delay. Bound { seq: page.header.sequence, ts: 0, delay: page_dur - page_end_ts } }; // Update codec parameters. let codec_params = self.mapper.codec_params_mut(); codec_params.with_start_ts(bound.ts); if bound.delay > 0 { codec_params.with_delay(bound.delay as u32); } // Update start bound. self.start_bound = Some(bound); } /// Examines one or more of the last pages of the codec bitstream to obtain the end time and /// end delay parameters. To obtain the end delay, at a minimum, the last two pages are /// required. The state returned by each iteration of this function should be passed into the /// subsequent iteration. pub fn inspect_end_page(&mut self, mut state: InspectState, page: &Page<'_>) -> InspectState { if self.end_bound.is_some() { debug!("end page already found"); return state; } // Get and/or create the sniffer state. let parser = match &mut state.parser { Some(parser) => parser, None => { state.parser = self.mapper.make_parser(); if let Some(parser) = &mut state.parser { parser } else { debug!("failed to make end bound packet parser"); return state; } } }; let start_delay = self.start_bound.as_ref().map_or(0, |b| b.delay); // The actual page end timestamp is the absolute granule position + the start delay. let page_end_ts = self .mapper .absgp_to_ts(page.header.absgp) .saturating_add(if self.gapless { 0 } else { start_delay }); // Calculate the page duration. Note that even though only the last page uses this duration, // it is important to feed the packet parser so that the first packet of the final page // doesn't have a duration of 0 due to lapping on some codecs. let mut page_dur = 0u64; for buf in page.packets() { page_dur = page_dur.saturating_add(parser.parse_next_packet_dur(buf)); } // The end delay can only be determined if this is the last page, and the timstamp of the // second last page is known. let end_delay = if page.header.is_last_page { if let Some(last_bound) = &state.bound { // The real ending timestamp of the decoded data is the timestamp of the previous // page plus the decoded duration of this page. let actual_page_end_ts = last_bound.ts.saturating_add(page_dur); // Any samples after the stated timestamp of this page are considered delay samples. if actual_page_end_ts > page_end_ts { actual_page_end_ts - page_end_ts } else { 0 } } else { // Don't have the timestamp of the previous page so it is not possible to // calculate the end delay. 0 } } else { // Only the last page can have an end delay. 0 }; let bound = Bound { seq: page.header.sequence, ts: page_end_ts, delay: end_delay }; // If this is the last page, update the codec parameters. if page.header.is_last_page { let codec_params = self.mapper.codec_params_mut(); // Do not report the end delay if gapless is enabled. let block_end_ts = bound.ts + if self.gapless { 0 } else { bound.delay }; if block_end_ts > codec_params.start_ts { codec_params.with_n_frames(block_end_ts - codec_params.start_ts); } if bound.delay > 0 { codec_params.with_padding(bound.delay as u32); } self.end_bound = Some(bound) } // Update the state's bound. state.bound = Some(bound); state } /// Examine a page and return the start and end timestamps as a tuple. pub fn inspect_page(&mut self, page: &Page<'_>) -> (u64, u64) { // Get the start delay. let start_delay = self.start_bound.as_ref().map_or(0, |b| b.delay); // Get the cumulative duration of all packets within this page. let mut page_dur = 0u64; if let Some(mut parser) = self.mapper.make_parser() { for buf in page.packets() { page_dur = page_dur.saturating_add(parser.parse_next_packet_dur(buf)); } } // If this is the final page, get the end delay. let end_delay = if page.header.is_last_page { self.end_bound.as_ref().map_or(0, |b| b.delay) } else { 0 }; // The total delay. let delay = start_delay + end_delay; // Add the total delay to the page end timestamp. let page_end_ts = self.mapper.absgp_to_ts(page.header.absgp).saturating_add(delay); // Get the page start timestamp of the page by subtracting the cumulative packet duration. let page_start_ts = page_end_ts.saturating_sub(page_dur); if !self.gapless { // If gapless playback is disabled, then report the start and end timestamps with the // delays incorporated. (page_start_ts, page_end_ts) } else { // If gapless playback is enabled, report the start and end timestamps without the // delays. (page_start_ts.saturating_sub(delay), page_end_ts.saturating_sub(delay)) } } fn get_packet(&mut self, packet_buf: &[u8]) -> Box<[u8]> { if self.part_len == 0 { Box::from(packet_buf) } else { let mut buf = vec![0u8; self.part_len + packet_buf.len()]; // Split packet buffer into two portions: saved and new. let (vec0, vec1) = buf.split_at_mut(self.part_len); // Copy and consume the saved partial packet. vec0.copy_from_slice(&self.part_buf[..self.part_len]); self.part_len = 0; // Read the remainder of the partial packet from the page. vec1.copy_from_slice(packet_buf); buf.into_boxed_slice() } } fn save_partial_packet(&mut self, buf: &[u8]) -> Result<()> { let new_part_len = self.part_len + buf.len(); if new_part_len > self.part_buf.len() { // Do not exceed an a certain limit to prevent unbounded memory growth. if new_part_len > LogicalStream::MAX_PACKET_LEN { return decode_error("ogg: packet buffer would exceed max size"); } // New partial packet buffer size, rounded up to the nearest 8K block. let new_buf_len = (new_part_len + (8 * 1024 - 1)) & !(8 * 1024 - 1); debug!("grow packet buffer to {} bytes", new_buf_len); self.part_buf.resize(new_buf_len, Default::default()); } self.part_buf[self.part_len..new_part_len].copy_from_slice(buf); self.part_len = new_part_len; Ok(()) } } symphonia-format-ogg-0.5.2/src/mappings/flac.rs000064400000000000000000000253101046102023000175600ustar 00000000000000// Symphonia // Copyright (c) 2019-2022 The Project Symphonia Developers. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::common::SideData; use super::{MapResult, Mapper, PacketParser}; use symphonia_core::checksum::Crc8Ccitt; use symphonia_core::codecs::{CodecParameters, VerificationCheck, CODEC_TYPE_FLAC}; use symphonia_core::errors::{decode_error, Result}; use symphonia_core::io::{BufReader, MonitorStream, ReadBytes}; use symphonia_core::meta::MetadataBuilder; use symphonia_core::units::TimeBase; use symphonia_utils_xiph::flac::metadata::{read_comment_block, read_picture_block}; use symphonia_utils_xiph::flac::metadata::{MetadataBlockHeader, MetadataBlockType, StreamInfo}; use log::warn; /// The expected size of the first FLAC header packet. const OGG_FLAC_HEADER_PACKET_SIZE: usize = 51; /// The major version number of the supported OGG-FLAC mapping. const OGG_FLAC_MAPPING_MAJOR_VERSION: u8 = 1; /// The OGG-FLAC header packet signature. const OGG_FLAC_HEADER_SIGNATURE: &[u8] = b"FLAC"; /// The OGG-FLAC header packet type value. const OGG_FLAC_PACKET_TYPE: u8 = 0x7f; /// The native FLAC signature. const FLAC_SIGNATURE: &[u8] = b"fLaC"; pub fn detect(buf: &[u8]) -> Result>> { // The packet shall be exactly the expected length. if buf.len() != OGG_FLAC_HEADER_PACKET_SIZE { return Ok(None); } let mut reader = BufReader::new(buf); // The first byte indicates the packet type and must be 0x7f. if reader.read_u8()? != OGG_FLAC_PACKET_TYPE { return Ok(None); } // Next, the OGG FLAC signature, in ASCII, must be "FLAC". if reader.read_quad_bytes()? != OGG_FLAC_HEADER_SIGNATURE { return Ok(None); } // Next, a one-byte binary major version number for the mapping, only version 1 is supported. if reader.read_u8()? != OGG_FLAC_MAPPING_MAJOR_VERSION { return Ok(None); } // Next, a one-byte minor version number for the mapping. This is ignored because we support all // version 1 features. let _minor = reader.read_u8()?; // Next, a two-byte, big-endian number signifying the number of header (non-audio) packets, not // including the identification packet. This number may be 0 to signify it is unknown. let _ = reader.read_be_u16()?; // Last, the four-byte ASCII native FLAC signature "fLaC". if reader.read_quad_bytes()? != FLAC_SIGNATURE { return Ok(None); } // Following the previous OGG FLAC identification data is the stream information block as a // native FLAC metadata block. let header = MetadataBlockHeader::read(&mut reader)?; if header.block_type != MetadataBlockType::StreamInfo { return Ok(None); } // Ensure the block length is correct for a stream information block before allocating a buffer // for it. if !StreamInfo::is_valid_size(u64::from(header.block_len)) { return Ok(None); } let extra_data = reader.read_boxed_slice_exact(header.block_len as usize)?; let stream_info = StreamInfo::read(&mut BufReader::new(&extra_data))?; // Populate the codec parameters with the information read from the stream information block. let mut codec_params = CodecParameters::new(); codec_params .for_codec(CODEC_TYPE_FLAC) .with_packet_data_integrity(true) .with_extra_data(extra_data) .with_sample_rate(stream_info.sample_rate) .with_time_base(TimeBase::new(1, stream_info.sample_rate)) .with_bits_per_sample(stream_info.bits_per_sample) .with_channels(stream_info.channels); if let Some(md5) = stream_info.md5 { codec_params.with_verification_code(VerificationCheck::Md5(md5)); } if let Some(n_frames) = stream_info.n_samples { codec_params.with_n_frames(n_frames); } // Instantiate the FLAC mapper. let mapper = Box::new(FlacMapper { codec_params }); Ok(Some(mapper)) } /// Decodes a big-endian unsigned integer encoded via extended UTF8. fn utf8_decode_be_u64(src: &mut B) -> Result> { // NOTE: See the symphonia-bundle-flac crate for a detailed description of this function. let mut state = u64::from(src.read_u8()?); let mask: u8 = match state { 0x00..=0x7f => return Ok(Some(state)), 0xc0..=0xdf => 0x1f, 0xe0..=0xef => 0x0f, 0xf0..=0xf7 => 0x07, 0xf8..=0xfb => 0x03, 0xfc..=0xfd => 0x01, 0xfe => 0x00, _ => return Ok(None), }; state &= u64::from(mask); for _ in 2..mask.leading_zeros() { state = (state << 6) | u64::from(src.read_u8()? & 0x3f); } Ok(Some(state)) } #[allow(dead_code)] struct FrameHeader { ts: u64, dur: u64, } /// Try to decode a FLAC frame header from the provided buffer. fn decode_frame_header(buf: &[u8]) -> Result { // The FLAC frame header is checksummed with a CRC-8 hash. let mut reader_crc8 = MonitorStream::new(BufReader::new(buf), Crc8Ccitt::new(0)); // Read the sync word. let sync = reader_crc8.read_be_u16()?; // Within an OGG packet the frame should be synchronized. if sync & 0xfffc != 0xfff8 { return decode_error("ogg (flac): header is not synchronized"); } // Read all the standard frame description fields as one 16-bit value and extract the fields. let desc = reader_crc8.read_be_u16()?; // Reserved bit field. if desc & 0x0001 == 1 { return decode_error("ogg (flac): frame header reserved bit is not set to 1"); } // Extract the blocking strategy from the sync word. let is_fixed_block_size = sync & 0x1 == 0; let block_sequence = if is_fixed_block_size { // Fixed block size stream sequence blocks by a frame number. let frame = match utf8_decode_be_u64(&mut reader_crc8)? { Some(frame) => frame, None => return decode_error("ogg (flac): frame sequence number is not valid"), }; // The frame number should only be 31-bits. if frame > 0x7fff_ffff { return decode_error("ogg (flac): frame sequence number exceeds 31-bits"); } frame } else { // Variable block size streams sequence blocks by a sample number. let sample = match utf8_decode_be_u64(&mut reader_crc8)? { Some(sample) => sample, None => return decode_error("ogg: sample sequence number is not valid"), }; // The sample number should only be 36-bits. if sample > 0xff_ffff_ffff { return decode_error("ogg (flac): sample sequence number exceeds 36-bits"); } sample }; // The block size provides the duration in samples. let block_size_enc = u32::from((desc & 0xf000) >> 12); let block_size = match block_size_enc { 0x1 => 192, 0x2..=0x5 => 576 * (1 << (block_size_enc - 2)), 0x6 => u64::from(reader_crc8.read_u8()?) + 1, 0x7 => { let block_size = reader_crc8.read_be_u16()?; if block_size == 0xffff { return decode_error("ogg (flac): block size not allowed to be greater than 65535"); } u64::from(block_size) + 1 } 0x8..=0xf => 256 * (1 << (block_size_enc - 8)), _ => return decode_error("ogg (flac): block size set to reserved value"), }; // The sample rate is not required but should be read so checksum verification of the header // can be performed. let sample_rate_enc = u32::from((desc & 0x0f00) >> 8); match sample_rate_enc { 0xc => { reader_crc8.read_u8()?; } 0xd => { reader_crc8.read_be_u16()?; } 0xe => { reader_crc8.read_be_u16()?; } _ => (), } // End of frame header, get the computed CRC-8 checksum. let crc8_computed = reader_crc8.monitor().crc(); // Read the expected CRC-8 checksum from the frame header. let crc8_expected = reader_crc8.into_inner().read_u8()?; if crc8_expected != crc8_computed && cfg!(not(fuzzing)) { return decode_error("ogg (flac): computed frame header CRC does not match expected CRC"); } let ts = if is_fixed_block_size { block_sequence * block_size } else { block_sequence }; Ok(FrameHeader { ts, dur: block_size }) } struct FlacPacketParser {} impl PacketParser for FlacPacketParser { fn parse_next_packet_dur(&mut self, packet: &[u8]) -> u64 { match decode_frame_header(packet).ok() { Some(header) => header.dur, _ => 0, } } } struct FlacMapper { codec_params: CodecParameters, } impl Mapper for FlacMapper { fn name(&self) -> &'static str { "flac" } fn codec_params(&self) -> &CodecParameters { &self.codec_params } fn codec_params_mut(&mut self) -> &mut CodecParameters { &mut self.codec_params } fn make_parser(&self) -> Option> { Some(Box::new(FlacPacketParser {})) } fn reset(&mut self) { // Nothing to do. } fn map_packet(&mut self, packet: &[u8]) -> Result { let packet_type = BufReader::new(packet).read_u8()?; // A packet type of 0xff is an audio packet. if packet_type == 0xff { // Parse the packet duration. let dur = match decode_frame_header(packet).ok() { Some(header) => header.dur, _ => 0, }; Ok(MapResult::StreamData { dur }) } else if packet_type == 0x00 || packet_type == 0x80 { // Packet types 0x00 and 0x80 are invalid. warn!("ogg (flac): flac packet type {} unexpected", packet_type); Ok(MapResult::Unknown) } else { let mut reader = BufReader::new(packet); // Packet types in the range 0x01 thru 0x7f, and 0x81 thru 0xfe are metadata blocks. let header = MetadataBlockHeader::read(&mut reader)?; match header.block_type { MetadataBlockType::VorbisComment => { let mut builder = MetadataBuilder::new(); read_comment_block(&mut reader, &mut builder)?; Ok(MapResult::SideData { data: SideData::Metadata(builder.metadata()) }) } MetadataBlockType::Picture => { let mut builder = MetadataBuilder::new(); read_picture_block(&mut reader, &mut builder)?; Ok(MapResult::SideData { data: SideData::Metadata(builder.metadata()) }) } _ => Ok(MapResult::Unknown), } } } } symphonia-format-ogg-0.5.2/src/mappings/mod.rs000064400000000000000000000065731046102023000174440ustar 00000000000000// Symphonia // Copyright (c) 2019-2022 The Project Symphonia Developers. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use super::common::SideData; use symphonia_core::codecs::CodecParameters; use symphonia_core::errors::Result; mod flac; mod opus; mod vorbis; /// Detect a `Mapper` for a logical stream given the identification packet of the stream. pub fn detect(buf: &[u8]) -> Result>> { let mapper = flac::detect(buf)? .or(vorbis::detect(buf)?) .or(opus::detect(buf)?) .or_else(make_null_mapper); Ok(mapper) } /// Result of a packet map operation. pub enum MapResult { /// The packet contained side data. SideData { data: SideData }, /// The packet contained setup data. Setup, /// The packet contained stream data. StreamData { dur: u64 }, /// The packet contained unknown data. Unknown, } /// A `PacketParser` implements a packet parser that decodes the timestamp and duration for a /// packet. pub trait PacketParser: Send + Sync { fn parse_next_packet_dur(&mut self, packet: &[u8]) -> u64; } /// A `Mapper` implements packet-handling for a specific `Codec`. pub trait Mapper: Send + Sync { /// Gets the name of the mapper. fn name(&self) -> &'static str; /// Soft-reset the mapper after a discontinuity in packets. fn reset(&mut self); /// Gets an immutable reference `CodecParameters` for the stream belonging to this `Mapper`. If /// the stream is not ready then the set of parameters may be incomplete. fn codec_params(&self) -> &CodecParameters; /// Gets a mutable reference to the `CodecParameters` for the stream belonging to this `Mapper`. /// If the stream is not ready then the set of parameters may be incomplete. fn codec_params_mut(&mut self) -> &mut CodecParameters; /// Convert an absolute granular position to a timestamp. fn absgp_to_ts(&self, ts: u64) -> u64 { ts } /// Make a packet parser for parsing packet timing. fn make_parser(&self) -> Option>; /// Map a packet. fn map_packet(&mut self, packet: &[u8]) -> Result; /// Returns `true` if the stream can is ready for usage. If the stream is not ready then the /// mapper needs to consume more setup packets. fn is_ready(&self) -> bool { true } } fn make_null_mapper() -> Option> { Some(Box::new(NullMapper::new())) } struct NullMapper { params: CodecParameters, } impl NullMapper { fn new() -> Self { NullMapper { params: CodecParameters::new() } } } impl Mapper for NullMapper { fn name(&self) -> &'static str { "null" } fn codec_params(&self) -> &CodecParameters { &self.params } fn codec_params_mut(&mut self) -> &mut CodecParameters { &mut self.params } fn reset(&mut self) { // Nothing to do! } fn make_parser(&self) -> Option> { Some(Box::new(NullPacketParser {})) } fn map_packet(&mut self, _: &[u8]) -> Result { Ok(MapResult::Unknown) } } struct NullPacketParser {} impl PacketParser for NullPacketParser { fn parse_next_packet_dur(&mut self, _: &[u8]) -> u64 { 0 } } symphonia-format-ogg-0.5.2/src/mappings/opus.rs000064400000000000000000000151261046102023000176450ustar 00000000000000// Symphonia // Copyright (c) 2019-2022 The Project Symphonia Developers. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::common::SideData; use super::{MapResult, Mapper, PacketParser}; use symphonia_core::audio::Channels; use symphonia_core::codecs::{CodecParameters, CODEC_TYPE_OPUS}; use symphonia_core::errors::Result; use symphonia_core::io::{BufReader, ReadBytes}; use symphonia_core::meta::MetadataBuilder; use symphonia_core::units::TimeBase; use symphonia_metadata::vorbis; use log::warn; /// The minimum expected size of an Opus identification packet. const OGG_OPUS_MIN_IDENTIFICATION_PACKET_SIZE: usize = 19; /// The signature for an Opus identification packet. const OGG_OPUS_MAGIC_SIGNATURE: &[u8] = b"OpusHead"; /// The signature for an Opus metadata packet. const OGG_OPUS_COMMENT_SIGNATURE: &[u8] = b"OpusTags"; /// The maximum support Opus OGG mapping version. const OGG_OPUS_MAPPING_VERSION_MAX: u8 = 0x0f; pub fn detect(buf: &[u8]) -> Result>> { // The identification packet for Opus must be a minimum size. if buf.len() < OGG_OPUS_MIN_IDENTIFICATION_PACKET_SIZE { return Ok(None); } let mut reader = BufReader::new(buf); // The first 8 bytes are the magic signature ASCII bytes. let mut magic = [0; 8]; reader.read_buf_exact(&mut magic)?; if magic != *OGG_OPUS_MAGIC_SIGNATURE { return Ok(None); } // The next byte is the OGG Opus encapsulation version. The version is split into two // sub-fields: major and minor. These fields are stored in the upper and lower 4-bit, // respectively. if reader.read_byte()? > OGG_OPUS_MAPPING_VERSION_MAX { return Ok(None); } // The next byte is the number of channels and must not be 0. let channel_count = reader.read_byte()?; if channel_count == 0 { return Ok(None); } // The next 16-bit integer is the pre-skip padding (# of samples at 48kHz to subtract from the // OGG granule position to obtain the PCM sample position). let pre_skip = reader.read_u16()?; // The next 32-bit integer is the sample rate of the original audio. let _ = reader.read_u32()?; // Next, the 16-bit gain value. let _ = reader.read_u16()?; // The next byte indicates the channel mapping. Most of these values are reserved. let channel_mapping = reader.read_byte()?; let channels = match channel_mapping { // RTP Mapping 0 if channel_count == 1 => Channels::FRONT_LEFT, 0 if channel_count == 2 => Channels::FRONT_LEFT | Channels::FRONT_RIGHT, // Vorbis Mapping 1 => match channel_count { 1 => Channels::FRONT_LEFT, 2 => Channels::FRONT_LEFT | Channels::FRONT_RIGHT, 3 => Channels::FRONT_LEFT | Channels::FRONT_CENTRE | Channels::FRONT_RIGHT, 4 => { Channels::FRONT_LEFT | Channels::FRONT_RIGHT | Channels::REAR_LEFT | Channels::REAR_RIGHT } 5 => { Channels::FRONT_LEFT | Channels::FRONT_CENTRE | Channels::FRONT_RIGHT | Channels::REAR_LEFT | Channels::REAR_RIGHT } 6 => { Channels::FRONT_LEFT | Channels::FRONT_CENTRE | Channels::FRONT_RIGHT | Channels::REAR_LEFT | Channels::REAR_RIGHT | Channels::LFE1 } 7 => { Channels::FRONT_LEFT | Channels::FRONT_CENTRE | Channels::FRONT_RIGHT | Channels::SIDE_LEFT | Channels::SIDE_RIGHT | Channels::REAR_CENTRE | Channels::LFE1 } 8 => { Channels::FRONT_LEFT | Channels::FRONT_CENTRE | Channels::FRONT_RIGHT | Channels::SIDE_LEFT | Channels::SIDE_RIGHT | Channels::REAR_LEFT | Channels::REAR_RIGHT | Channels::LFE1 } _ => return Ok(None), }, // Reserved, and should NOT be supported for playback. _ => return Ok(None), }; // Populate the codec parameters with the information read from identification header. let mut codec_params = CodecParameters::new(); codec_params .for_codec(CODEC_TYPE_OPUS) .with_delay(u32::from(pre_skip)) .with_sample_rate(48_000) .with_time_base(TimeBase::new(1, 48_000)) .with_channels(channels) .with_extra_data(Box::from(buf)); // Instantiate the Opus mapper. let mapper = Box::new(OpusMapper { codec_params, need_comment: true }); Ok(Some(mapper)) } pub struct OpusPacketParser {} impl PacketParser for OpusPacketParser { fn parse_next_packet_dur(&mut self, _packet: &[u8]) -> u64 { // TODO: Implement. 0 } } struct OpusMapper { codec_params: CodecParameters, need_comment: bool, } impl Mapper for OpusMapper { fn name(&self) -> &'static str { "opus" } fn reset(&mut self) { // Nothing to do. } fn codec_params(&self) -> &CodecParameters { &self.codec_params } fn codec_params_mut(&mut self) -> &mut CodecParameters { &mut self.codec_params } fn make_parser(&self) -> Option> { Some(Box::new(OpusPacketParser {})) } fn map_packet(&mut self, packet: &[u8]) -> Result { if !self.need_comment { Ok(MapResult::StreamData { dur: 0 }) } else { let mut reader = BufReader::new(packet); // Read the header signature. let mut sig = [0; 8]; reader.read_buf_exact(&mut sig)?; if sig == *OGG_OPUS_COMMENT_SIGNATURE { // This packet should be a metadata packet containing a Vorbis Comment. let mut builder = MetadataBuilder::new(); vorbis::read_comment_no_framing(&mut reader, &mut builder)?; self.need_comment = false; Ok(MapResult::SideData { data: SideData::Metadata(builder.metadata()) }) } else { warn!("ogg (opus): invalid packet type"); Ok(MapResult::Unknown) } } } } symphonia-format-ogg-0.5.2/src/mappings/vorbis.rs000064400000000000000000000517011046102023000201620ustar 00000000000000// Symphonia // Copyright (c) 2019-2022 The Project Symphonia Developers. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use super::{MapResult, Mapper, PacketParser}; use crate::common::SideData; use symphonia_core::codecs::{CodecParameters, CODEC_TYPE_VORBIS}; use symphonia_core::errors::{decode_error, unsupported_error, Result}; use symphonia_core::io::{BitReaderRtl, BufReader, ReadBitsRtl, ReadBytes}; use symphonia_core::meta::MetadataBuilder; use symphonia_core::units::TimeBase; use symphonia_metadata::vorbis::*; use symphonia_utils_xiph::vorbis::*; use log::warn; /// The identification header packet size. const VORBIS_IDENTIFICATION_HEADER_SIZE: usize = 30; /// The packet type for an identification header. const VORBIS_PACKET_TYPE_IDENTIFICATION: u8 = 1; /// The packet type for a comment header. const VORBIS_PACKET_TYPE_COMMENT: u8 = 3; /// The packet type for a setup header. const VORBIS_PACKET_TYPE_SETUP: u8 = 5; /// The common header packet signature. const VORBIS_HEADER_PACKET_SIGNATURE: &[u8] = b"vorbis"; /// The Vorbis version supported by this mapper. const VORBIS_VERSION: u32 = 0; /// The minimum block size (64) expressed as a power-of-2 exponent. const VORBIS_BLOCKSIZE_MIN: u8 = 6; /// The maximum block size (8192) expressed as a power-of-2 exponent. const VORBIS_BLOCKSIZE_MAX: u8 = 13; struct VorbisPacketParser { modes_block_flags: u64, num_modes: u8, bs0_exp: u8, bs1_exp: u8, prev_bs_exp: Option, } impl VorbisPacketParser { fn new(bs0_exp: u8, bs1_exp: u8, num_modes: u8, modes_block_flags: u64) -> Self { Self { bs0_exp, bs1_exp, num_modes, modes_block_flags, prev_bs_exp: None } } fn reset(&mut self) { self.prev_bs_exp = None; } } impl PacketParser for VorbisPacketParser { fn parse_next_packet_dur(&mut self, packet: &[u8]) -> u64 { let mut bs = BitReaderRtl::new(packet); // First bit must be 0 to indicate audio packet. match bs.read_bool() { Ok(bit) if !bit => (), _ => return 0, } // Number of bits for the mode number. let mode_num_bits = ilog(u32::from(self.num_modes) - 1); // Read the mode number. let mode_num = match bs.read_bits_leq32(mode_num_bits) { Ok(mode_num) => mode_num as u8, _ => return 0, }; // Determine the current block size. let cur_bs_exp = if mode_num < self.num_modes { let block_flag = (self.modes_block_flags >> mode_num) & 1; if block_flag == 1 { self.bs1_exp } else { self.bs0_exp } } else { return 0; }; // Calculate the duration if the previous block size is available. Otherwise return 0. let dur = if let Some(prev_bs_exp) = self.prev_bs_exp { ((1 << prev_bs_exp) >> 2) + ((1 << cur_bs_exp) >> 2) } else { 0 }; self.prev_bs_exp = Some(cur_bs_exp); dur } } pub fn detect(buf: &[u8]) -> Result>> { // The identification header packet must be the correct size. if buf.len() != VORBIS_IDENTIFICATION_HEADER_SIZE { return Ok(None); } // Read the identification header. Any errors cause detection to fail. let ident = match read_ident_header(&mut BufReader::new(buf)) { Ok(ident) => ident, _ => return Ok(None), }; // Populate the codec parameters with the information above. let mut codec_params = CodecParameters::new(); codec_params .for_codec(CODEC_TYPE_VORBIS) .with_sample_rate(ident.sample_rate) .with_time_base(TimeBase::new(1, ident.sample_rate)) .with_extra_data(Box::from(buf)); if let Some(channels) = vorbis_channels_to_channels(ident.n_channels) { codec_params.with_channels(channels); } // Instantiate the Vorbis mapper. let mapper = Box::new(VorbisMapper { codec_params, ident, parser: None, has_setup_header: false }); Ok(Some(mapper)) } struct VorbisMapper { codec_params: CodecParameters, ident: IdentHeader, parser: Option, has_setup_header: bool, } impl Mapper for VorbisMapper { fn name(&self) -> &'static str { "vorbis" } fn codec_params(&self) -> &CodecParameters { &self.codec_params } fn codec_params_mut(&mut self) -> &mut CodecParameters { &mut self.codec_params } fn reset(&mut self) { if let Some(parser) = &mut self.parser { parser.reset() } } fn make_parser(&self) -> Option> { match &self.parser { Some(base_parser) => { let parser = Box::new(VorbisPacketParser::new( base_parser.bs0_exp, base_parser.bs1_exp, base_parser.num_modes, base_parser.modes_block_flags, )); Some(parser) } _ => None, } } fn map_packet(&mut self, packet: &[u8]) -> Result { let mut reader = BufReader::new(packet); // All Vorbis packets indicate the packet type in the first byte. let packet_type = reader.read_u8()?; // An even numbered packet type is an audio packet. if packet_type & 1 == 0 { let dur = match &mut self.parser { Some(parser) => parser.parse_next_packet_dur(packet), _ => 0, }; Ok(MapResult::StreamData { dur }) } else { // Odd numbered packet types are header packets. let mut sig = [0; 6]; reader.read_buf_exact(&mut sig)?; // Check if the presumed header packet has the common header packet signature. if sig != VORBIS_HEADER_PACKET_SIGNATURE { return decode_error("ogg (vorbis): header packet signature invalid"); } // Handle each header packet type specifically. match packet_type { VORBIS_PACKET_TYPE_COMMENT => { let mut builder = MetadataBuilder::new(); read_comment_no_framing(&mut reader, &mut builder)?; Ok(MapResult::SideData { data: SideData::Metadata(builder.metadata()) }) } VORBIS_PACKET_TYPE_SETUP => { // Append the setup headers to the extra data. let mut extra_data = self.codec_params.extra_data.take().unwrap().to_vec(); extra_data.extend_from_slice(packet); // Try to read the setup header. if let Ok(modes) = read_setup(&mut BufReader::new(packet), &self.ident) { let num_modes = modes.len(); let mut modes_block_flags = 0; assert!(num_modes <= 64); for (i, mode) in modes.iter().enumerate() { if mode.block_flag { modes_block_flags |= 1 << i; } } let parser = VorbisPacketParser::new( self.ident.bs0_exp, self.ident.bs1_exp, num_modes as u8, modes_block_flags, ); self.parser.replace(parser); } self.codec_params.with_extra_data(extra_data.into_boxed_slice()); self.has_setup_header = true; Ok(MapResult::Setup) } _ => { warn!("ogg (vorbis): packet type {} unexpected", packet_type); Ok(MapResult::Unknown) } } } } fn is_ready(&self) -> bool { self.has_setup_header } } struct IdentHeader { n_channels: u8, sample_rate: u32, bs0_exp: u8, bs1_exp: u8, } fn read_ident_header(reader: &mut B) -> Result { // The packet type must be an identification header. let packet_type = reader.read_u8()?; if packet_type != VORBIS_PACKET_TYPE_IDENTIFICATION { return decode_error("ogg (vorbis): invalid packet type for identification header"); } // Next, the header packet signature must be correct. let mut packet_sig_buf = [0; 6]; reader.read_buf_exact(&mut packet_sig_buf)?; if packet_sig_buf != VORBIS_HEADER_PACKET_SIGNATURE { return decode_error("ogg (vorbis): invalid header signature"); } // Next, the Vorbis version must be 0. let version = reader.read_u32()?; if version != VORBIS_VERSION { return unsupported_error("ogg (vorbis): only vorbis 1 is supported"); } // Next, the number of channels and sample rate must be non-zero. let n_channels = reader.read_u8()?; if n_channels == 0 { return decode_error("ogg (vorbis): number of channels cannot be 0"); } let sample_rate = reader.read_u32()?; if sample_rate == 0 { return decode_error("ogg (vorbis): sample rate cannot be 0"); } // Read the bitrate range. let _bitrate_max = reader.read_u32()?; let _bitrate_nom = reader.read_u32()?; let _bitrate_min = reader.read_u32()?; // Next, blocksize_0 and blocksize_1 are packed into a single byte. let block_sizes = reader.read_u8()?; let bs0_exp = (block_sizes & 0x0f) >> 0; let bs1_exp = (block_sizes & 0xf0) >> 4; // The block sizes must not exceed the bounds. if bs0_exp < VORBIS_BLOCKSIZE_MIN || bs0_exp > VORBIS_BLOCKSIZE_MAX { return decode_error("ogg (vorbis): blocksize_0 out-of-bounds"); } if bs1_exp < VORBIS_BLOCKSIZE_MIN || bs1_exp > VORBIS_BLOCKSIZE_MAX { return decode_error("ogg (vorbis): blocksize_1 out-of-bounds"); } // Blocksize_0 must be >= blocksize_1 if bs0_exp > bs1_exp { return decode_error("ogg (vorbis): blocksize_0 exceeds blocksize_1"); } // Framing flag must be set. if reader.read_u8()? != 0x1 { return decode_error("ogg (vorbis): ident header framing flag unset"); } Ok(IdentHeader { n_channels, sample_rate, bs0_exp, bs1_exp }) } fn read_setup(reader: &mut BufReader<'_>, ident: &IdentHeader) -> Result> { // The packet type must be an setup header. let packet_type = reader.read_u8()?; if packet_type != VORBIS_PACKET_TYPE_SETUP { return decode_error("ogg (vorbis): invalid packet type for setup header"); } // Next, the setup packet signature must be correct. let mut packet_sig_buf = [0; 6]; reader.read_buf_exact(&mut packet_sig_buf)?; if packet_sig_buf != VORBIS_HEADER_PACKET_SIGNATURE { return decode_error("ogg (vorbis): invalid setup header signature"); } // The remaining portion of the setup header packet is read bitwise. let mut bs = BitReaderRtl::new(reader.read_buf_bytes_available_ref()); // Skip the codebooks. skip_codebooks(&mut bs)?; // Skip the time-domain transforms (placeholders in Vorbis 1). skip_time_domain_transforms(&mut bs)?; // Skip the floors. skip_floors(&mut bs)?; // Skip the residues. skip_residues(&mut bs)?; // Skip the channel mappings. skip_mappings(&mut bs, ident.n_channels)?; // Read modes. let modes = read_modes(&mut bs)?; // Framing flag must be set. if !bs.read_bool()? { return decode_error("ogg (vorbis): setup header framing flag unset"); } Ok(modes) } fn skip_codebooks(bs: &mut BitReaderRtl<'_>) -> Result<()> { let count = bs.read_bits_leq32(8)? + 1; for _ in 0..count { skip_codebook(bs)?; } Ok(()) } pub fn skip_codebook(bs: &mut BitReaderRtl<'_>) -> Result<()> { // Verify codebook synchronization word. let sync = bs.read_bits_leq32(24)?; if sync != 0x564342 { return decode_error("ogg (vorbis): invalid codebook sync"); } // Read codebook number of dimensions and entries. let codebook_dimensions = bs.read_bits_leq32(16)? as u16; let codebook_entries = bs.read_bits_leq32(24)?; let is_length_ordered = bs.read_bool()?; if !is_length_ordered { // Codeword list is not length ordered. let is_sparse = bs.read_bool()?; if is_sparse { // Sparsely packed codeword entry list. for _ in 0..codebook_entries { if bs.read_bool()? { let _ = bs.read_bits_leq32(5)?; } } } else { bs.ignore_bits(codebook_entries * 5)?; } } else { // Codeword list is length ordered. let mut cur_entry = 0; let mut _cur_len = bs.read_bits_leq32(5)? + 1; loop { let num_bits = if codebook_entries > cur_entry { ilog(codebook_entries - cur_entry) } else { 0 }; let num = bs.read_bits_leq32(num_bits)?; cur_entry += num; if cur_entry > codebook_entries { return decode_error("ogg (vorbis): invalid codebook"); } if cur_entry == codebook_entries { break; } } } // Read and unpack vector quantization (VQ) lookup table. let lookup_type = bs.read_bits_leq32(4)?; match lookup_type & 0xf { 0 => (), 1 | 2 => { let _min_value = bs.read_bits_leq32(32)?; let _delta_value = bs.read_bits_leq32(32)?; let value_bits = bs.read_bits_leq32(4)? + 1; let _sequence_p = bs.read_bool()?; // Lookup type is either 1 or 2 as per outer match. let lookup_values = match lookup_type { 1 => lookup1_values(codebook_entries, codebook_dimensions), 2 => codebook_entries * u32::from(codebook_dimensions), _ => unreachable!(), }; // Multiplicands bs.ignore_bits(lookup_values * value_bits)?; } _ => return decode_error("ogg (vorbis): invalid codeword lookup type"), } Ok(()) } fn skip_time_domain_transforms(bs: &mut BitReaderRtl<'_>) -> Result<()> { let count = bs.read_bits_leq32(6)? + 1; for _ in 0..count { // All these values are placeholders and must be 0. if bs.read_bits_leq32(16)? != 0 { return decode_error("ogg (vorbis): invalid time domain tranform"); } } Ok(()) } fn skip_floors(bs: &mut BitReaderRtl<'_>) -> Result<()> { let count = bs.read_bits_leq32(6)? + 1; for _ in 0..count { skip_floor(bs)?; } Ok(()) } fn skip_floor(bs: &mut BitReaderRtl<'_>) -> Result<()> { let floor_type = bs.read_bits_leq32(16)?; match floor_type { 0 => skip_floor0_setup(bs), 1 => skip_floor1_setup(bs), _ => decode_error("ogg (vorbis): invalid floor type"), } } fn skip_floor0_setup(bs: &mut BitReaderRtl<'_>) -> Result<()> { // floor0_order // floor0_rate // floor0_bark_map_size // floor0_amplitude_bits // floor0_amplitude_offset bs.ignore_bits(8 + 16 + 16 + 6 + 8)?; let floor0_number_of_books = bs.read_bits_leq32(4)? + 1; bs.ignore_bits(floor0_number_of_books * 8)?; Ok(()) } fn skip_floor1_setup(bs: &mut BitReaderRtl<'_>) -> Result<()> { // The number of partitions. 5-bit value, 0..31 range. let floor1_partitions = bs.read_bits_leq32(5)? as usize; // Parition list of up-to 32 partitions (floor1_partitions), with each partition indicating // a 4-bit class (0..16) identifier. let mut floor1_partition_class_list = [0; 32]; let mut floor1_classes_dimensions = [0; 16]; if floor1_partitions > 0 { let mut max_class = 0; // 4-bits, 0..15 for class_idx in &mut floor1_partition_class_list[..floor1_partitions] { *class_idx = bs.read_bits_leq32(4)? as u8; max_class = max_class.max(*class_idx); } let num_classes = usize::from(1 + max_class); for dimensions in floor1_classes_dimensions[..num_classes].iter_mut() { *dimensions = bs.read_bits_leq32(3)? as u8 + 1; let subclass_bits = bs.read_bits_leq32(2)?; if subclass_bits != 0 { let _main_book = bs.read_bits_leq32(8)?; } let num_subclasses = 1 << subclass_bits; // Sub-class books bs.ignore_bits(num_subclasses * 8)?; } } let _floor1_multiplier = bs.read_bits_leq32(2)?; let rangebits = bs.read_bits_leq32(4)?; for &class_idx in &floor1_partition_class_list[..floor1_partitions] { let class_dimensions = u32::from(floor1_classes_dimensions[class_idx as usize]); // TODO? No more than 65 elements are allowed. bs.ignore_bits(class_dimensions * rangebits)?; } Ok(()) } fn skip_residues(bs: &mut BitReaderRtl<'_>) -> Result<()> { let count = bs.read_bits_leq32(6)? + 1; for _ in 0..count { let _residue_type = bs.read_bits_leq32(16)?; skip_residue_setup(bs)? } Ok(()) } fn skip_residue_setup(bs: &mut BitReaderRtl<'_>) -> Result<()> { // residue_begin // residue_end // residue_partition_size bs.ignore_bits(24 + 24 + 24)?; let residue_classifications = bs.read_bits_leq32(6)? as u8 + 1; // residue_classbook bs.ignore_bits(8)?; let mut num_codebooks = 0; for _ in 0..residue_classifications { let low_bits = bs.read_bits_leq32(3)? as u8; let high_bits = if bs.read_bool()? { bs.read_bits_leq32(5)? as u8 } else { 0 }; let is_used = (high_bits << 3) | low_bits; num_codebooks += is_used.count_ones(); } bs.ignore_bits(num_codebooks * 8)?; Ok(()) } fn skip_mappings(bs: &mut BitReaderRtl<'_>, audio_channels: u8) -> Result<()> { let count = bs.read_bits_leq32(6)? + 1; for _ in 0..count { skip_mapping(bs, audio_channels)? } Ok(()) } fn skip_mapping(bs: &mut BitReaderRtl<'_>, audio_channels: u8) -> Result<()> { let mapping_type = bs.read_bits_leq32(16)?; match mapping_type { 0 => skip_mapping_type0_setup(bs, audio_channels), _ => decode_error("ogg (vorbis): invalid mapping type"), } } fn skip_mapping_type0_setup(bs: &mut BitReaderRtl<'_>, audio_channels: u8) -> Result<()> { let num_submaps = if bs.read_bool()? { bs.read_bits_leq32(4)? + 1 } else { 1 }; if bs.read_bool()? { // Number of channel couplings (up-to 256). let coupling_steps = bs.read_bits_leq32(8)? as u16 + 1; // The maximum channel number. let max_ch = audio_channels - 1; // The number of bits to read for the magnitude and angle channel numbers. Never exceeds 8. let coupling_bits = ilog(u32::from(max_ch)); debug_assert!(coupling_bits <= 8); // Read each channel coupling. for _ in 0..coupling_steps { let _magnitude_ch = bs.read_bits_leq32(coupling_bits)?; let _angle_ch = bs.read_bits_leq32(coupling_bits)?; } } if bs.read_bits_leq32(2)? != 0 { return decode_error("ogg (vorbis): reserved mapping bits non-zero"); } // If the number of submaps is > 1 read the multiplex numbers from the bitstream, otherwise // they're all 0. if num_submaps > 1 { // Mux to use per channel. bs.ignore_bits(u32::from(audio_channels) * 4)?; } // Reserved, floor, and residue to use per submap. bs.ignore_bits(num_submaps * (8 + 8 + 8))?; Ok(()) } fn read_modes(bs: &mut BitReaderRtl<'_>) -> Result> { let count = bs.read_bits_leq32(6)? + 1; (0..count).map(|_| read_mode(bs)).collect() } #[derive(Debug)] struct Mode { block_flag: bool, } fn read_mode(bs: &mut BitReaderRtl<'_>) -> Result { let block_flag = bs.read_bool()?; let window_type = bs.read_bits_leq32(16)? as u16; let transform_type = bs.read_bits_leq32(16)? as u16; let _mapping = bs.read_bits_leq32(8)? as u8; // Only window type 0 is allowed in Vorbis 1 (section 4.2.4). if window_type != 0 { return decode_error("ogg (vorbis): invalid window type for mode"); } // Only transform type 0 is allowed in Vorbis 1 (section 4.2.4). if transform_type != 0 { return decode_error("ogg (vorbis): invalid transform type for mode"); } let mode = Mode { block_flag }; Ok(mode) } #[inline(always)] pub fn ilog(x: u32) -> u32 { 32 - x.leading_zeros() } #[inline(always)] fn lookup1_values(entries: u32, dimensions: u16) -> u32 { // (value ^ dimensions) <= entries // [(value ^ dimensions) ^ (1 / dimensions)] = lower[entries ^ (1 / dimensions)] // value = lower[entries ^ (1 / dimensions)] let value = (entries as f32).powf(1.0f32 / f32::from(dimensions)).floor() as u32; assert!(value.pow(u32::from(dimensions)) <= entries); assert!((value + 1).pow(u32::from(dimensions)) > entries); value } symphonia-format-ogg-0.5.2/src/page.rs000064400000000000000000000244341046102023000157570ustar 00000000000000// Symphonia // Copyright (c) 2019-2022 The Project Symphonia Developers. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use symphonia_core::checksum::Crc32; use symphonia_core::errors::{decode_error, Error, Result}; use symphonia_core::io::{BufReader, Monitor, MonitorStream, ReadBytes, SeekBuffered}; use log::{debug, warn}; const OGG_PAGE_MARKER: [u8; 4] = *b"OggS"; const OGG_PAGE_HEADER_SIZE: usize = 27; pub const OGG_PAGE_MAX_SIZE: usize = OGG_PAGE_HEADER_SIZE + 255 + 255 * 255; #[derive(Copy, Clone, Default)] pub struct PageHeader { pub version: u8, pub absgp: u64, pub serial: u32, pub sequence: u32, pub crc: u32, pub n_segments: u8, pub is_continuation: bool, pub is_first_page: bool, pub is_last_page: bool, } /// Reads a `PageHeader` from the the provided reader. fn read_page_header(reader: &mut B) -> Result { // The OggS marker should be present. let marker = reader.read_quad_bytes()?; if marker != OGG_PAGE_MARKER { return decode_error("ogg: missing ogg stream marker"); } let version = reader.read_byte()?; // There is only one OGG version, and that is version 0. if version != 0 { return decode_error("ogg: invalid ogg version"); } let flags = reader.read_byte()?; // Only the first 3 least-significant bits are used for flags. if flags & 0xf8 != 0 { return decode_error("ogg: invalid flag bits set"); } let ts = reader.read_u64()?; let serial = reader.read_u32()?; let sequence = reader.read_u32()?; let crc = reader.read_u32()?; let n_segments = reader.read_byte()?; Ok(PageHeader { version, absgp: ts, serial, sequence, crc, n_segments, is_continuation: (flags & 0x01) != 0, is_first_page: (flags & 0x02) != 0, is_last_page: (flags & 0x04) != 0, }) } /// Quickly synchronizes the provided reader to the next OGG page capture pattern, but does not /// perform any further verification. fn sync_page(reader: &mut B) -> Result<()> { let mut marker = u32::from_be_bytes(reader.read_quad_bytes()?); while marker.to_be_bytes() != OGG_PAGE_MARKER { marker <<= 8; marker |= u32::from(reader.read_u8()?); } Ok(()) } /// An iterator over packets within a `Page`. pub struct PagePackets<'a> { lens: core::slice::Iter<'a, u16>, data: &'a [u8], } impl<'a> PagePackets<'a> { /// If this page ends with an incomplete (partial) packet, get a slice to the data associated /// with the partial packet. pub fn partial_packet(self) -> Option<&'a [u8]> { // Consume the rest of the packets. let discard = usize::from(self.lens.sum::()); if self.data.len() > discard { Some(&self.data[discard..]) } else { None } } } impl<'a> Iterator for PagePackets<'a> { type Item = &'a [u8]; fn next(&mut self) -> Option { match self.lens.next() { Some(len) => { let (packet, rem) = self.data.split_at(usize::from(*len)); self.data = rem; Some(packet) } _ => None, } } } /// An OGG page. pub struct Page<'a> { /// The page header. pub header: PageHeader, packet_lens: &'a [u16], page_buf: &'a [u8], } impl<'a> Page<'a> { /// Returns an iterator over all complete packets within the page. /// /// If this page contains a partial packet, then the partial packet data may be retrieved using /// the `partial_packet` function of the iterator. pub fn packets(&self) -> PagePackets<'_> { PagePackets { lens: self.packet_lens.iter(), data: self.page_buf } } /// Gets the number of packets completed on this page. pub fn num_packets(&self) -> usize { self.packet_lens.len() } } /// A reader of OGG pages. pub struct PageReader { header: PageHeader, packet_lens: Vec, page_buf: Vec, page_buf_len: usize, } impl PageReader { pub fn try_new(reader: &mut B) -> Result where B: ReadBytes + SeekBuffered, { let mut page_reader = PageReader { header: Default::default(), packet_lens: Vec::new(), page_buf: Vec::new(), page_buf_len: 0, }; page_reader.try_next_page(reader)?; Ok(page_reader) } /// Attempts to read the next page. If the page is corrupted or invalid, returns an error. pub fn try_next_page(&mut self, reader: &mut B) -> Result<()> where B: ReadBytes + SeekBuffered, { let mut header_buf = [0u8; OGG_PAGE_HEADER_SIZE]; header_buf[..4].copy_from_slice(&OGG_PAGE_MARKER); // Synchronize to an OGG page capture pattern. sync_page(reader)?; // Record the position immediately after synchronization. If the page is found corrupt the // reader will need to seek back here to try to regain synchronization. let sync_pos = reader.pos(); // Read the part of the page header after the capture pattern into a buffer. reader.read_buf_exact(&mut header_buf[4..])?; // Parse the page header buffer. let header = read_page_header(&mut BufReader::new(&header_buf))?; // debug!( // "page {{ version={}, absgp={}, serial={}, sequence={}, crc={:#x}, n_segments={}, \ // is_first={}, is_last={}, is_continuation={} }}", // header.version, // header.absgp, // header.serial, // header.sequence, // header.crc, // header.n_segments, // header.is_first_page, // header.is_last_page, // header.is_continuation, // ); // The CRC of the OGG page requires the page checksum bytes to be zeroed. header_buf[22..26].copy_from_slice(&[0u8; 4]); // Instantiate a Crc32, initialize it with 0, and feed it the page header buffer. let mut crc32 = Crc32::new(0); crc32.process_buf_bytes(&header_buf); // The remainder of the page will be checksummed as it is read. let mut crc32_reader = MonitorStream::new(reader, crc32); // Read segment table. let mut page_body_len = 0; let mut packet_len = 0; // TODO: Can this be transactional? A corrupt page causes the PageReader's state not // to change. self.packet_lens.clear(); for _ in 0..header.n_segments { let seg_len = crc32_reader.read_byte()?; page_body_len += usize::from(seg_len); packet_len += u16::from(seg_len); // A segment with a length < 255 indicates that the segment is the end of a packet. // Push the packet length into the packet queue for the stream. if seg_len < 255 { self.packet_lens.push(packet_len); packet_len = 0; } } self.read_page_body(&mut crc32_reader, page_body_len)?; let calculated_crc = crc32_reader.monitor().crc(); // If the CRC for the page is incorrect, then the page is corrupt. if header.crc != calculated_crc { warn!("crc mismatch: expected {:#x}, got {:#x}", header.crc, calculated_crc); // Clear packet buffer. self.packet_lens.clear(); self.page_buf_len = 0; // Seek back to the immediately after the previous sync position. crc32_reader.into_inner().seek_buffered(sync_pos); return decode_error("ogg: crc mismatch"); } self.header = header; Ok(()) } /// Reads the next page. If the next page is corrupted or invalid, the page is discarded and /// the reader tries again until a valid page is read or end-of-stream. pub fn next_page(&mut self, reader: &mut B) -> Result<()> where B: ReadBytes + SeekBuffered, { loop { match self.try_next_page(reader) { Ok(_) => break, Err(Error::IoError(e)) => return Err(Error::from(e)), _ => (), } } Ok(()) } /// Reads the next page with a specific serial. If the next page is corrupted or invalid, the /// page is discarded and the reader tries again until a valid page is read or end-of-stream. pub fn next_page_for_serial(&mut self, reader: &mut B, serial: u32) -> Result<()> where B: ReadBytes + SeekBuffered, { loop { match self.try_next_page(reader) { Ok(_) => { // Exit if a page with the specific serial is found. if self.header.serial == serial && !self.header.is_continuation { break; } } Err(Error::IoError(e)) => return Err(Error::from(e)), _ => (), } } Ok(()) } /// Gets a buffer to the first packet, if it exists. pub fn first_packet(&self) -> Option<&[u8]> { self.packet_lens.first().map(|&len| &self.page_buf[..usize::from(len)]) } /// Gets the current page header. pub fn header(&self) -> PageHeader { self.header } /// Gets a reference to the current page. pub fn page(&self) -> Page<'_> { assert!(self.page_buf_len <= 255 * 255, "ogg pages are <= 65025 bytes"); Page { header: self.header, packet_lens: &self.packet_lens, page_buf: &self.page_buf[..self.page_buf_len], } } fn read_page_body(&mut self, reader: &mut B, len: usize) -> Result<()> { // This is precondition. assert!(len <= 255 * 255); if len > self.page_buf.len() { // New page buffer size, rounded up to the nearest 8K block. let new_buf_len = (len + (8 * 1024 - 1)) & !(8 * 1024 - 1); debug!("grow page buffer to {} bytes", new_buf_len); self.page_buf.resize(new_buf_len, Default::default()); } self.page_buf_len = len; reader.read_buf_exact(&mut self.page_buf[..len])?; Ok(()) } } symphonia-format-ogg-0.5.2/src/physical.rs000064400000000000000000000130621046102023000166520ustar 00000000000000// Symphonia // Copyright (c) 2019-2022 The Project Symphonia Developers. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use std::collections::{BTreeMap, BTreeSet}; use std::io::{Seek, SeekFrom}; use symphonia_core::errors::Result; use symphonia_core::io::{MediaSourceStream, ReadBytes, ScopedStream, SeekBuffered}; use super::logical::{InspectState, LogicalStream}; use super::page::*; use log::debug; pub fn probe_stream_start( reader: &mut MediaSourceStream, pages: &mut PageReader, streams: &mut BTreeMap, ) { // Save the original position to jump back to. let original_pos = reader.pos(); // Scope the reader the prevent overruning the seekback region. let mut scoped_reader = ScopedStream::new(reader, OGG_PAGE_MAX_SIZE as u64); let mut probed = BTreeSet::::new(); // Examine the first bitstream page of each logical stream within the physical stream to // determine the number of leading samples, and start time. This function is called assuming // the page reader is on the first bitstream page within the physical stream. loop { let page = pages.page(); // If the page does not belong to the current physical stream, break out. let stream = if let Some(stream) = streams.get_mut(&page.header.serial) { stream } else { break; }; // If the stream hasn't been marked as probed. if !probed.contains(&page.header.serial) { // Probe the first page of the logical stream. stream.inspect_start_page(&page); // Mark the logical stream as probed. probed.insert(page.header.serial); } // If all logical streams were probed, break out immediately. if probed.len() >= streams.len() { break; } // Read the next page. match pages.try_next_page(&mut scoped_reader) { Ok(_) => (), _ => break, }; } scoped_reader.into_inner().seek_buffered(original_pos); } pub fn probe_stream_end( reader: &mut MediaSourceStream, pages: &mut PageReader, streams: &mut BTreeMap, byte_range_start: u64, byte_range_end: u64, ) -> Result> { // Save the original position. let original_pos = reader.pos(); // Number of bytes to linearly scan. We assume the OGG maximum page size for each logical // stream. let linear_scan_len = (streams.len() * OGG_PAGE_MAX_SIZE) as u64; // Optimization: Try a linear scan of the last few pages first. This will cover all // non-chained physical streams, which is the majority of cases. if byte_range_end >= linear_scan_len && byte_range_start <= byte_range_end - linear_scan_len { reader.seek(SeekFrom::Start(byte_range_end - linear_scan_len))?; } else { reader.seek(SeekFrom::Start(byte_range_start))?; } pages.next_page(reader)?; let result = scan_stream_end(reader, pages, streams, byte_range_end); // If there are no pages belonging to the current physical stream at the end of the media // source stream, then one or more physical streams are chained. Use a bisection method to find // the end of the current physical stream. let result = if result.is_none() { debug!("media source stream is chained, bisecting end of physical stream"); let mut start = byte_range_start; let mut end = byte_range_end; loop { let mid = (end + start) / 2; reader.seek(SeekFrom::Start(mid))?; match pages.next_page(reader) { Ok(_) => (), _ => break, } let header = pages.header(); if streams.contains_key(&header.serial) { start = mid; } else { end = mid; } if end - start < linear_scan_len { break; } } // Scan the last few pages of the physical stream. reader.seek(SeekFrom::Start(start))?; pages.next_page(reader)?; scan_stream_end(reader, pages, streams, end) } else { result }; // Restore the original position reader.seek(SeekFrom::Start(original_pos))?; Ok(result) } fn scan_stream_end( reader: &mut MediaSourceStream, pages: &mut PageReader, streams: &mut BTreeMap, byte_range_end: u64, ) -> Option { let scoped_len = byte_range_end - reader.pos(); let mut scoped_reader = ScopedStream::new(reader, scoped_len); let mut upper_pos = None; let mut state: InspectState = Default::default(); // Read pages until the provided end position or a new physical stream starts. loop { let page = pages.page(); // If the page does not belong to the current physical stream, then break out, the // extent of the physical stream has been found. let stream = if let Some(stream) = streams.get_mut(&page.header.serial) { stream } else { break; }; state = stream.inspect_end_page(state, &page); // The new end of the physical stream is the position after this page. upper_pos = Some(scoped_reader.pos()); // Read to the next page. match pages.next_page(&mut scoped_reader) { Ok(_) => (), _ => break, } } upper_pos }