Skip to content

Commit

Permalink
ogg: Decode Opus packet durations (#231)
Browse files Browse the repository at this point in the history
Decode the packet duration for Opus streams in OGG.

Co-authored-by: Karl Rikte <karl.rikte@axis.com>
  • Loading branch information
karlri and Karl Rikte committed Feb 22, 2024
1 parent 37cf4d8 commit 7938624
Showing 1 changed file with 58 additions and 4 deletions.
62 changes: 58 additions & 4 deletions symphonia-format-ogg/src/mappings/opus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,63 @@ pub fn detect(buf: &[u8]) -> Result<Option<Box<dyn Mapper>>> {
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
}
}

Expand Down Expand Up @@ -184,7 +238,7 @@ impl Mapper for OpusMapper {

fn map_packet(&mut self, packet: &[u8]) -> Result<MapResult> {
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);
Expand Down

0 comments on commit 7938624

Please sign in to comment.