From 7938624b77f39319e5fd591ce55fcc3bc763d63b Mon Sep 17 00:00:00 2001 From: karlri <49443488+karlri@users.noreply.github.com> Date: Thu, 22 Feb 2024 03:24:48 +0100 Subject: [PATCH] ogg: Decode Opus packet durations (#231) Decode the packet duration for Opus streams in OGG. Co-authored-by: Karl Rikte --- symphonia-format-ogg/src/mappings/opus.rs | 62 +++++++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/symphonia-format-ogg/src/mappings/opus.rs b/symphonia-format-ogg/src/mappings/opus.rs index bef5cb4d..0c4ef074 100644 --- a/symphonia-format-ogg/src/mappings/opus.rs +++ b/symphonia-format-ogg/src/mappings/opus.rs @@ -150,9 +150,63 @@ pub fn detect(buf: &[u8]) -> Result>> { pub struct OpusPacketParser {} impl PacketParser for OpusPacketParser { - fn parse_next_packet_dur(&mut self, _packet: &[u8]) -> u64 { - // TODO: Implement. - 0 + 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.get(0) { + 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 } } @@ -184,7 +238,7 @@ impl Mapper for OpusMapper { fn map_packet(&mut self, packet: &[u8]) -> Result { if !self.need_comment { - Ok(MapResult::StreamData { dur: 0 }) + Ok(MapResult::StreamData { dur: OpusPacketParser {}.parse_next_packet_dur(packet) }) } else { let mut reader = BufReader::new(packet);