symphonia-format-ogg-0.5.4/.cargo_vcs_info.json0000644000000001620000000000100151100ustar { "git": { "sha1": "d3b7742fa73674b70d9ab80cc5f8384cc653df3a" }, "path_in_vcs": "symphonia-format-ogg" }symphonia-format-ogg-0.5.4/Cargo.toml0000644000000022420000000000100131070ustar # 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.4" 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.4" [dependencies.symphonia-metadata] version = "0.5.4" [dependencies.symphonia-utils-xiph] version = "0.5.4" symphonia-format-ogg-0.5.4/Cargo.toml.orig000064400000000000000000000013441046102023000165720ustar 00000000000000[package] name = "symphonia-format-ogg" version = "0.5.4" 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.4", path = "../symphonia-core" } symphonia-metadata = { version = "0.5.4", path = "../symphonia-metadata" } symphonia-utils-xiph = { version = "0.5.4", path = "../symphonia-utils-xiph" }symphonia-format-ogg-0.5.4/README.md000064400000000000000000000011721046102023000151610ustar 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 a free and open-source project that welcomes contributions! To get started, please read our [Contribution Guidelines](https://github.com/pdeljanov/Symphonia/tree/master/CONTRIBUTING.md). symphonia-format-ogg-0.5.4/src/common.rs000064400000000000000000000006241046102023000163300ustar 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.4/src/demuxer.rs000064400000000000000000000433041046102023000165130ustar 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(); // Bisection method byte ranges. When these two values are equal, the bisection has // converged on the position of the correct page. let mut start_byte_pos = self.phys_byte_range_start; let mut end_byte_pos = self.phys_byte_range_end.unwrap(); // Bisect the stream while the byte range is large. For smaller ranges, a linear scan is // faster than having the the binary search converge. while end_byte_pos - start_byte_pos > 2 * OGG_PAGE_MAX_SIZE as u64 { // 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(_) => (), _ => { // No more pages for the stream from the mid-point onwards. debug!( "seek: bisect step: byte_range=[{}, {}, {}]", start_byte_pos, mid_byte_pos, end_byte_pos, ); end_byte_pos = mid_byte_pos; continue; } } // 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_ts={}, end_ts={} }} byte_range=[{}, {}, {}]", start_ts, end_ts, start_byte_pos, mid_byte_pos, end_byte_pos, ); if required_ts < start_ts { // The required timestamp is less-than the timestamp of the first sample in the // page. 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 the page. Update the lower bound and bisect again. start_byte_pos = mid_byte_pos; } else { // The sample with the required timestamp is contained in the page. The // bisection has converged on the correct page so stop the bisection. start_byte_pos = mid_byte_pos; end_byte_pos = mid_byte_pos; break; } } // If the bisection did not converge, then the linear search must continue from the // lower-bound (start) position of what would've been the next iteration of bisection. if start_byte_pos != end_byte_pos { 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), } } // 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.4/src/lib.rs000064400000000000000000000012701046102023000156040ustar 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.4/src/logical.rs000064400000000000000000000376041046102023000164620ustar 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 = 16 * 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.4/src/mappings/flac.rs000064400000000000000000000253101046102023000175620ustar 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.4/src/mappings/mod.rs000064400000000000000000000065731046102023000174460ustar 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.4/src/mappings/opus.rs000064400000000000000000000220331046102023000176420ustar 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 { // See https://www.rfc-editor.org/rfc/rfc6716 // Read TOC (Table Of Contents) byte which is the first byte in the opus data. let toc_byte = match packet.first() { Some(b) => b, None => { warn!("opus packet empty"); return 0; } }; // The configuration number is the 5 most significant bits. Shift out 3 least significant // bits. let configuration_number = toc_byte >> 3; // max 2^5-1 = 31 // The configuration number maps to packet length according to this lookup table. // See https://www.rfc-editor.org/rfc/rfc6716 top half of page 14. // Numbers are in milliseconds in the rfc. Down below they are in TimeBase units, so // 10ms = 10*48. #[rustfmt::skip] const CONFIGURATION_NUMBER_TO_FRAME_DURATION: [u32; 32] = [ 10*48, 20*48, 40*48, 60*48, 10*48, 20*48, 40*48, 60*48, 10*48, 20*48, 40*48, 60*48, 10*48, 20*48, 10*48, 20*48, (2.5*48.0) as u32, 5*48, 10*48, 20*48, (2.5*48.0) as u32, 5*48, 10*48, 20*48, (2.5*48.0) as u32, 5*48, 10*48, 20*48, (2.5*48.0) as u32, 5*48, 10*48, 20*48, ]; // Look up the frame length. let frame_duration = CONFIGURATION_NUMBER_TO_FRAME_DURATION[configuration_number as usize] as u64; // Look up the number of frames in the packet. // See https://www.rfc-editor.org/rfc/rfc6716 bottom half of page 14. let c = toc_byte & 0b11; // Note: it's actually called "c" in the rfc. let num_frames = match c { 0 => 1, 1 | 2 => 2, 3 => match packet.get(1) { Some(byte) => { // TOC byte is followed by number of frames. See page 18 section 3.2.5 code 3 let m = byte & 0b11111; // Note: it's actually called "M" in the rfc. m as u64 } None => { // What to do here? I'd like to return an error but this is an infalliable // trait. warn!("opus code 3 packet with no following byte containing number of frames"); return 0; } }, _ => unreachable!("masked 2 bits"), }; // Look up the packet length and return it. frame_duration * num_frames } } 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: OpusPacketParser {}.parse_next_packet_dur(packet) }) } 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.4/src/mappings/vorbis.rs000064400000000000000000000517011046102023000201640ustar 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.4/src/page.rs000064400000000000000000000244341046102023000157610ustar 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.4/src/physical.rs000064400000000000000000000130621046102023000166540ustar 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 }