blob: 193ef8fd3376a365e9cd5b0a9f975da5aa0d2078 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::{format_err, Result};
use bitfield::bitfield;
use fidl::encoding::Decodable;
use fidl_fuchsia_media::*;
use std::{convert::TryInto, fs, io, path::Path};
use stream_processor_test::*;
/// Represents an SBC elementary stream.
pub struct SbcStream {
data: Vec<u8>,
oob_bytes: Vec<u8>,
chunk_frames: usize,
}
impl SbcStream {
/// Constructs an SBC elementary stream from a file with raw elementary stream data.
pub fn from_file(
filename: impl AsRef<Path>,
codec_info: &[u8],
chunk_frames: usize,
) -> io::Result<Self> {
Ok(SbcStream { data: fs::read(filename)?, oob_bytes: codec_info.to_vec(), chunk_frames })
}
/// Returns an iterator over SBC frames that does not copy.
fn frame_iter(&self) -> impl Iterator<Item = SbcFrame<'_>> {
SbcFrameIter { data: &self.data, pos: 0, chunk_frames: self.chunk_frames }
}
}
impl ElementaryStream for SbcStream {
fn format_details(&self, version_ordinal: u64) -> FormatDetails {
FormatDetails {
format_details_version_ordinal: Some(version_ordinal),
mime_type: Some(String::from("audio/sbc")),
oob_bytes: Some(self.oob_bytes.clone()),
..<FormatDetails as Decodable>::new_empty()
}
}
fn is_access_units(&self) -> bool {
false
}
fn stream<'a>(&'a self) -> Box<dyn Iterator<Item = ElementaryStreamChunk> + 'a> {
Box::new(self.frame_iter().map(|frame| ElementaryStreamChunk {
start_access_unit: false,
known_end_access_unit: false,
data: frame.data.to_vec(),
significance: Significance::Audio(AudioSignificance::Encoded),
timestamp: None,
}))
}
}
#[derive(Debug, PartialEq)]
enum ChannelMode {
Mono,
DualChannel,
Stereo,
JointStereo,
}
impl From<u8> for ChannelMode {
fn from(bits: u8) -> Self {
match bits {
0 => ChannelMode::Mono,
1 => ChannelMode::DualChannel,
2 => ChannelMode::Stereo,
3 => ChannelMode::JointStereo,
_ => panic!("invalid channel mode"),
}
}
}
bitfield! {
pub struct SbcFrameHeader(u32);
impl Debug;
u8;
syncword, _: 7, 0;
subbands, _: 8;
allocation_method, _: 9;
into ChannelMode, channel_mode, _: 11, 10;
blocks_bits, _: 13, 12;
sampling_frequency_bits, _: 15, 14;
bitpool_bits, _: 23, 16;
crc_check, _: 31, 24;
}
impl SbcFrameHeader {
/// The number of channels, based on the channel mode in the header.
/// From Table 12.18 in the A2DP Spec.
fn channels(&self) -> usize {
match self.channel_mode() {
ChannelMode::Mono => 1,
_ => 2,
}
}
fn has_syncword(&self) -> bool {
const SBC_SYNCWORD: u8 = 0x9c;
self.syncword() == SBC_SYNCWORD
}
/// The number of blocks, based on tbe bits in the header.
/// From Table 12.17 in the A2DP Spec.
fn blocks(&self) -> usize {
4 * (self.blocks_bits() + 1) as usize
}
fn bitpool(&self) -> usize {
self.bitpool_bits() as usize
}
/// Number of subbands based on the header bit.
/// From Table 12.20 in the A2DP Spec.
fn num_subbands(&self) -> usize {
if self.subbands() {
8
} else {
4
}
}
/// Calculates the frame length.
/// Formula from Section 12.9 of the A2DP Spec.
fn frame_length(&self) -> Result<usize> {
if !self.has_syncword() {
return Err(format_err!("syncword does not match"));
}
let len = 4 + (4 * self.num_subbands() * self.channels()) / 8;
let rest = (match self.channel_mode() {
ChannelMode::Mono | ChannelMode::DualChannel => {
self.blocks() * self.channels() * self.bitpool()
}
ChannelMode::Stereo => self.blocks() * self.bitpool(),
ChannelMode::JointStereo => self.num_subbands() + (self.blocks() * self.bitpool()),
} as f64
/ 8.0)
.ceil() as usize;
Ok(len + rest)
}
/// Given a buffer with an SBC frame at the start, find the length of the
/// SBC frame.
fn find_sbc_frame_len(buf: &[u8]) -> Result<usize> {
if buf.len() < 4 {
return Err(format_err!("Buffer too short for header"));
}
let hdr = u32::from_le_bytes((&buf[0..4]).try_into()?);
SbcFrameHeader(hdr).frame_length()
}
}
pub struct SbcFrame<'a> {
pub data: &'a [u8],
pub length: usize,
}
/// An iterator over frames in an SBC stream.
struct SbcFrameIter<'a> {
data: &'a [u8],
pos: usize,
chunk_frames: usize,
}
impl<'a> SbcFrameIter<'a> {
fn next_frame(&self, pos: usize) -> Option<SbcFrame<'a>> {
SbcFrameHeader::find_sbc_frame_len(&self.data[pos..])
.map(|len| {
let end_pos = std::cmp::min(pos + len * self.chunk_frames, self.data.len());
let frame_len = end_pos - pos;
SbcFrame { data: &self.data[pos..end_pos], length: frame_len }
})
.ok()
}
}
impl<'a> Iterator for SbcFrameIter<'a> {
type Item = SbcFrame<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.data.len() {
return None;
}
let frame = self.next_frame(self.pos);
self.pos += frame.as_ref().map(|f| f.length).unwrap_or(0);
frame
}
}