diff --git a/Cargo.lock b/Cargo.lock index d224b4d45..36041cc9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "aac" -version = "0.0.1" -dependencies = [ - "byteorder", - "bytes", - "bytesio", - "num-derive", - "num-traits", - "scuffle-workspace-hack", -] - [[package]] name = "addr2line" version = "0.24.2" @@ -74,6 +62,7 @@ dependencies = [ "bytesio", "num-derive", "num-traits", + "scuffle-bitio", "scuffle-workspace-hack", ] @@ -217,6 +206,7 @@ dependencies = [ "byteorder", "bytes", "bytesio", + "scuffle-bitio", "scuffle-workspace-hack", ] @@ -909,6 +899,7 @@ version = "0.0.1" dependencies = [ "bytes", "bytesio", + "scuffle-bitio", "scuffle-workspace-hack", ] @@ -976,7 +967,6 @@ dependencies = [ name = "flv" version = "0.0.1" dependencies = [ - "aac", "amf0", "av1", "byteorder", @@ -986,6 +976,8 @@ dependencies = [ "h265", "num-derive", "num-traits", + "scuffle-aac", + "scuffle-bitio", "scuffle-workspace-hack", ] @@ -1165,6 +1157,7 @@ dependencies = [ "bytes", "bytesio", "exp_golomb", + "scuffle-bitio", "scuffle-workspace-hack", ] @@ -1176,6 +1169,7 @@ dependencies = [ "bytes", "bytesio", "exp_golomb", + "scuffle-bitio", "scuffle-workspace-hack", ] @@ -1602,7 +1596,6 @@ dependencies = [ name = "mp4" version = "0.0.1" dependencies = [ - "aac", "av1", "byteorder", "bytes", @@ -1611,6 +1604,8 @@ dependencies = [ "h264", "h265", "paste", + "scuffle-aac", + "scuffle-bitio", "scuffle-workspace-hack", "serde", "serde_json", @@ -2481,6 +2476,18 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scuffle-aac" +version = "0.0.1" +dependencies = [ + "byteorder", + "bytes", + "num-derive", + "num-traits", + "scuffle-bitio", + "scuffle-workspace-hack", +] + [[package]] name = "scuffle-batching" version = "0.0.4" @@ -2502,6 +2509,13 @@ dependencies = [ "tokio", ] +[[package]] +name = "scuffle-bitio" +version = "0.0.1" +dependencies = [ + "scuffle-workspace-hack", +] + [[package]] name = "scuffle-bootstrap" version = "0.0.2" @@ -3332,7 +3346,6 @@ dependencies = [ name = "transmuxer" version = "0.0.1" dependencies = [ - "aac", "amf0", "av1", "byteorder", @@ -3342,6 +3355,8 @@ dependencies = [ "h264", "h265", "mp4", + "scuffle-aac", + "scuffle-bitio", "scuffle-workspace-hack", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index a8d40aeda..1f90a643b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ members = [ "crates/transmuxer", "dev-tools/xtask", "crates/future-ext", + "crates/bitio", ] resolver = "2" @@ -59,6 +60,7 @@ scuffle-h3-webtransport = { path = "crates/h3-webtransport", version = "0.0.2" } scuffle-metrics-derive = { path = "crates/metrics/derive", version = "0.0.2" } scuffle-ffmpeg-sys = { path = "crates/ffmpeg-sys", version = "7.1.0" } scuffle-future-ext = { path = "crates/future-ext", version = "0.0.1" } +scuffle-bitio = { path = "crates/bitio", version = "0.0.1" } [profile.release-debug] inherits = "release" diff --git a/crates/aac/Cargo.toml b/crates/aac/Cargo.toml index 641623503..41b74cbd3 100644 --- a/crates/aac/Cargo.toml +++ b/crates/aac/Cargo.toml @@ -1,13 +1,16 @@ [package] -name = "aac" +name = "scuffle-aac" version = "0.0.1" edition = "2021" license = "MIT OR Apache-2.0" +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] } + [dependencies] bytes = "1.5" byteorder = "1.5" num-traits = "0.2" num-derive = "0.4" -bytesio = { path = "../bytesio" } scuffle-workspace-hack.workspace = true +scuffle-bitio = { workspace = true } diff --git a/crates/aac/src/config.rs b/crates/aac/src/config.rs deleted file mode 100644 index ad9438479..000000000 --- a/crates/aac/src/config.rs +++ /dev/null @@ -1,117 +0,0 @@ -use std::io; - -use bytes::Bytes; -use bytesio::bit_reader::BitReader; -use num_derive::FromPrimitive; -use num_traits::FromPrimitive; - -#[derive(Debug, Clone, PartialEq)] -/// Audio Specific Config -/// ISO/IEC 14496-3:2019(E) - 1.6 -pub struct AudioSpecificConfig { - pub audio_object_type: AudioObjectType, - pub sampling_frequency: u32, - pub channel_configuration: u8, - pub data: Bytes, -} - -#[derive(Debug, Clone, PartialEq, Copy, Eq)] -/// SBR Audio Object Type -/// ISO/IEC 14496-3:2019(E) - 1.5.1.2.6 -pub enum AudioObjectType { - AacMain, - AacLowComplexity, - Unknown(u16), -} - -impl From for AudioObjectType { - fn from(value: u16) -> Self { - match value { - 1 => AudioObjectType::AacMain, - 2 => AudioObjectType::AacLowComplexity, - _ => AudioObjectType::Unknown(value), - } - } -} - -impl From for u16 { - fn from(value: AudioObjectType) -> Self { - match value { - AudioObjectType::AacMain => 1, - AudioObjectType::AacLowComplexity => 2, - AudioObjectType::Unknown(value) => value, - } - } -} - -#[derive(FromPrimitive)] -#[repr(u8)] -/// Sampling Frequency Index -/// ISO/IEC 14496-3:2019(E) - 1.6.2.4 (Table 1.22) -pub enum SampleFrequencyIndex { - Freq96000 = 0x0, - Freq88200 = 0x1, - Freq64000 = 0x2, - Freq48000 = 0x3, - Freq44100 = 0x4, - Freq32000 = 0x5, - Freq24000 = 0x6, - Freq22050 = 0x7, - Freq16000 = 0x8, - Freq12000 = 0x9, - Freq11025 = 0xA, - Freq8000 = 0xB, - Freq7350 = 0xC, - FreqReserved = 0xD, - FreqReserved2 = 0xE, - FreqEscape = 0xF, -} - -impl SampleFrequencyIndex { - pub fn to_freq(&self) -> u32 { - match self { - SampleFrequencyIndex::Freq96000 => 96000, - SampleFrequencyIndex::Freq88200 => 88200, - SampleFrequencyIndex::Freq64000 => 64000, - SampleFrequencyIndex::Freq48000 => 48000, - SampleFrequencyIndex::Freq44100 => 44100, - SampleFrequencyIndex::Freq32000 => 32000, - SampleFrequencyIndex::Freq24000 => 24000, - SampleFrequencyIndex::Freq22050 => 22050, - SampleFrequencyIndex::Freq16000 => 16000, - SampleFrequencyIndex::Freq12000 => 12000, - SampleFrequencyIndex::Freq11025 => 11025, - SampleFrequencyIndex::Freq8000 => 8000, - SampleFrequencyIndex::Freq7350 => 7350, - SampleFrequencyIndex::FreqReserved => 0, - SampleFrequencyIndex::FreqReserved2 => 0, - SampleFrequencyIndex::FreqEscape => 0, - } - } -} - -impl AudioSpecificConfig { - pub fn parse(data: Bytes) -> io::Result { - let mut bitreader = BitReader::from(data); - let mut audio_object_type = bitreader.read_bits(5)? as u16; - if audio_object_type == 31 { - audio_object_type = 32 + bitreader.read_bits(6)? as u16; - } - - let sampling_frequency_index = SampleFrequencyIndex::from_u8(bitreader.read_bits(4)? as u8) - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?; - let sampling_frequency = match sampling_frequency_index { - SampleFrequencyIndex::FreqEscape => bitreader.read_bits(24)? as u32, - _ => sampling_frequency_index.to_freq(), - }; - - let channel_configuration = bitreader.read_bits(4)? as u8; - - Ok(Self { - audio_object_type: audio_object_type.into(), - sampling_frequency, - channel_configuration, - data: bitreader.into_inner().into_inner(), - }) - } -} diff --git a/crates/aac/src/lib.rs b/crates/aac/src/lib.rs index bc1bd5f52..4ba8d8a91 100644 --- a/crates/aac/src/lib.rs +++ b/crates/aac/src/lib.rs @@ -1,6 +1,220 @@ -mod config; +#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))] -pub use config::{AudioObjectType, AudioSpecificConfig, SampleFrequencyIndex}; +use std::io; + +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; +use scuffle_bitio::BitReader; + +/// A Partial Audio Specific Config +/// ISO/IEC 14496-3:2019(E) - 1.6 +/// +/// This struct does not represent the full AudioSpecificConfig, it only +/// represents the top few fields. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[must_use] +pub struct PartialAudioSpecificConfig { + /// Audio Object Type + pub audio_object_type: AudioObjectType, + /// Sampling Frequency + pub sampling_frequency: u32, + /// Channel Configuration + pub channel_configuration: u8, +} + +/// SBR Audio Object Type +/// ISO/IEC 14496-3:2019(E) - 1.5.1.2.6 +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[must_use] +pub enum AudioObjectType { + AacMain, + AacLowComplexity, + Unknown(u16), +} + +impl AudioObjectType { + /// Converts an AudioObjectType to a u16 + pub const fn as_u16(&self) -> u16 { + match self { + AudioObjectType::AacMain => 1, + AudioObjectType::AacLowComplexity => 2, + AudioObjectType::Unknown(value) => *value, + } + } + + /// Converts a u16 to an AudioObjectType + pub const fn from_u16(value: u16) -> Self { + match value { + 1 => AudioObjectType::AacMain, + 2 => AudioObjectType::AacLowComplexity, + _ => AudioObjectType::Unknown(value), + } + } +} + +impl From for AudioObjectType { + fn from(value: u16) -> Self { + Self::from_u16(value) + } +} + +impl From for u16 { + fn from(value: AudioObjectType) -> Self { + value.as_u16() + } +} + +/// Sampling Frequency Index +/// +/// The purpose of the FrequencyIndex is to encode commonly used frequencies in +/// 4 bits to save space. These are the set of commonly used frequencies defined +/// in the specification. +/// +/// ISO/IEC 14496-3:2019(E) - 1.6.2.4 (Table 1.22) +#[derive(FromPrimitive, Debug, Clone, PartialEq, Copy, Eq, PartialOrd, Ord)] +#[repr(u8)] +#[must_use] +pub enum SampleFrequencyIndex { + /// 96000 Hz + Freq96000 = 0x0, + /// 88200 Hz + Freq88200 = 0x1, + /// 64000 Hz + Freq64000 = 0x2, + /// 48000 Hz + Freq48000 = 0x3, + /// 44100 Hz + Freq44100 = 0x4, + /// 32000 Hz + Freq32000 = 0x5, + /// 24000 Hz + Freq24000 = 0x6, + /// 22050 Hz + Freq22050 = 0x7, + /// 16000 Hz + Freq16000 = 0x8, + /// 12000 Hz + Freq12000 = 0x9, + /// 11025 Hz + Freq11025 = 0xA, + /// 8000 Hz + Freq8000 = 0xB, + /// 7350 Hz + Freq7350 = 0xC, + /// Reserved + FreqReserved = 0xD, + /// Reserved + FreqReserved2 = 0xE, + /// Escape (Meaning the frequency is not in the table, and we need to read + /// an additional 24 bits to get the frequency) + FreqEscape = 0xF, +} + +impl SampleFrequencyIndex { + /// Convert the SampleFrequencyIndex to the actual frequency in Hz + pub const fn to_freq(&self) -> Option { + match self { + SampleFrequencyIndex::Freq96000 => Some(96000), + SampleFrequencyIndex::Freq88200 => Some(88200), + SampleFrequencyIndex::Freq64000 => Some(64000), + SampleFrequencyIndex::Freq48000 => Some(48000), + SampleFrequencyIndex::Freq44100 => Some(44100), + SampleFrequencyIndex::Freq32000 => Some(32000), + SampleFrequencyIndex::Freq24000 => Some(24000), + SampleFrequencyIndex::Freq22050 => Some(22050), + SampleFrequencyIndex::Freq16000 => Some(16000), + SampleFrequencyIndex::Freq12000 => Some(12000), + SampleFrequencyIndex::Freq11025 => Some(11025), + SampleFrequencyIndex::Freq8000 => Some(8000), + SampleFrequencyIndex::Freq7350 => Some(7350), + SampleFrequencyIndex::FreqReserved => None, + SampleFrequencyIndex::FreqReserved2 => None, + SampleFrequencyIndex::FreqEscape => None, + } + } +} + +impl PartialAudioSpecificConfig { + /// Parse the Audio Specific Config from given bytes + /// The implementation is based on ISO/IEC 14496-3:2019(E) - 1.6.2.1 (Table + /// 1.19) This does not parse the entire AAC Data, it only parses the + /// top few fields. + /// - Audio Object Type + /// - Sampling Frequency + /// - Channel Configuration + pub fn parse(data: &[u8]) -> io::Result { + let mut bitreader = BitReader::new_from_slice(data); + + // GetAudioObjectType() # ISO/IEC 14496-3:2019(E) - 1.6.2.1 (Table 1.20) + let mut audio_object_type = bitreader.read_bits(5)? as u16; + if audio_object_type == 31 { + audio_object_type = 32 + bitreader.read_bits(6)? as u16; + } + + // The table calls for us to read a 4-bit value. If the value is type FreqEscape + // (0xF), we need to read 24 bits to get the sampling frequency. + let sampling_frequency_index = SampleFrequencyIndex::from_u8(bitreader.read_bits(4)? as u8) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?; + + let sampling_frequency = match sampling_frequency_index { + // Uses the extended sampling frequency to represent the freq as a non-common value + SampleFrequencyIndex::FreqEscape => bitreader.read_bits(24)? as u32, + _ => sampling_frequency_index + .to_freq() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?, + }; + + // 4 Bits to get the channel configuration + let channel_configuration = bitreader.read_bits(4)? as u8; + + Ok(Self { + audio_object_type: audio_object_type.into(), + sampling_frequency, + channel_configuration, + }) + } +} #[cfg(test)] -mod tests; +#[cfg_attr(all(test, coverage_nightly), coverage(off))] +mod tests { + use super::*; + + #[test] + fn test_aac_config_parse() { + let data = [ + 0x12, 0x10, 0x56, 0xe5, 0x00, 0x2d, 0x96, 0x01, 0x80, 0x80, 0x05, 0x00, 0x00, 0x00, 0x00, + ]; + + let config = PartialAudioSpecificConfig::parse(&data).unwrap(); + assert_eq!(config.audio_object_type, AudioObjectType::AacLowComplexity); + assert_eq!(config.sampling_frequency, 44100); + assert_eq!(config.channel_configuration, 2); + } + + #[test] + fn test_idx_to_freq() { + let cases = [ + (SampleFrequencyIndex::FreqEscape, None), + (SampleFrequencyIndex::FreqReserved2, None), + (SampleFrequencyIndex::FreqReserved, None), + (SampleFrequencyIndex::Freq7350, Some(7350)), + (SampleFrequencyIndex::Freq8000, Some(8000)), + (SampleFrequencyIndex::Freq11025, Some(11025)), + (SampleFrequencyIndex::Freq12000, Some(12000)), + (SampleFrequencyIndex::Freq16000, Some(16000)), + (SampleFrequencyIndex::Freq22050, Some(22050)), + (SampleFrequencyIndex::Freq24000, Some(24000)), + (SampleFrequencyIndex::Freq32000, Some(32000)), + (SampleFrequencyIndex::Freq44100, Some(44100)), + (SampleFrequencyIndex::Freq48000, Some(48000)), + (SampleFrequencyIndex::Freq64000, Some(64000)), + (SampleFrequencyIndex::Freq88200, Some(88200)), + (SampleFrequencyIndex::Freq96000, Some(96000)), + ]; + + for (idx, freq) in cases { + assert_eq!(freq, idx.to_freq(), "Expected frequency for {:?}", idx); + } + } +} diff --git a/crates/aac/src/tests.rs b/crates/aac/src/tests.rs deleted file mode 100644 index 2e87d0484..000000000 --- a/crates/aac/src/tests.rs +++ /dev/null @@ -1,36 +0,0 @@ -use bytes::Bytes; - -use crate::config::SampleFrequencyIndex; -use crate::{AudioObjectType, AudioSpecificConfig}; - -#[test] -fn test_aac_config_parse() { - let data = vec![ - 0x12, 0x10, 0x56, 0xe5, 0x00, 0x2d, 0x96, 0x01, 0x80, 0x80, 0x05, 0x00, 0x00, 0x00, 0x00, - ]; - - let config = AudioSpecificConfig::parse(Bytes::from(data)).unwrap(); - assert_eq!(config.audio_object_type, AudioObjectType::AacLowComplexity); - assert_eq!(config.sampling_frequency, 44100); - assert_eq!(config.channel_configuration, 2); -} - -#[test] -fn test_idx_to_freq() { - assert_eq!(0, SampleFrequencyIndex::FreqEscape.to_freq()); - assert_eq!(0, SampleFrequencyIndex::FreqReserved2.to_freq()); - assert_eq!(0, SampleFrequencyIndex::FreqReserved.to_freq()); - assert_eq!(7350, SampleFrequencyIndex::Freq7350.to_freq()); - assert_eq!(8000, SampleFrequencyIndex::Freq8000.to_freq()); - assert_eq!(11025, SampleFrequencyIndex::Freq11025.to_freq()); - assert_eq!(12000, SampleFrequencyIndex::Freq12000.to_freq()); - assert_eq!(16000, SampleFrequencyIndex::Freq16000.to_freq()); - assert_eq!(22050, SampleFrequencyIndex::Freq22050.to_freq()); - assert_eq!(24000, SampleFrequencyIndex::Freq24000.to_freq()); - assert_eq!(32000, SampleFrequencyIndex::Freq32000.to_freq()); - assert_eq!(44100, SampleFrequencyIndex::Freq44100.to_freq()); - assert_eq!(48000, SampleFrequencyIndex::Freq48000.to_freq()); - assert_eq!(64000, SampleFrequencyIndex::Freq64000.to_freq()); - assert_eq!(88200, SampleFrequencyIndex::Freq88200.to_freq()); - assert_eq!(96000, SampleFrequencyIndex::Freq96000.to_freq()); -} diff --git a/crates/amf0/Cargo.toml b/crates/amf0/Cargo.toml index 64d0c4f02..7b935dfff 100644 --- a/crates/amf0/Cargo.toml +++ b/crates/amf0/Cargo.toml @@ -11,3 +11,4 @@ num-traits = "0.2" num-derive = "0.4" bytesio = { path = "../bytesio" } scuffle-workspace-hack.workspace = true +scuffle-bitio = { workspace = true } diff --git a/crates/amf0/src/tests.rs b/crates/amf0/src/tests.rs index 7155fcbfd..20ae3fb0a 100644 --- a/crates/amf0/src/tests.rs +++ b/crates/amf0/src/tests.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::io::Cursor; use byteorder::ReadBytesExt; -use bytesio::bytes_writer::BytesWriter; use crate::{Amf0Marker, Amf0ReadError, Amf0Reader, Amf0Value, Amf0WriteError, Amf0Writer}; @@ -148,22 +147,22 @@ fn test_write_number() { let mut amf0_number = vec![0x00]; amf0_number.extend_from_slice(&772.161_f64.to_be_bytes()); - let mut writer = BytesWriter::default(); + let mut vec = Vec::::new(); - Amf0Writer::write_number(&mut writer, 772.161).unwrap(); + Amf0Writer::write_number(&mut vec, 772.161).unwrap(); - assert_eq!(writer.dispose(), amf0_number); + assert_eq!(vec, amf0_number); } #[test] fn test_write_boolean() { let amf0_boolean = vec![0x01, 0x01]; - let mut writer = BytesWriter::default(); + let mut vec = Vec::::new(); - Amf0Writer::write_bool(&mut writer, true).unwrap(); + Amf0Writer::write_bool(&mut vec, true).unwrap(); - assert_eq!(writer.dispose(), amf0_boolean); + assert_eq!(vec, amf0_boolean); } #[test] @@ -171,22 +170,22 @@ fn test_write_string() { let mut amf0_string = vec![0x02, 0x00, 0x0b]; amf0_string.extend_from_slice(b"Hello World"); - let mut writer = BytesWriter::default(); + let mut vec = Vec::::new(); - Amf0Writer::write_string(&mut writer, "Hello World").unwrap(); + Amf0Writer::write_string(&mut vec, "Hello World").unwrap(); - assert_eq!(writer.dispose(), amf0_string); + assert_eq!(vec, amf0_string); } #[test] fn test_write_null() { let amf0_null = vec![0x05]; - let mut writer = BytesWriter::default(); + let mut vec = Vec::::new(); - Amf0Writer::write_null(&mut writer).unwrap(); + Amf0Writer::write_null(&mut vec).unwrap(); - assert_eq!(writer.dispose(), amf0_null); + assert_eq!(vec, amf0_null); } #[test] @@ -196,9 +195,9 @@ fn test_write_object() { amf0_object.extend_from_slice(&[0x05]); amf0_object.extend_from_slice(&[0x00, 0x00, 0x09]); - let mut writer = BytesWriter::default(); + let mut vec = Vec::::new(); - Amf0Writer::write_object(&mut writer, &HashMap::from([("test".to_string(), Amf0Value::Null)])).unwrap(); + Amf0Writer::write_object(&mut vec, &HashMap::from([("test".to_string(), Amf0Value::Null)])).unwrap(); - assert_eq!(writer.dispose(), amf0_object); + assert_eq!(vec, amf0_object); } diff --git a/crates/amf0/src/writer.rs b/crates/amf0/src/writer.rs index 8f2343f6a..6d2730c3b 100644 --- a/crates/amf0/src/writer.rs +++ b/crates/amf0/src/writer.rs @@ -1,8 +1,7 @@ use std::collections::HashMap; -use std::io::Write; +use std::io; use byteorder::{BigEndian, WriteBytesExt}; -use bytesio::bytes_writer::BytesWriter; use super::define::Amf0Marker; use super::{Amf0Value, Amf0WriteError}; @@ -10,7 +9,7 @@ use super::{Amf0Value, Amf0WriteError}; pub struct Amf0Writer; impl Amf0Writer { - pub fn write_any(writer: &mut BytesWriter, value: &Amf0Value) -> Result<(), Amf0WriteError> { + pub fn write_any(writer: &mut impl io::Write, value: &Amf0Value) -> Result<(), Amf0WriteError> { match value { Amf0Value::Boolean(val) => Self::write_bool(writer, *val), Amf0Value::Null => Self::write_null(writer), @@ -21,24 +20,24 @@ impl Amf0Writer { } } - fn write_object_eof(writer: &mut BytesWriter) -> Result<(), Amf0WriteError> { + fn write_object_eof(writer: &mut impl io::Write) -> Result<(), Amf0WriteError> { writer.write_u24::(Amf0Marker::ObjectEnd as u32)?; Ok(()) } - pub fn write_number(writer: &mut BytesWriter, value: f64) -> Result<(), Amf0WriteError> { + pub fn write_number(writer: &mut impl io::Write, value: f64) -> Result<(), Amf0WriteError> { writer.write_u8(Amf0Marker::Number as u8)?; writer.write_f64::(value)?; Ok(()) } - pub fn write_bool(writer: &mut BytesWriter, value: bool) -> Result<(), Amf0WriteError> { + pub fn write_bool(writer: &mut impl io::Write, value: bool) -> Result<(), Amf0WriteError> { writer.write_u8(Amf0Marker::Boolean as u8)?; writer.write_u8(value as u8)?; Ok(()) } - pub fn write_string(writer: &mut BytesWriter, value: &str) -> Result<(), Amf0WriteError> { + pub fn write_string(writer: &mut impl io::Write, value: &str) -> Result<(), Amf0WriteError> { if value.len() > (u16::MAX as usize) { return Err(Amf0WriteError::NormalStringTooLong); } @@ -48,12 +47,12 @@ impl Amf0Writer { Ok(()) } - pub fn write_null(writer: &mut BytesWriter) -> Result<(), Amf0WriteError> { + pub fn write_null(writer: &mut impl io::Write) -> Result<(), Amf0WriteError> { writer.write_u8(Amf0Marker::Null as u8)?; Ok(()) } - pub fn write_object(writer: &mut BytesWriter, properties: &HashMap) -> Result<(), Amf0WriteError> { + pub fn write_object(writer: &mut impl io::Write, properties: &HashMap) -> Result<(), Amf0WriteError> { writer.write_u8(Amf0Marker::Object as u8)?; for (key, value) in properties { writer.write_u16::(key.len() as u16)?; diff --git a/crates/av1/Cargo.toml b/crates/av1/Cargo.toml index e9b4e86ee..afa452a6d 100644 --- a/crates/av1/Cargo.toml +++ b/crates/av1/Cargo.toml @@ -8,4 +8,5 @@ license = "MIT OR Apache-2.0" bytes = "1.5" byteorder = "1.5" bytesio = { path = "../bytesio" } +scuffle-bitio = { workspace = true } scuffle-workspace-hack.workspace = true diff --git a/crates/av1/src/config.rs b/crates/av1/src/config.rs index 1ec2cd795..20cd79c4b 100644 --- a/crates/av1/src/config.rs +++ b/crates/av1/src/config.rs @@ -1,9 +1,8 @@ use std::io; use bytes::Bytes; -use bytesio::bit_reader::BitReader; -use bytesio::bit_writer::BitWriter; use bytesio::bytes_reader::BytesCursor; +use scuffle_bitio::{BitReader, BitWriter}; #[derive(Debug, Clone, PartialEq)] /// AV1 Codec Configuration Record @@ -83,7 +82,7 @@ impl AV1CodecConfigurationRecord { } pub fn mux(&self, writer: &mut T) -> io::Result<()> { - let mut bit_writer = BitWriter::default(); + let mut bit_writer = BitWriter::new(writer); bit_writer.write_bit(self.marker)?; bit_writer.write_bits(self.version as u64, 7)?; @@ -109,8 +108,7 @@ impl AV1CodecConfigurationRecord { bit_writer.write_bits(0, 4)?; // reserved 4 bits } - writer.write_all(&bit_writer.into_inner())?; - writer.write_all(&self.config_obu)?; + bit_writer.finish()?.write_all(&self.config_obu)?; Ok(()) } diff --git a/crates/av1/src/obu/mod.rs b/crates/av1/src/obu/mod.rs index ee5defb52..b19acfc45 100644 --- a/crates/av1/src/obu/mod.rs +++ b/crates/av1/src/obu/mod.rs @@ -3,7 +3,7 @@ use std::io::{ }; use bytes::Bytes; -use bytesio::bit_reader::BitReader; +use scuffle_bitio::BitReader; pub mod seq; diff --git a/crates/av1/src/obu/seq.rs b/crates/av1/src/obu/seq.rs index ecad12ae9..cc0fd68bc 100644 --- a/crates/av1/src/obu/seq.rs +++ b/crates/av1/src/obu/seq.rs @@ -2,7 +2,7 @@ use std::io; use byteorder::{BigEndian, ReadBytesExt}; use bytes::Bytes; -use bytesio::bit_reader::BitReader; +use scuffle_bitio::BitReader; use super::ObuHeader; use crate::obu::read_uvlc; @@ -99,7 +99,7 @@ impl SequenceHeaderObu { } pub fn parse(header: ObuHeader, data: Bytes) -> io::Result { - let mut bit_reader = BitReader::from(data); + let mut bit_reader = BitReader::new_from_slice(data); let seq_profile = bit_reader.read_bits(3)? as u8; let still_picture = bit_reader.read_bit()?; diff --git a/crates/av1/src/tests.rs b/crates/av1/src/tests.rs index 6802cd2e1..40473ddb1 100644 --- a/crates/av1/src/tests.rs +++ b/crates/av1/src/tests.rs @@ -1,6 +1,6 @@ use std::io; -use bytesio::bit_reader::BitReader; +use scuffle_bitio::BitReader; use crate::config::AV1CodecConfigurationRecord; use crate::seq::{ColorConfig, OperatingPoint, SequenceHeaderObu}; @@ -24,7 +24,7 @@ fn test_config_demux() { assert!(config.chroma_subsampling_y); assert_eq!(config.initial_presentation_delay_minus_one, None); - let (header, data) = ObuHeader::parse(&mut BitReader::from(config.config_obu)).unwrap(); + let (header, data) = ObuHeader::parse(&mut BitReader::new_from_slice(config.config_obu)).unwrap(); assert_eq!(header.obu_type, ObuType::SequenceHeader); diff --git a/crates/bitio/Cargo.toml b/crates/bitio/Cargo.toml new file mode 100644 index 000000000..c76d1ccec --- /dev/null +++ b/crates/bitio/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "scuffle-bitio" +version = "0.0.1" +edition = "2021" +license = "MIT OR Apache-2.0" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] } + +[dependencies] +scuffle-workspace-hack.workspace = true diff --git a/crates/bitio/LICENSE.Apache-2.0 b/crates/bitio/LICENSE.Apache-2.0 new file mode 120000 index 000000000..5a4558f07 --- /dev/null +++ b/crates/bitio/LICENSE.Apache-2.0 @@ -0,0 +1 @@ +../../LICENSE.Apache-2.0 \ No newline at end of file diff --git a/crates/bitio/LICENSE.MIT b/crates/bitio/LICENSE.MIT new file mode 120000 index 000000000..244dbbf0b --- /dev/null +++ b/crates/bitio/LICENSE.MIT @@ -0,0 +1 @@ +../../LICENSE.MIT \ No newline at end of file diff --git a/crates/bitio/README.md b/crates/bitio/README.md new file mode 100644 index 000000000..1b610adc8 --- /dev/null +++ b/crates/bitio/README.md @@ -0,0 +1,17 @@ +# scuffle-bitio + +> [!WARNING] +> This crate is under active development and may not be stable. + +[![crates.io](https://img.shields.io/crates/v/scuffle-bitio.svg)](https://crates.io/crates/scuffle-bitio) [![docs.rs](https://img.shields.io/docsrs/scuffle-bitio)](https://docs.rs/scuffle-bitio) + +--- + +Adds a simple interface for writing and reading bits to and from a stream. + +## License + +This project is licensed under the [MIT](./LICENSE.MIT) or [Apache-2.0](./LICENSE.Apache-2.0) license. +You can choose between one of them if you use this work. + +`SPDX-License-Identifier: MIT OR Apache-2.0` diff --git a/crates/bitio/src/lib.rs b/crates/bitio/src/lib.rs new file mode 100644 index 000000000..509c27776 --- /dev/null +++ b/crates/bitio/src/lib.rs @@ -0,0 +1,7 @@ +#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))] + +mod read; +mod write; + +pub use read::BitReader; +pub use write::BitWriter; diff --git a/crates/bitio/src/read.rs b/crates/bitio/src/read.rs new file mode 100644 index 000000000..c64b7da30 --- /dev/null +++ b/crates/bitio/src/read.rs @@ -0,0 +1,298 @@ +use std::io; + +/// A reader that reads individual bits from a stream +#[derive(Debug)] +#[must_use] +pub struct BitReader { + data: T, + bit_pos: u8, + current_byte: u8, +} + +impl BitReader { + /// Create a new BitReader from a reader + pub const fn new(data: T) -> Self { + Self { + data, + bit_pos: 0, + current_byte: 0, + } + } +} + +impl BitReader { + /// Reads a single bit + pub fn read_bit(&mut self) -> io::Result { + if self.is_aligned() { + let mut buf = [0]; + self.data.read_exact(&mut buf)?; + self.current_byte = buf[0]; + } + + let bit = (self.current_byte >> (7 - self.bit_pos)) & 1; + + self.bit_pos = (self.bit_pos + 1) % 8; + + Ok(bit == 1) + } + + /// Reads multiple bits + pub fn read_bits(&mut self, count: u8) -> io::Result { + let count = count.min(64); + + let mut bits = 0; + for _ in 0..count { + let bit = self.read_bit()?; + bits <<= 1; + bits |= if bit { 1 } else { 0 }; + } + + Ok(bits) + } + + /// Aligns the reader to the next byte boundary + pub fn align(&mut self) -> io::Result<()> { + let amount_to_read = 8 - self.bit_pos; + self.read_bits(amount_to_read)?; + Ok(()) + } +} + +impl BitReader { + /// Returns the underlying reader + pub fn into_inner(self) -> T { + self.data + } + + /// Returns a reference to the underlying reader + pub const fn get_ref(&self) -> &T { + &self.data + } + + /// Returns the current bit position (0-7) + pub const fn bit_pos(&self) -> u8 { + self.bit_pos + } + + /// Checks if the reader is aligned to the byte boundary + pub const fn is_aligned(&self) -> bool { + self.bit_pos == 0 + } +} + +impl io::Read for BitReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + if self.is_aligned() { + return self.data.read(buf); + } + + for byte in buf.iter_mut() { + *byte = 0; + for _ in 0..8 { + let bit = self.read_bit()?; + *byte <<= 1; + *byte |= bit as u8; + } + } + + Ok(buf.len()) + } +} + +impl> BitReader> { + /// Creates a new BitReader from a slice + pub const fn new_from_slice(data: B) -> Self { + Self::new(std::io::Cursor::new(data)) + } +} + +impl BitReader { + /// Seeks a number of bits forward or backward + pub fn seek_bits(&mut self, count: i64) -> io::Result<()> { + if count == 0 { + return Ok(()); + } + + let abs = count.abs(); + let bit_move = abs % 8; + let byte_move = abs / 8; + + if count > 0 { + self.data.seek(io::SeekFrom::Current(byte_move))?; + self.read_bits(bit_move as u8)?; + } else { + let bit_pos = self.bit_pos as i64 - bit_move; + let additional_byte_move = if bit_pos < 0 { 1 } else { 0 }; + self.data.seek(io::SeekFrom::Current(-byte_move - additional_byte_move))?; + self.read_bits(bit_move.unsigned_abs() as u8)?; + } + + Ok(()) + } +} + +impl io::Seek for BitReader { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + match pos { + io::SeekFrom::Start(_) => { + self.bit_pos = 0; + self.data.seek(pos) + } + io::SeekFrom::Current(offset) => { + self.seek_bits(offset * 8)?; + Ok(self.data.stream_position()?) + } + io::SeekFrom::End(_) => { + self.bit_pos = 0; + self.data.seek(pos) + } + } + } +} + +#[cfg(test)] +#[cfg_attr(all(test, coverage_nightly), coverage(off))] +mod tests { + use io::{Read, Seek}; + + use super::*; + + #[test] + fn test_bit_reader() { + let binary = 0b10101010110011001111000101010101u32; + + let mut reader = BitReader::new_from_slice(binary.to_be_bytes()); + for i in 0..32 { + assert_eq!( + reader.read_bit().unwrap(), + (binary & (1 << (31 - i))) != 0, + "bit {} is not correct", + i + ); + } + + assert!(reader.read_bit().is_err(), "there shouldnt be any bits left"); + } + + #[test] + fn test_bit_reader_read_bits() { + let binary = 0b10101010110011001111000101010101u32; + let mut reader = BitReader::new_from_slice(binary.to_be_bytes()); + let cases = [ + (3, 0b101), + (4, 0b0101), + (3, 0b011), + (3, 0b001), + (3, 0b100), + (3, 0b111), + (5, 0b10001), + (1, 0b0), + (7, 0b1010101), + ]; + + for (i, (count, expected)) in cases.into_iter().enumerate() { + assert_eq!( + reader.read_bits(count).ok(), + Some(expected), + "reading {} bits ({i}) are not correct", + count + ); + } + + assert!(reader.read_bit().is_err(), "there shouldnt be any bits left"); + } + + #[test] + fn test_bit_reader_align() { + let mut reader = BitReader::new_from_slice([0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000]); + + for i in 0..6 { + let pos = reader.data.stream_position().unwrap(); + assert_eq!(pos, i, "stream pos"); + assert_eq!(reader.bit_pos(), 0, "bit pos"); + assert!(reader.read_bit().unwrap(), "bit {} is not correct", i); + reader.align().unwrap(); + let pos = reader.data.stream_position().unwrap(); + assert_eq!(pos, i + 1, "stream pos"); + assert_eq!(reader.bit_pos(), 0, "bit pos"); + } + + assert!(reader.read_bit().is_err(), "there shouldnt be any bits left"); + } + + #[test] + fn test_bit_reader_io_read() { + let binary = 0b10101010110011001111000101010101u32; + let mut reader = BitReader::new_from_slice(binary.to_be_bytes()); + + // Aligned read (calls the underlying read directly (very fast)) + let mut buf = [0; 1]; + reader.read_exact(&mut buf).unwrap(); + assert_eq!(buf, [0b10101010]); + + // Unaligned read + assert_eq!(reader.read_bits(1).unwrap(), 0b1); + let mut buf = [0; 1]; + reader.read_exact(&mut buf).unwrap(); + assert_eq!(buf, [0b10011001]); + } + + #[test] + fn test_bit_reader_seek() { + let binary = 0b10101010110011001111000101010101u32; + let mut reader = BitReader::new_from_slice(binary.to_be_bytes()); + + reader.seek_bits(5).unwrap(); + assert_eq!(reader.data.stream_position().unwrap(), 1); + assert_eq!(reader.bit_pos(), 5); + assert_eq!(reader.read_bits(1).unwrap(), 0b0); + assert_eq!(reader.bit_pos(), 6); + + reader.seek_bits(10).unwrap(); + assert_eq!(reader.data.stream_position().unwrap(), 2); + assert_eq!(reader.bit_pos(), 0); + assert_eq!(reader.read_bits(1).unwrap(), 0b1); + assert_eq!(reader.bit_pos(), 1); + assert_eq!(reader.data.stream_position().unwrap(), 3); + + reader.seek_bits(-8).unwrap(); + assert_eq!(reader.data.stream_position().unwrap(), 2); + assert_eq!(reader.bit_pos(), 1); + assert_eq!(reader.read_bits(1).unwrap(), 0b1); + assert_eq!(reader.bit_pos(), 2); + assert_eq!(reader.data.stream_position().unwrap(), 2); + } + + #[test] + fn test_bit_reader_io_seek() { + let binary = 0b10101010110011001111000101010101u32; + let mut reader = BitReader::new_from_slice(binary.to_be_bytes()); + reader.seek(io::SeekFrom::Start(1)).unwrap(); + assert_eq!(reader.bit_pos(), 0); + assert_eq!(reader.data.stream_position().unwrap(), 1); + assert_eq!(reader.read_bits(1).unwrap(), 0b1); + assert_eq!(reader.bit_pos(), 1); + assert_eq!(reader.data.stream_position().unwrap(), 2); + + reader.seek(io::SeekFrom::Current(1)).unwrap(); + assert_eq!(reader.bit_pos(), 1); + assert_eq!(reader.data.stream_position().unwrap(), 3); + assert_eq!(reader.read_bits(1).unwrap(), 0b1); + assert_eq!(reader.bit_pos(), 2); + assert_eq!(reader.data.stream_position().unwrap(), 3); + + reader.seek(io::SeekFrom::Current(-1)).unwrap(); + assert_eq!(reader.bit_pos(), 2); + assert_eq!(reader.data.stream_position().unwrap(), 2); + assert_eq!(reader.read_bits(1).unwrap(), 0b0); + assert_eq!(reader.bit_pos(), 3); + assert_eq!(reader.data.stream_position().unwrap(), 2); + + reader.seek(io::SeekFrom::End(-1)).unwrap(); + assert_eq!(reader.bit_pos(), 0); + assert_eq!(reader.data.stream_position().unwrap(), 3); + assert_eq!(reader.read_bits(1).unwrap(), 0b0); + assert_eq!(reader.bit_pos(), 1); + assert_eq!(reader.data.stream_position().unwrap(), 4); + } +} diff --git a/crates/bitio/src/write.rs b/crates/bitio/src/write.rs new file mode 100644 index 000000000..f4f4151a0 --- /dev/null +++ b/crates/bitio/src/write.rs @@ -0,0 +1,234 @@ +use std::io; + +/// A writer that allows you to write bits to a stream +#[derive(Debug)] +#[must_use] +pub struct BitWriter { + bit_pos: u8, + current_byte: u8, + writer: W, +} + +impl Default for BitWriter { + fn default() -> Self { + Self { + bit_pos: 0, + current_byte: 0, + writer: W::default(), + } + } +} + +impl BitWriter { + /// Writes a single bit to the stream + pub fn write_bit(&mut self, bit: bool) -> io::Result<()> { + if bit { + self.current_byte |= 1 << (7 - self.bit_pos); + } else { + self.current_byte &= !(1 << (7 - self.bit_pos)); + } + + self.bit_pos += 1; + + if self.bit_pos == 8 { + self.writer.write_all(&[self.current_byte])?; + self.current_byte = 0; + self.bit_pos = 0; + } + + Ok(()) + } + + /// Writes a number of bits to the stream (the most significant bit is + /// written first) + pub fn write_bits(&mut self, bits: u64, count: u8) -> io::Result<()> { + let count = count.min(64); + + for i in 0..count { + let bit = (bits >> (count - i - 1)) & 1 == 1; + self.write_bit(bit)?; + } + + Ok(()) + } + + /// Flushes the buffer and returns the underlying writer + /// This will also align the writer to the byte boundary + pub fn finish(mut self) -> io::Result { + self.align()?; + Ok(self.writer) + } + + /// Aligns the writer to the byte boundary + pub fn align(&mut self) -> io::Result<()> { + if self.bit_pos % 8 != 0 { + self.write_bits(0, 8 - (self.bit_pos % 8))?; + } + + Ok(()) + } +} + +impl BitWriter { + /// Creates a new BitWriter from a writer + pub const fn new(writer: W) -> Self { + Self { + bit_pos: 0, + current_byte: 0, + writer, + } + } + + /// Returns the current bit position (0-7) + pub const fn bit_pos(&self) -> u8 { + self.bit_pos % 8 + } + + /// Checks if the writer is aligned to the byte boundary + pub const fn is_aligned(&self) -> bool { + self.bit_pos % 8 == 0 + } + + /// Returns a reference to the underlying writer + pub const fn get_ref(&self) -> &W { + &self.writer + } +} + +impl io::Write for BitWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + if self.is_aligned() { + return self.writer.write(buf); + } + + for byte in buf { + self.write_bits(*byte as u64, 8)?; + } + + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + self.writer.flush() + } +} + +#[cfg(test)] +#[cfg_attr(all(test, coverage_nightly), coverage(off))] +mod tests { + use io::Write; + + use super::*; + + #[test] + fn test_bit_writer() { + let mut bit_writer = BitWriter::>::default(); + + bit_writer.write_bits(0b11111111, 8).unwrap(); + assert_eq!(bit_writer.bit_pos(), 0); + assert!(bit_writer.is_aligned()); + + bit_writer.write_bits(0b0000, 4).unwrap(); + assert_eq!(bit_writer.bit_pos(), 4); + assert!(!bit_writer.is_aligned()); + bit_writer.align().unwrap(); + assert_eq!(bit_writer.bit_pos(), 0); + assert!(bit_writer.is_aligned()); + + bit_writer.write_bits(0b1010, 4).unwrap(); + assert_eq!(bit_writer.bit_pos(), 4); + assert!(!bit_writer.is_aligned()); + + bit_writer.write_bits(0b101010101010, 12).unwrap(); + assert_eq!(bit_writer.bit_pos(), 0); + assert!(bit_writer.is_aligned()); + + bit_writer.write_bit(true).unwrap(); + assert_eq!(bit_writer.bit_pos(), 1); + assert!(!bit_writer.is_aligned()); + + assert_eq!( + bit_writer.finish().unwrap(), + vec![0b11111111, 0b00000000, 0b10101010, 0b10101010, 0b10000000] + ); + } + + #[test] + fn test_flush_buffer() { + let mut bit_writer = BitWriter::>::default(); + + bit_writer.write_bits(0b11111111, 8).unwrap(); + assert_eq!(bit_writer.bit_pos(), 0); + assert!(bit_writer.is_aligned()); + assert_eq!(bit_writer.get_ref(), &[0b11111111], "underlying writer should have one byte"); + + bit_writer.write_bits(0b0000, 4).unwrap(); + assert_eq!(bit_writer.bit_pos(), 4); + assert!(!bit_writer.is_aligned()); + assert_eq!(bit_writer.get_ref(), &[0b11111111], "underlying writer should have one bytes"); + + bit_writer.write_bits(0b1010, 4).unwrap(); + assert_eq!(bit_writer.bit_pos(), 0); + assert!(bit_writer.is_aligned()); + assert_eq!( + bit_writer.get_ref(), + &[0b11111111, 0b00001010], + "underlying writer should have two bytes" + ); + } + + #[test] + fn test_io_write() { + let mut inner = Vec::new(); + let mut bit_writer = BitWriter::new(&mut inner); + + bit_writer.write_bits(0b11111111, 8).unwrap(); + assert_eq!(bit_writer.bit_pos(), 0); + assert!(bit_writer.is_aligned()); + // We should have buffered the write + assert_eq!(bit_writer.get_ref().as_slice(), &[255]); + + bit_writer.write_all(&[1, 2, 3]).unwrap(); + assert_eq!(bit_writer.bit_pos(), 0); + assert!(bit_writer.is_aligned()); + // since we did an io::Write on an aligned bit_writer + // we should have written directly to the underlying + // writer + assert_eq!(bit_writer.get_ref().as_slice(), &[255, 1, 2, 3]); + + bit_writer.write_bit(true).unwrap(); + + bit_writer.write_bits(0b1010, 4).unwrap(); + + bit_writer + .write_all(&[0b11111111, 0b00000000, 0b11111111, 0b00000000]) + .unwrap(); + + // Since the writer was not aligned we should have buffered the writes + assert_eq!( + bit_writer.get_ref().as_slice(), + &[255, 1, 2, 3, 0b11010111, 0b11111000, 0b00000111, 0b11111000] + ); + + bit_writer.finish().unwrap(); + + assert_eq!( + inner, + vec![255, 1, 2, 3, 0b11010111, 0b11111000, 0b00000111, 0b11111000, 0b00000000] + ); + } + + #[test] + fn test_flush() { + let mut inner = Vec::new(); + let mut bit_writer = BitWriter::new(&mut inner); + + bit_writer.write_bits(0b10100000, 8).unwrap(); + + bit_writer.flush().unwrap(); + + assert_eq!(bit_writer.get_ref().as_slice(), &[0b10100000]); + assert_eq!(bit_writer.bit_pos(), 0); + assert!(bit_writer.is_aligned()); + } +} diff --git a/crates/bytesio/src/bit_reader.rs b/crates/bytesio/src/bit_reader.rs deleted file mode 100644 index a4f012bda..000000000 --- a/crates/bytesio/src/bit_reader.rs +++ /dev/null @@ -1,181 +0,0 @@ -use std::io::{ - SeekFrom, {self}, -}; - -use byteorder::ReadBytesExt; -use bytes::{Buf, Bytes}; - -pub struct BitReader> { - data: T, - bit_pos: usize, - current_byte: u8, -} - -impl> From for BitReader> { - fn from(bytes: T) -> Self { - Self::new(io::Cursor::new(bytes.into())) - } -} - -impl BitReader { - pub fn seek_bits(&mut self, pos: i64) -> io::Result<()> { - let mut seek_pos = self.data.stream_position()? as i64; - if !self.is_aligned() && seek_pos > 0 { - seek_pos -= 1; - } - - let current_bit_pos = self.bit_pos as i64; - let new_tb_pos = current_bit_pos + pos + seek_pos * 8; - if new_tb_pos < 0 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Cannot seek to a negative position", - )); - } - - self.seek_to(new_tb_pos as u64)?; - - Ok(()) - } - - pub fn current_byte_bit_pos(&mut self) -> io::Result { - let position = self.data.stream_position()?; - if position == 0 { - return Ok(0); - } - - if self.is_aligned() { - Ok(position * 8) - } else { - Ok(position * 8 - 8 + self.bit_pos as u64) - } - } - - pub fn seek_to(&mut self, pos: u64) -> io::Result<()> { - self.data.seek(SeekFrom::Start(pos / 8))?; - - self.bit_pos = (pos % 8) as usize; - if self.bit_pos != 0 { - self.current_byte = self.data.read_u8()?; - } - - Ok(()) - } -} - -impl BitReader { - pub fn new(data: T) -> Self { - Self { - data, - bit_pos: 0, - current_byte: 0, - } - } - - pub fn read_bit(&mut self) -> io::Result { - if self.is_aligned() { - self.current_byte = self.data.read_u8()?; - } - - let bit = (self.current_byte >> (7 - self.bit_pos)) & 1; - - self.bit_pos += 1; - self.bit_pos %= 8; - - Ok(bit == 1) - } - - pub fn read_bits(&mut self, count: u8) -> io::Result { - let mut bits = 0; - for _ in 0..count { - let bit = self.read_bit()?; - bits <<= 1; - bits |= bit as u64; - } - - Ok(bits) - } - - pub fn into_inner(self) -> T { - self.data - } - - pub fn get_ref(&self) -> &T { - &self.data - } - - pub fn get_mut(&mut self) -> &mut T { - &mut self.data - } - - pub fn get_bit_pos(&self) -> usize { - self.bit_pos - } - - pub fn align(&mut self) -> io::Result<()> { - let amount_to_read = 8 - self.bit_pos; - self.read_bits(amount_to_read as u8)?; - Ok(()) - } - - pub fn is_aligned(&self) -> bool { - self.bit_pos == 0 - } -} - -impl io::Read for BitReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - if self.is_aligned() { - return self.data.read(buf); - } - - let mut read = 0; - for b in buf { - let mut byte = 0; - for _ in 0..8 { - let bit = self.read_bit()?; - byte <<= 1; - byte |= bit as u8; - } - *b = byte; - read += 1; - } - - Ok(read) - } -} - -impl> BitReader> { - pub fn is_empty(&self) -> bool { - self.data.position() as usize == self.data.get_ref().as_ref().len() - } - - pub fn remaining_bits(&self) -> usize { - let remaining = self.data.remaining(); - - if self.is_aligned() { - remaining * 8 - } else { - remaining * 8 + 8 - self.bit_pos - } - } -} - -impl io::Seek for BitReader { - fn seek(&mut self, pos: io::SeekFrom) -> io::Result { - match pos { - io::SeekFrom::Start(pos) => { - self.seek_to(pos * 8)?; - } - io::SeekFrom::Current(pos) => { - self.seek_bits(pos * 8)?; - } - io::SeekFrom::End(pos) => { - let end = self.data.seek(io::SeekFrom::End(0))? as i64; - self.seek_to((end + pos) as u64 * 8)?; - } - } - - self.data.stream_position() - } -} diff --git a/crates/bytesio/src/bit_writer.rs b/crates/bytesio/src/bit_writer.rs deleted file mode 100644 index 5dcb2bb69..000000000 --- a/crates/bytesio/src/bit_writer.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::io; - -#[derive(Default, Clone, Debug)] -pub struct BitWriter { - data: Vec, - bit_pos: usize, -} - -impl BitWriter { - pub fn write_bit(&mut self, bit: bool) -> io::Result<()> { - let byte_pos = self.bit_pos / 8; - let bit_pos = self.bit_pos % 8; - - if byte_pos >= self.data.len() { - self.data.push(0); - } - - let byte = &mut self.data[byte_pos]; - if bit { - *byte |= 1 << (7 - bit_pos); - } else { - *byte &= !(1 << (7 - bit_pos)); - } - - self.bit_pos += 1; - - Ok(()) - } - - pub fn write_bits(&mut self, bits: u64, count: usize) -> io::Result<()> { - for i in 0..count { - let bit = (bits >> (count - i - 1)) & 1 == 1; - self.write_bit(bit)?; - } - - Ok(()) - } - - pub fn into_inner(self) -> Vec { - self.data - } - - pub fn get_ref(&self) -> &Vec { - &self.data - } - - pub fn get_mut(&mut self) -> &mut Vec { - &mut self.data - } - - pub fn get_bit_pos(&self) -> usize { - self.bit_pos - } - - pub fn is_aligned(&self) -> bool { - self.bit_pos % 8 == 0 - } - - pub fn align(&mut self) -> io::Result<()> { - if !self.is_aligned() { - self.write_bits(0, 8 - (self.bit_pos % 8))?; - } - - Ok(()) - } - - pub fn seek_bits(&mut self, count: i64) { - if count < 0 { - if self.bit_pos < (-count) as usize { - self.bit_pos = 0; - } else { - self.bit_pos -= (-count) as usize; - } - } else if self.bit_pos + count as usize >= self.data.len() * 8 { - self.bit_pos = self.data.len() * 8; - } else { - self.bit_pos += count as usize; - } - } - - pub fn seek_to(&mut self, pos: usize) { - if pos >= self.data.len() * 8 { - self.bit_pos = self.data.len() * 8; - } else { - self.bit_pos = pos; - } - } -} - -impl io::Write for BitWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - for b in buf { - self.write_bits(*b as u64, 8)?; - } - - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl io::Seek for BitWriter { - fn seek(&mut self, pos: io::SeekFrom) -> io::Result { - match pos { - io::SeekFrom::Start(pos) => self.seek_to((pos * 8) as usize), - io::SeekFrom::Current(pos) => self.seek_bits(pos * 8), - io::SeekFrom::End(pos) => self.seek_to((self.data.len() as i64 + pos) as usize * 8), - }; - - Ok(self.bit_pos as u64) - } -} diff --git a/crates/bytesio/src/bytes_writer.rs b/crates/bytesio/src/bytes_writer.rs deleted file mode 100644 index ef6afb762..000000000 --- a/crates/bytesio/src/bytes_writer.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::io; - -use bytes::{Bytes, BytesMut}; - -#[derive(Default)] -pub struct BytesWriter { - bytes: Vec, -} - -impl BytesWriter { - pub fn extract_current_bytes(&mut self) -> BytesMut { - let mut rv_data = BytesMut::new(); - rv_data.extend_from_slice(&self.bytes.clone()[..]); - self.bytes.clear(); - - rv_data - } - - pub fn get_current_bytes(&mut self) -> BytesMut { - let mut rv_data = BytesMut::new(); - rv_data.extend_from_slice(&self.bytes[..]); - - rv_data - } - - pub fn dispose(self) -> Bytes { - self.bytes.into() - } -} - -impl io::Write for BytesWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.bytes.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.bytes.flush() - } -} diff --git a/crates/bytesio/src/lib.rs b/crates/bytesio/src/lib.rs index f5863290a..f9054685c 100644 --- a/crates/bytesio/src/lib.rs +++ b/crates/bytesio/src/lib.rs @@ -1,7 +1,4 @@ -pub mod bit_reader; -pub mod bit_writer; pub mod bytes_reader; -pub mod bytes_writer; #[cfg(feature = "tokio")] pub mod bytesio; diff --git a/crates/bytesio/src/tests/bit_reader.rs b/crates/bytesio/src/tests/bit_reader.rs deleted file mode 100644 index 8c5d9f4e5..000000000 --- a/crates/bytesio/src/tests/bit_reader.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::io::{Read, Seek, SeekFrom}; - -use byteorder::ReadBytesExt; -use bytes::Bytes; - -use crate::bit_reader::BitReader; - -#[test] -fn test_bit_reader() { - let data = Bytes::from(vec![0b10110111, 0b01011000]); - - let mut reader = BitReader::from(data); - - assert!(reader.read_bit().unwrap()); - assert!(!reader.read_bit().unwrap()); - assert!(reader.read_bit().unwrap()); - assert!(reader.read_bit().unwrap()); - - assert_eq!(reader.read_bits(3).unwrap(), 0b011); - assert_eq!(reader.read_bits(8).unwrap(), 0b10101100); - assert_eq!(reader.read_bits(1).unwrap(), 0b0); - - assert!(reader.is_empty()); -} - -#[test] -fn test_bit_reader_read() { - let data = Bytes::from(vec![0b10110111, 0b01011000, 0b11111111]); - - let mut reader = BitReader::from(data); - - reader.seek_bits(1).unwrap(); - - let mut buf = [0u8; 2]; - assert_eq!(reader.read(&mut buf).unwrap(), 2); - assert_eq!(buf, [0b01101110, 0b10110001]); -} - -#[test] -fn test_bit_reader_read_ext() { - let data = Bytes::from(vec![0b10110111, 0b01011000, 0b11111111]); - - let mut reader = BitReader::from(data); - - reader.seek_bits(1).unwrap(); - - assert_eq!(reader.get_bit_pos(), 1); - - reader.seek_bits(3).unwrap(); - - assert_eq!(reader.get_bit_pos(), 4); - - let mut buf = [0u8; 2]; - assert_eq!(reader.read(&mut buf).unwrap(), 2); - assert_eq!(buf, [0b01110101, 0b10001111]); -} - -#[test] -fn test_bit_reader_seek() { - let data = Bytes::from(vec![0b10110111, 0b01011000, 0b11111111]); - - let mut reader = BitReader::from(data); - - reader.seek(SeekFrom::Start(1)).unwrap(); - - assert!(!reader.read_bit().unwrap()); - assert!(reader.read_bit().unwrap()); - assert!(!reader.read_bit().unwrap()); - - reader.seek_to(3).unwrap(); - reader.seek(SeekFrom::Current(1)).unwrap(); - - assert_eq!(reader.read_u8().unwrap(), 0b11000111); - - reader.seek(SeekFrom::End(-1)).unwrap(); - - assert_eq!(reader.read_u8().unwrap(), 0b11111111); -} - -#[test] -fn test_bit_reader_align() { - let data = Bytes::from(vec![0b10110111, 0b01011000, 0b11111111]); - - let mut reader = BitReader::from(data); - - reader.seek_bits(1).unwrap(); - - reader.align().unwrap(); - - assert!(reader.is_aligned()); - assert_eq!(reader.get_bit_pos(), 0); -} diff --git a/crates/bytesio/src/tests/bit_writer.rs b/crates/bytesio/src/tests/bit_writer.rs deleted file mode 100644 index 792bcc45b..000000000 --- a/crates/bytesio/src/tests/bit_writer.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::io::{Seek, SeekFrom, Write}; - -use crate::bit_writer::BitWriter; - -#[test] -fn test_bit_writer() { - let mut bit_writer = BitWriter::default(); - - bit_writer.write_bits(0b1, 1).unwrap(); // 1 - bit_writer.write_bits(0b010, 3).unwrap(); // 4 - bit_writer.write_bits(0b011, 3).unwrap(); // 7 - bit_writer.write_bits(0b00100, 5).unwrap(); // 12 - bit_writer.write_bits(0b00101, 5).unwrap(); // 17 - - let data = bit_writer.get_ref(); - - // 2 bytes + 1 bit - assert_eq!(data, &[0b10100110, 0b01000010, 0b10000000]); - - assert!(!bit_writer.is_aligned()); - - bit_writer.write_bits(0b1111000, 7).unwrap(); // 24 - - let data = bit_writer.get_ref(); - - // 3 bytes - assert_eq!(data, &[0b10100110, 0b01000010, 0b11111000]); - - assert!(bit_writer.is_aligned()); - - bit_writer.write_bits(0b1111000, 7).unwrap(); // 31 - - bit_writer.align().unwrap(); // 32 - - let data = bit_writer.get_ref(); - - // 4 bytes - assert_eq!(data, &[0b10100110, 0b01000010, 0b11111000, 0b11110000]); - - assert!(bit_writer.is_aligned()); - - bit_writer.write_bits(0b1, 1).unwrap(); // 33 - - let data = bit_writer.get_ref(); - - // 5 bytes - assert_eq!(data, &[0b10100110, 0b01000010, 0b11111000, 0b11110000, 0b10000000]); -} - -#[test] -fn test_bit_writer_write() { - let mut bit_writer = BitWriter::default(); - - bit_writer.write_bit(true).unwrap(); // 1 - bit_writer - .write_all(&[0b00000001, 0b00000010, 0b00000011, 0b00000100]) - .unwrap(); // 33 - - let data = bit_writer.get_ref(); - - // 5 bytes - assert_eq!(data, &[0b10000000, 0b10000001, 0b00000001, 0b10000010, 0b0,]); -} - -#[test] -fn test_bit_writer_write_aligned() { - let mut bit_writer = BitWriter::default(); - - bit_writer.write_bit(true).unwrap(); // 1 - bit_writer.align().unwrap(); // 8 - bit_writer - .write_all(&[0b00000001, 0b00000010, 0b00000011, 0b00000100]) - .unwrap(); // 40 - - let data = bit_writer.get_ref(); - - // 5 bytes - assert_eq!(data, &[0b10000000, 0b00000001, 0b00000010, 0b00000011, 0b00000100,]); -} - -#[test] -fn test_bit_writer_seek() { - let mut bit_writer = BitWriter::default(); - - bit_writer.write_bits(0b1, 1).unwrap(); // 1 - bit_writer.write_bits(0b010, 3).unwrap(); // 4 - bit_writer.write_bits(0b011, 3).unwrap(); // 7 - bit_writer.write_bits(0b0, 1).unwrap(); // 8 - - let data = bit_writer.get_ref(); - - // 1 byte - assert_eq!(data, &[0b10100110]); - - bit_writer.seek(SeekFrom::Start(0)).unwrap(); - - bit_writer.write_bits(0b1, 1).unwrap(); // 1 - bit_writer.write_bits(0b111, 3).unwrap(); // 4 - bit_writer.write_bits(0b111, 3).unwrap(); // 7 - bit_writer.write_bits(0b10100, 5).unwrap(); // 12 - - let data = bit_writer.get_ref(); - - // 1 bytes + 4 bits - assert_eq!(data, &[0b11111111, 0b01000000]); - - bit_writer.seek_bits(-5); - - bit_writer.write_bits(0b0, 1).unwrap(); - - let data = bit_writer.get_ref(); - - // 1 bytes + 4 bits - assert_eq!(data, &[0b11111110, 0b01000000]); - - bit_writer.seek_to(4); - - bit_writer.write_bits(0b0, 1).unwrap(); - - let data = bit_writer.get_ref(); - - // 1 bytes + 4 bits - assert_eq!(data, &[0b11110110, 0b01000000]); -} diff --git a/crates/bytesio/src/tests/bytes_writer.rs b/crates/bytesio/src/tests/bytes_writer.rs deleted file mode 100644 index cd83a47a4..000000000 --- a/crates/bytesio/src/tests/bytes_writer.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::io::Write; - -use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; - -use crate::bytes_reader::BytesReader; -use crate::bytes_writer::BytesWriter; - -#[test] -fn test_byte_writer() { - let mut writer = BytesWriter::default(); - writer.write_u8(0x01).unwrap(); // 1 byte - writer.write_u16::(0x0203).unwrap(); // 2 bytes - writer.write_u24::(0x040506).unwrap(); // 3 bytes - writer.write_u32::(0x0708090a).unwrap(); // 4 bytes - writer.write_f64::(0.123456789).unwrap(); // 8 bytes - writer.write_all(&[0x0b, 0x0c, 0x0d, 0x0e, 0x0f]).unwrap(); // 5 bytes - - let bytes = writer.get_current_bytes(); - let mut reader = BytesReader::new(bytes); - assert_eq!(reader.read_u8().unwrap(), 0x01); - assert_eq!(reader.read_u16::().unwrap(), 0x0203); - assert_eq!(reader.read_u24::().unwrap(), 0x040506); - assert_eq!(reader.read_u32::().unwrap(), 0x0708090a); - assert_eq!(reader.read_f64::().unwrap(), 0.123456789); - assert_eq!(reader.read_bytes(5).unwrap().to_vec(), &[0x0b, 0x0c, 0x0d, 0x0e, 0x0f]); - - assert!(reader.is_empty()); - assert!(!writer.extract_current_bytes().is_empty()) -} diff --git a/crates/bytesio/src/tests/mod.rs b/crates/bytesio/src/tests/mod.rs index d58f8c5fd..ec7080b66 100644 --- a/crates/bytesio/src/tests/mod.rs +++ b/crates/bytesio/src/tests/mod.rs @@ -1,6 +1,3 @@ -mod bit_reader; -mod bit_writer; mod bytes_reader; -mod bytes_writer; mod bytesio; mod errors; diff --git a/crates/exp_golomb/Cargo.toml b/crates/exp_golomb/Cargo.toml index 00caa19a0..8d2fa5616 100644 --- a/crates/exp_golomb/Cargo.toml +++ b/crates/exp_golomb/Cargo.toml @@ -7,4 +7,5 @@ license = "MIT OR Apache-2.0" [dependencies] bytes = "1.5" bytesio = { path = "../bytesio" } +scuffle-bitio = { workspace = true } scuffle-workspace-hack.workspace = true diff --git a/crates/exp_golomb/src/lib.rs b/crates/exp_golomb/src/lib.rs index 3724a2f4f..b939fcfb1 100644 --- a/crates/exp_golomb/src/lib.rs +++ b/crates/exp_golomb/src/lib.rs @@ -1,9 +1,8 @@ use std::io; -use bytesio::bit_reader::BitReader; -use bytesio::bit_writer::BitWriter; +use scuffle_bitio::{BitReader, BitWriter}; -pub fn read_exp_golomb(reader: &mut BitReader) -> io::Result { +pub fn read_exp_golomb(reader: &mut BitReader) -> io::Result { let mut leading_zeros = 0; while !reader.read_bit()? { leading_zeros += 1; @@ -18,7 +17,7 @@ pub fn read_exp_golomb(reader: &mut BitReader) -> io::Result { Ok(result - 1) } -pub fn read_signed_exp_golomb(reader: &mut BitReader) -> io::Result { +pub fn read_signed_exp_golomb(reader: &mut BitReader) -> io::Result { let exp_glob = read_exp_golomb(reader)?; if exp_glob % 2 == 0 { @@ -28,7 +27,7 @@ pub fn read_signed_exp_golomb(reader: &mut BitReader) -> io::Result { } } -pub fn write_exp_golomb(writer: &mut BitWriter, input: u64) -> io::Result<()> { +pub fn write_exp_golomb(writer: &mut BitWriter, input: u64) -> io::Result<()> { let mut number = input + 1; let mut leading_zeros = 0; while number > 1 { @@ -45,7 +44,7 @@ pub fn write_exp_golomb(writer: &mut BitWriter, input: u64) -> io::Result<()> { Ok(()) } -pub fn write_signed_exp_golomb(writer: &mut BitWriter, number: i64) -> io::Result<()> { +pub fn write_signed_exp_golomb(writer: &mut BitWriter, number: i64) -> io::Result<()> { let number = if number <= 0 { -number as u64 * 2 } else { @@ -56,4 +55,217 @@ pub fn write_signed_exp_golomb(writer: &mut BitWriter, number: i64) -> io::Resul } #[cfg(test)] -mod tests; +mod tests { + use bytes::Buf; + use scuffle_bitio::{BitReader, BitWriter}; + + use crate::{read_exp_golomb, read_signed_exp_golomb, write_exp_golomb, write_signed_exp_golomb}; + + pub fn get_remaining_bits(reader: &BitReader>>) -> usize { + let remaining = reader.get_ref().remaining(); + + if reader.is_aligned() { + remaining * 8 + } else { + remaining * 8 + (8 - reader.bit_pos() as usize) + } + } + + #[test] + fn test_exp_glob_decode() { + let mut bit_writer = BitWriter::>::default(); + + bit_writer.write_bits(0b1, 1).unwrap(); // 0 + bit_writer.write_bits(0b010, 3).unwrap(); // 1 + bit_writer.write_bits(0b011, 3).unwrap(); // 2 + bit_writer.write_bits(0b00100, 5).unwrap(); // 3 + bit_writer.write_bits(0b00101, 5).unwrap(); // 4 + bit_writer.write_bits(0b00110, 5).unwrap(); // 5 + bit_writer.write_bits(0b00111, 5).unwrap(); // 6 + + let data = bit_writer.finish().unwrap(); + + let mut bit_reader = BitReader::new(std::io::Cursor::new(data)); + + let remaining_bits = get_remaining_bits(&bit_reader); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 0); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 1); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 1); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 4); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 2); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 7); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 3); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 12); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 4); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 17); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 5); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 22); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 6); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 27); + } + + #[test] + fn test_signed_exp_glob_decode() { + let mut bit_writer = BitWriter::>::default(); + + bit_writer.write_bits(0b1, 1).unwrap(); // 0 + bit_writer.write_bits(0b010, 3).unwrap(); // 1 + bit_writer.write_bits(0b011, 3).unwrap(); // -1 + bit_writer.write_bits(0b00100, 5).unwrap(); // 2 + bit_writer.write_bits(0b00101, 5).unwrap(); // -2 + bit_writer.write_bits(0b00110, 5).unwrap(); // 3 + bit_writer.write_bits(0b00111, 5).unwrap(); // -3 + + let data = bit_writer.finish().unwrap(); + + let mut bit_reader = BitReader::new(std::io::Cursor::new(data)); + + let remaining_bits = get_remaining_bits(&bit_reader); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 0); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 1); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 1); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 4); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, -1); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 7); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 2); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 12); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, -2); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 17); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 3); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 22); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, -3); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 27); + } + + #[test] + fn test_exp_glob_encode() { + let mut bit_writer = BitWriter::>::default(); + + write_exp_golomb(&mut bit_writer, 0).unwrap(); + write_exp_golomb(&mut bit_writer, 1).unwrap(); + write_exp_golomb(&mut bit_writer, 2).unwrap(); + write_exp_golomb(&mut bit_writer, 3).unwrap(); + write_exp_golomb(&mut bit_writer, 4).unwrap(); + write_exp_golomb(&mut bit_writer, 5).unwrap(); + write_exp_golomb(&mut bit_writer, 6).unwrap(); + write_exp_golomb(&mut bit_writer, u64::MAX - 1).unwrap(); + + let data = bit_writer.finish().unwrap(); + + let mut bit_reader = BitReader::new(std::io::Cursor::new(data)); + + let remaining_bits = get_remaining_bits(&bit_reader); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 0); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 1); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 1); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 4); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 2); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 7); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 3); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 12); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 4); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 17); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 5); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 22); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 6); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 27); + + let result = read_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, u64::MAX - 1); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 154); + } + + #[test] + fn test_signed_exp_glob_encode() { + let mut bit_writer = BitWriter::>::default(); + + write_signed_exp_golomb(&mut bit_writer, 0).unwrap(); + write_signed_exp_golomb(&mut bit_writer, 1).unwrap(); + write_signed_exp_golomb(&mut bit_writer, -1).unwrap(); + write_signed_exp_golomb(&mut bit_writer, 2).unwrap(); + write_signed_exp_golomb(&mut bit_writer, -2).unwrap(); + write_signed_exp_golomb(&mut bit_writer, 3).unwrap(); + write_signed_exp_golomb(&mut bit_writer, -3).unwrap(); + write_signed_exp_golomb(&mut bit_writer, i64::MAX).unwrap(); + + let data = bit_writer.finish().unwrap(); + + let mut bit_reader = BitReader::new(std::io::Cursor::new(data)); + + let remaining_bits = get_remaining_bits(&bit_reader); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 0); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 1); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 1); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 4); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, -1); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 7); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 2); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 12); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, -2); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 17); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, 3); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 22); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, -3); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 27); + + let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); + assert_eq!(result, i64::MAX); + assert_eq!(get_remaining_bits(&bit_reader), remaining_bits - 154); + } +} diff --git a/crates/exp_golomb/src/tests.rs b/crates/exp_golomb/src/tests.rs deleted file mode 100644 index 37b91c234..000000000 --- a/crates/exp_golomb/src/tests.rs +++ /dev/null @@ -1,202 +0,0 @@ -use bytesio::bit_reader::BitReader; -use bytesio::bit_writer::BitWriter; - -use crate::{read_exp_golomb, read_signed_exp_golomb, write_exp_golomb, write_signed_exp_golomb}; - -#[test] -fn test_exp_glob_decode() { - let mut bit_writer = BitWriter::default(); - - bit_writer.write_bits(0b1, 1).unwrap(); // 0 - bit_writer.write_bits(0b010, 3).unwrap(); // 1 - bit_writer.write_bits(0b011, 3).unwrap(); // 2 - bit_writer.write_bits(0b00100, 5).unwrap(); // 3 - bit_writer.write_bits(0b00101, 5).unwrap(); // 4 - bit_writer.write_bits(0b00110, 5).unwrap(); // 5 - bit_writer.write_bits(0b00111, 5).unwrap(); // 6 - - let data = bit_writer.into_inner(); - - let mut bit_reader = BitReader::from(data); - - let remaining_bits = bit_reader.remaining_bits(); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 0); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 1); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 1); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 4); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 2); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 7); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 3); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 12); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 4); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 17); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 5); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 22); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 6); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 27); -} - -#[test] -fn test_signed_exp_glob_decode() { - let mut bit_writer = BitWriter::default(); - - bit_writer.write_bits(0b1, 1).unwrap(); // 0 - bit_writer.write_bits(0b010, 3).unwrap(); // 1 - bit_writer.write_bits(0b011, 3).unwrap(); // -1 - bit_writer.write_bits(0b00100, 5).unwrap(); // 2 - bit_writer.write_bits(0b00101, 5).unwrap(); // -2 - bit_writer.write_bits(0b00110, 5).unwrap(); // 3 - bit_writer.write_bits(0b00111, 5).unwrap(); // -3 - - let data = bit_writer.into_inner(); - - let mut bit_reader = BitReader::from(data); - - let remaining_bits = bit_reader.remaining_bits(); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 0); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 1); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 1); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 4); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, -1); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 7); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 2); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 12); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, -2); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 17); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 3); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 22); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, -3); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 27); -} - -#[test] -fn test_exp_glob_encode() { - let mut bit_writer = BitWriter::default(); - - write_exp_golomb(&mut bit_writer, 0).unwrap(); - write_exp_golomb(&mut bit_writer, 1).unwrap(); - write_exp_golomb(&mut bit_writer, 2).unwrap(); - write_exp_golomb(&mut bit_writer, 3).unwrap(); - write_exp_golomb(&mut bit_writer, 4).unwrap(); - write_exp_golomb(&mut bit_writer, 5).unwrap(); - write_exp_golomb(&mut bit_writer, 6).unwrap(); - write_exp_golomb(&mut bit_writer, u64::MAX - 1).unwrap(); - - let data = bit_writer.into_inner(); - - let mut bit_reader = BitReader::from(data); - - let remaining_bits = bit_reader.remaining_bits(); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 0); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 1); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 1); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 4); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 2); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 7); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 3); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 12); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 4); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 17); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 5); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 22); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 6); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 27); - - let result = read_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, u64::MAX - 1); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 154); -} - -#[test] -fn test_signed_exp_glob_encode() { - let mut bit_writer = BitWriter::default(); - - write_signed_exp_golomb(&mut bit_writer, 0).unwrap(); - write_signed_exp_golomb(&mut bit_writer, 1).unwrap(); - write_signed_exp_golomb(&mut bit_writer, -1).unwrap(); - write_signed_exp_golomb(&mut bit_writer, 2).unwrap(); - write_signed_exp_golomb(&mut bit_writer, -2).unwrap(); - write_signed_exp_golomb(&mut bit_writer, 3).unwrap(); - write_signed_exp_golomb(&mut bit_writer, -3).unwrap(); - write_signed_exp_golomb(&mut bit_writer, i64::MAX).unwrap(); - - let data = bit_writer.into_inner(); - - let mut bit_reader = BitReader::from(data); - - let remaining_bits = bit_reader.remaining_bits(); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 0); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 1); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 1); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 4); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, -1); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 7); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 2); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 12); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, -2); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 17); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, 3); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 22); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, -3); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 27); - - let result = read_signed_exp_golomb(&mut bit_reader).unwrap(); - assert_eq!(result, i64::MAX); - assert_eq!(bit_reader.remaining_bits(), remaining_bits - 154); -} diff --git a/crates/flv/Cargo.toml b/crates/flv/Cargo.toml index cf27a0795..d307eef5b 100644 --- a/crates/flv/Cargo.toml +++ b/crates/flv/Cargo.toml @@ -14,6 +14,7 @@ bytesio = { path = "../bytesio" } av1 = { path = "../av1" } h264 = { path = "../h264" } h265 = { path = "../h265" } -aac = { path = "../aac" } +scuffle-aac = { path = "../aac" } amf0 = { path = "../amf0" } scuffle-workspace-hack.workspace = true +scuffle-bitio = { workspace = true } diff --git a/crates/flv/src/tests/demuxer.rs b/crates/flv/src/tests/demuxer.rs index 94cde40f6..3dfab1322 100644 --- a/crates/flv/src/tests/demuxer.rs +++ b/crates/flv/src/tests/demuxer.rs @@ -1,12 +1,12 @@ use std::io; use std::path::PathBuf; -use aac::{AudioObjectType, AudioSpecificConfig}; use av1::seq::SequenceHeaderObu; use av1::ObuHeader; use bytes::Bytes; -use bytesio::bit_reader::BitReader; use h264::{Sps, SpsExtended}; +use scuffle_aac::{AudioObjectType, PartialAudioSpecificConfig}; +use scuffle_bitio::BitReader; use crate::{ AacPacket, Av1Packet, AvcPacket, EnhancedPacket, Flv, FlvTagAudioData, FlvTagData, FlvTagVideoData, FrameType, @@ -239,7 +239,7 @@ fn test_demux_flv_avc_aac() { // The aac sequence header should be able to be decoded into an aac decoder // configuration record let aac_decoder_configuration_record = - AudioSpecificConfig::parse(data).expect("expected aac decoder configuration record"); + PartialAudioSpecificConfig::parse(&data).expect("expected aac decoder configuration record"); assert_eq!( aac_decoder_configuration_record.audio_object_type, @@ -448,7 +448,7 @@ fn test_demux_flv_av1_aac() { // The aac sequence header should be able to be decoded into an aac decoder // configuration record let aac_decoder_configuration_record = - AudioSpecificConfig::parse(data).expect("expected aac decoder configuration record"); + PartialAudioSpecificConfig::parse(&data).expect("expected aac decoder configuration record"); assert_eq!( aac_decoder_configuration_record.audio_object_type, @@ -486,7 +486,7 @@ fn test_demux_flv_av1_aac() { assert!(!config.twelve_bit); let (header, data) = - ObuHeader::parse(&mut BitReader::new(io::Cursor::new(&config.config_obu))).expect("expected obu header"); + ObuHeader::parse(&mut BitReader::new_from_slice(&config.config_obu)).expect("expected obu header"); let seq_obu = SequenceHeaderObu::parse(header, data).expect("expected sequence obu"); @@ -697,7 +697,7 @@ fn test_demux_flv_hevc_aac() { // The aac sequence header should be able to be decoded into an aac decoder // configuration record let aac_decoder_configuration_record = - AudioSpecificConfig::parse(data).expect("expected aac decoder configuration record"); + PartialAudioSpecificConfig::parse(&data).expect("expected aac decoder configuration record"); assert_eq!( aac_decoder_configuration_record.audio_object_type, diff --git a/crates/h264/Cargo.toml b/crates/h264/Cargo.toml index 11d1ac133..64980fdfc 100644 --- a/crates/h264/Cargo.toml +++ b/crates/h264/Cargo.toml @@ -9,4 +9,5 @@ bytes = "1.5" byteorder = "1.5" bytesio = { path = "../bytesio" } exp_golomb = { path = "../exp_golomb" } +scuffle-bitio = { workspace = true } scuffle-workspace-hack.workspace = true diff --git a/crates/h264/src/config.rs b/crates/h264/src/config.rs index 47f21863e..2b27c51ff 100644 --- a/crates/h264/src/config.rs +++ b/crates/h264/src/config.rs @@ -4,8 +4,8 @@ use std::io::{ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use bytes::{Buf, Bytes}; -use bytesio::bit_writer::BitWriter; use bytesio::bytes_reader::BytesCursor; +use scuffle_bitio::BitWriter; #[derive(Debug, Clone, PartialEq)] /// AVC (H.264) Decoder Configuration Record @@ -132,7 +132,7 @@ impl AVCDecoderConfigurationRecord { } pub fn mux(&self, writer: &mut T) -> io::Result<()> { - let mut bit_writer = BitWriter::default(); + let mut bit_writer = BitWriter::new(writer); bit_writer.write_u8(self.configuration_version)?; bit_writer.write_u8(self.profile_indication)?; @@ -169,7 +169,7 @@ impl AVCDecoderConfigurationRecord { } } - writer.write_all(&bit_writer.into_inner())?; + bit_writer.finish()?; Ok(()) } diff --git a/crates/h264/src/sps.rs b/crates/h264/src/sps.rs index 1fd0333b2..a9bc77d3b 100644 --- a/crates/h264/src/sps.rs +++ b/crates/h264/src/sps.rs @@ -2,8 +2,8 @@ use std::io; use byteorder::{BigEndian, ReadBytesExt}; use bytes::Bytes; -use bytesio::bit_reader::BitReader; use exp_golomb::{read_exp_golomb, read_signed_exp_golomb}; +use scuffle_bitio::BitReader; #[derive(Debug, Clone, PartialEq)] /// Sequence parameter set @@ -46,7 +46,7 @@ impl Sps { } } - let mut bit_reader = BitReader::from(vec); + let mut bit_reader = BitReader::new_from_slice(vec); let forbidden_zero_bit = bit_reader.read_bit()?; if forbidden_zero_bit { @@ -209,15 +209,15 @@ pub struct SpsExtended { } impl SpsExtended { - pub fn parse(reader: &mut BitReader) -> io::Result { + pub fn parse(reader: &mut BitReader) -> io::Result { let chroma_format_idc = read_exp_golomb(reader)?; if chroma_format_idc == 3 { - reader.seek_bits(1)?; + reader.read_bit()?; } let bit_depth_luma_minus8 = read_exp_golomb(reader)?; let bit_depth_chroma_minus8 = read_exp_golomb(reader)?; - reader.seek_bits(1)?; // qpprime_y_zero_transform_bypass_flag + reader.read_bit()?; // qpprime_y_zero_transform_bypass_flag if reader.read_bit()? { // seq_scaling_matrix_present_flag diff --git a/crates/h265/Cargo.toml b/crates/h265/Cargo.toml index 0764dc3ac..07086c54a 100644 --- a/crates/h265/Cargo.toml +++ b/crates/h265/Cargo.toml @@ -9,4 +9,5 @@ bytes = "1.5" byteorder = "1.5" bytesio = { path = "../bytesio" } exp_golomb = { path = "../exp_golomb" } +scuffle-bitio = { workspace = true } scuffle-workspace-hack.workspace = true diff --git a/crates/h265/src/config.rs b/crates/h265/src/config.rs index 86ca82ad4..1a0d11633 100644 --- a/crates/h265/src/config.rs +++ b/crates/h265/src/config.rs @@ -4,8 +4,7 @@ use std::io::{ use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; use bytes::Bytes; -use bytesio::bit_reader::BitReader; -use bytesio::bit_writer::BitWriter; +use scuffle_bitio::{BitReader, BitWriter}; #[derive(Debug, Clone, PartialEq)] /// HEVC Decoder Configuration Record @@ -180,7 +179,7 @@ impl HEVCDecoderConfigurationRecord { } pub fn mux(&self, writer: &mut T) -> io::Result<()> { - let mut bit_writer = BitWriter::default(); + let mut bit_writer = BitWriter::new(writer); bit_writer.write_u8(self.configuration_version)?; bit_writer.write_bits(self.general_profile_space as u64, 2)?; @@ -226,7 +225,7 @@ impl HEVCDecoderConfigurationRecord { } } - writer.write_all(&bit_writer.into_inner())?; + bit_writer.finish()?; Ok(()) } diff --git a/crates/h265/src/sps.rs b/crates/h265/src/sps.rs index eab551898..bf67d0f1a 100644 --- a/crates/h265/src/sps.rs +++ b/crates/h265/src/sps.rs @@ -2,8 +2,8 @@ use std::io; use byteorder::ReadBytesExt; use bytes::Bytes; -use bytesio::bit_reader::BitReader; use exp_golomb::{read_exp_golomb, read_signed_exp_golomb}; +use scuffle_bitio::BitReader; #[derive(Debug, Clone, PartialEq)] /// Sequence parameter set @@ -41,7 +41,7 @@ impl Sps { } } - let mut bit_reader = BitReader::from(vec); + let mut bit_reader = BitReader::new_from_slice(vec); let forbidden_zero_bit = bit_reader.read_bit()?; if forbidden_zero_bit { @@ -66,15 +66,15 @@ impl Sps { bit_reader.seek_bits(1)?; // sps_temporal_id_nesting_flag { bit_reader.seek_bits( - 2 // general_profile_space - + 1 // general_tier_flag - + 5 // general_profile_idc - + 32 // general_profile_compatibility_flag - + 1 // general_progressive_source_flag - + 1 // general_interlaced_source_flag - + 1 // general_non_packed_constraint_flag - + 1 // general_frame_only_constraint_flag - + 43 // general_reserved_zero_43bits + 2 // general_profile_space + + 1 // general_tier_flag + + 5 // general_profile_idc + + 32 // general_profile_compatibility_flag + + 1 // general_progressive_source_flag + + 1 // general_interlaced_source_flag + + 1 // general_non_packed_constraint_flag + + 1 // general_frame_only_constraint_flag + + 43 // general_reserved_zero_43bits + 1 // general_reserved_zero_bit + 8, // general_level_idc )?; diff --git a/crates/mp4/Cargo.toml b/crates/mp4/Cargo.toml index ad2baaa5f..6c92d6b80 100644 --- a/crates/mp4/Cargo.toml +++ b/crates/mp4/Cargo.toml @@ -14,8 +14,9 @@ bytesio = { path = "../bytesio", default-features = false, features = []} h264 = { path = "../h264" } h265 = { path = "../h265" } av1 = { path = "../av1" } -aac = { path = "../aac" } +scuffle-aac = { path = "../aac" } scuffle-workspace-hack.workspace = true +scuffle-bitio = { workspace = true } [dev-dependencies] serde = { version = "1", features = ["derive"] } diff --git a/crates/mp4/src/boxes/types/av01.rs b/crates/mp4/src/boxes/types/av01.rs index 4b1dbc8e1..625d56a89 100644 --- a/crates/mp4/src/boxes/types/av01.rs +++ b/crates/mp4/src/boxes/types/av01.rs @@ -3,7 +3,7 @@ use std::io; use av1::seq::SequenceHeaderObu; use av1::{ObuHeader, ObuType}; use bytes::{Buf, Bytes}; -use bytesio::bit_reader::BitReader; +use scuffle_bitio::BitReader; use super::av1c::Av1C; use super::btrt::Btrt; @@ -36,7 +36,7 @@ impl Av01 { } pub fn codec(&self) -> io::Result { - let (header, data) = ObuHeader::parse(&mut BitReader::from(self.av1c.av1_config.config_obu.clone()))?; + let (header, data) = ObuHeader::parse(&mut BitReader::new_from_slice(&self.av1c.av1_config.config_obu))?; if header.obu_type != ObuType::SequenceHeader { return Err(io::Error::new( diff --git a/crates/mp4/src/boxes/types/mp4a.rs b/crates/mp4/src/boxes/types/mp4a.rs index c48719ade..486de823f 100644 --- a/crates/mp4/src/boxes/types/mp4a.rs +++ b/crates/mp4/src/boxes/types/mp4a.rs @@ -41,7 +41,7 @@ impl Mp4a { .as_ref() .and_then(|c| c.decoder_specific_info.as_ref().map(|c| c.data.clone())) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Missing decoder specific info"))?; - let aac_config = aac::AudioSpecificConfig::parse(info)?; + let aac_config = scuffle_aac::PartialAudioSpecificConfig::parse(&info)?; Ok(AudioCodec::Aac { object_type: aac_config.audio_object_type, diff --git a/crates/mp4/src/codec.rs b/crates/mp4/src/codec.rs index 37cbdb335..dfb1651a5 100644 --- a/crates/mp4/src/codec.rs +++ b/crates/mp4/src/codec.rs @@ -1,7 +1,7 @@ use std::fmt; use std::str::FromStr; -use aac::AudioObjectType; +use scuffle_aac::AudioObjectType; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum VideoCodec { diff --git a/crates/rtmp/src/chunk/encoder.rs b/crates/rtmp/src/chunk/encoder.rs index afe0721bf..4c9fc8651 100644 --- a/crates/rtmp/src/chunk/encoder.rs +++ b/crates/rtmp/src/chunk/encoder.rs @@ -1,7 +1,6 @@ -use std::io::Write; +use std::io; use byteorder::{BigEndian, LittleEndian, WriteBytesExt}; -use bytesio::bytes_writer::BytesWriter; use super::define::{Chunk, ChunkMessageHeader, ChunkType, INIT_CHUNK_SIZE}; use super::errors::ChunkEncodeError; @@ -24,7 +23,7 @@ impl ChunkEncoder { } /// Internal function to write the basic header. - fn write_basic_header(writer: &mut BytesWriter, fmt: ChunkType, csid: u32) -> Result<(), ChunkEncodeError> { + fn write_basic_header(writer: &mut impl io::Write, fmt: ChunkType, csid: u32) -> Result<(), ChunkEncodeError> { let fmt = fmt as u8; if csid >= 64 + 255 { @@ -46,7 +45,10 @@ impl ChunkEncoder { Ok(()) } - fn write_message_header(writer: &mut BytesWriter, message_header: &ChunkMessageHeader) -> Result<(), ChunkEncodeError> { + fn write_message_header( + writer: &mut impl io::Write, + message_header: &ChunkMessageHeader, + ) -> Result<(), ChunkEncodeError> { let timestamp = if message_header.timestamp >= 0xFFFFFF { 0xFFFFFF } else { @@ -65,13 +67,13 @@ impl ChunkEncoder { Ok(()) } - fn write_extened_timestamp(writer: &mut BytesWriter, timestamp: u32) -> Result<(), ChunkEncodeError> { + fn write_extened_timestamp(writer: &mut impl io::Write, timestamp: u32) -> Result<(), ChunkEncodeError> { writer.write_u32::(timestamp)?; Ok(()) } - pub fn write_chunk(&self, writer: &mut BytesWriter, mut chunk_info: Chunk) -> Result<(), ChunkEncodeError> { + pub fn write_chunk(&self, writer: &mut impl io::Write, mut chunk_info: Chunk) -> Result<(), ChunkEncodeError> { Self::write_basic_header(writer, ChunkType::Type0, chunk_info.basic_header.chunk_stream_id)?; Self::write_message_header(writer, &chunk_info.message_header)?; diff --git a/crates/rtmp/src/chunk/tests/decoder.rs b/crates/rtmp/src/chunk/tests/decoder.rs index d868474f8..0242b1d9e 100644 --- a/crates/rtmp/src/chunk/tests/decoder.rs +++ b/crates/rtmp/src/chunk/tests/decoder.rs @@ -1,7 +1,6 @@ use std::io::Write; use byteorder::WriteBytesExt; -use bytesio::bytes_writer::BytesWriter; use crate::chunk::{ChunkDecodeError, ChunkDecoder}; @@ -96,7 +95,7 @@ fn test_decoder_chunk_type0_double_sized() { #[test] fn test_decoder_chunk_mutli_streams() { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); #[rustfmt::skip] writer @@ -129,7 +128,8 @@ fn test_decoder_chunk_mutli_streams() { } let mut unpacker = ChunkDecoder::default(); - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); + writer.clear(); // We wrote 2 chunks but neither of them are complete assert!(unpacker.read_chunk().expect("read chunk").is_none()); @@ -145,7 +145,8 @@ fn test_decoder_chunk_mutli_streams() { writer.write_u8(3).unwrap(); } - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); + writer.clear(); // Even though we wrote chunk 3 first, chunk 4 should be read first since it's a // different stream @@ -175,7 +176,8 @@ fn test_decoder_chunk_mutli_streams() { writer.write_u8(3).unwrap(); } - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); + writer.clear(); let chunk = unpacker.read_chunk().expect("read chunk").expect("chunk"); @@ -192,7 +194,8 @@ fn test_decoder_chunk_mutli_streams() { #[test] fn test_decoder_extended_timestamp() { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); + #[rustfmt::skip] writer .write_all(&[ @@ -210,7 +213,7 @@ fn test_decoder_extended_timestamp() { } let mut unpacker = ChunkDecoder::default(); - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); // We should not have enough data to read the chunk // But the chunk is valid, so we should not get an error @@ -255,7 +258,7 @@ fn test_decoder_extended_timestamp() { writer.write_u8(i as u8).unwrap(); } - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); let chunk = unpacker.read_chunk().expect("read chunk").expect("chunk"); @@ -269,7 +272,7 @@ fn test_decoder_extended_timestamp() { #[test] fn test_decoder_extended_timestamp_ext() { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); #[rustfmt::skip] writer @@ -288,7 +291,7 @@ fn test_decoder_extended_timestamp_ext() { } let mut unpacker = ChunkDecoder::default(); - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); // We should not have enough data to read the chunk // But the chunk is valid, so we should not get an error @@ -306,7 +309,7 @@ fn test_decoder_extended_timestamp_ext() { writer.write_u8(i as u8).unwrap(); } - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); let chunk = unpacker.read_chunk().expect("read chunk").expect("chunk"); @@ -320,7 +323,7 @@ fn test_decoder_extended_timestamp_ext() { #[test] fn test_read_extended_csid() { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); #[rustfmt::skip] writer @@ -335,7 +338,7 @@ fn test_read_extended_csid() { .unwrap(); let mut unpacker = ChunkDecoder::default(); - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); let chunk = unpacker.read_chunk().expect("read chunk").expect("chunk"); @@ -344,7 +347,7 @@ fn test_read_extended_csid() { #[test] fn test_read_extended_csid_ext2() { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); #[rustfmt::skip] writer @@ -360,7 +363,7 @@ fn test_read_extended_csid_ext2() { .unwrap(); let mut unpacker = ChunkDecoder::default(); - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); let chunk = unpacker.read_chunk().expect("read chunk").expect("chunk"); @@ -369,7 +372,7 @@ fn test_read_extended_csid_ext2() { #[test] fn test_decoder_error_no_previous_chunk() { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); // Write a chunk with type 3 but no previous chunk #[rustfmt::skip] @@ -380,7 +383,7 @@ fn test_decoder_error_no_previous_chunk() { .unwrap(); let mut unpacker = ChunkDecoder::default(); - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); let err = unpacker.read_chunk().unwrap_err(); match err { @@ -391,7 +394,7 @@ fn test_decoder_error_no_previous_chunk() { #[test] fn test_decoder_error_partial_chunk_too_large() { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); // Write a chunk that has a message size that is too large #[rustfmt::skip] @@ -407,7 +410,7 @@ fn test_decoder_error_partial_chunk_too_large() { .unwrap(); let mut unpacker = ChunkDecoder::default(); - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); let err = unpacker.read_chunk().unwrap_err(); match err { @@ -418,7 +421,7 @@ fn test_decoder_error_partial_chunk_too_large() { #[test] fn test_decoder_error_invalid_message_type_id() { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); // Write a chunk with an invalid message type id #[rustfmt::skip] @@ -434,7 +437,7 @@ fn test_decoder_error_invalid_message_type_id() { .unwrap(); let mut unpacker = ChunkDecoder::default(); - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); let err = unpacker.read_chunk().unwrap_err(); @@ -446,7 +449,7 @@ fn test_decoder_error_invalid_message_type_id() { #[test] fn test_decoder_error_too_many_partial_chunks() { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); let mut unpacker = ChunkDecoder::default(); @@ -468,7 +471,8 @@ fn test_decoder_error_too_many_partial_chunks() { writer.write_u8(i as u8).unwrap(); } - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); + writer.clear(); // Read the chunk assert!(unpacker @@ -494,8 +498,8 @@ fn test_decoder_error_too_many_partial_chunks() { writer.write_u8(i as u8).unwrap(); } - unpacker.extend_data(&writer.extract_current_bytes()); - + unpacker.extend_data(&writer); + writer.clear(); let err = unpacker.read_chunk().unwrap_err(); match err { ChunkDecodeError::TooManyPartialChunks => {} @@ -505,26 +509,20 @@ fn test_decoder_error_too_many_partial_chunks() { #[test] fn test_decoder_error_too_many_chunk_headers() { - let mut writer = BytesWriter::default(); - let mut unpacker = ChunkDecoder::default(); for i in 0..100 { // Write another chunk with a different chunk stream id #[rustfmt::skip] - writer - .write_all(&[ - (0 << 6), // chunk type 0 (partial), chunk stream id 0 - i, // chunk id - 0xFF, 0xFF, 0xFF, // timestamp - 0x00, 0x00, 0x00, // message length (max chunk size is set to 128) - 0x09, // message type id (video) - 0x00, 0x01, 0x00, 0x00, // message stream id - 0x01, 0x00, 0x00, 0x00, // extended timestamp - ]) - .unwrap(); - - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&[ + (0 << 6), // chunk type 0 (partial), chunk stream id 0 + i, // chunk id + 0xFF, 0xFF, 0xFF, // timestamp + 0x00, 0x00, 0x00, // message length (max chunk size is set to 128) + 0x09, // message type id (video) + 0x00, 0x01, 0x00, 0x00, // message stream id + 0x01, 0x00, 0x00, 0x00, // extended timestamp + ]); // Read the chunk (should be a full chunk since the message length is 0) assert!(unpacker @@ -535,18 +533,14 @@ fn test_decoder_error_too_many_chunk_headers() { // Write another chunk with a different chunk stream id #[rustfmt::skip] - writer - .write_all(&[ - 12, // chunk type 0, chunk stream id 6 - 0xFF, 0xFF, 0xFF, // timestamp - 0x00, 0x00, 0x00, // message length (max chunk size is set to 128) - 0x09, // message type id (video) - 0x00, 0x01, 0x00, 0x00, // message stream id - 0x01, 0x00, 0x00, 0x00, // extended timestamp - ]) - .unwrap(); - - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&[ + 12, // chunk type 0, chunk stream id 6 + 0xFF, 0xFF, 0xFF, // timestamp + 0x00, 0x00, 0x00, // message length (max chunk size is set to 128) + 0x09, // message type id (video) + 0x00, 0x01, 0x00, 0x00, // message stream id + 0x01, 0x00, 0x00, 0x00, // extended timestamp + ]); let err = unpacker.read_chunk().unwrap_err(); match err { @@ -557,7 +551,7 @@ fn test_decoder_error_too_many_chunk_headers() { #[test] fn test_decoder_larger_chunk_size() { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); // Write a chunk that has a message size that is too large #[rustfmt::skip] @@ -578,7 +572,7 @@ fn test_decoder_larger_chunk_size() { let mut unpacker = ChunkDecoder::default(); unpacker.update_max_chunk_size(4096); - unpacker.extend_data(&writer.extract_current_bytes()); + unpacker.extend_data(&writer); let chunk = unpacker.read_chunk().expect("failed").expect("chunk"); assert_eq!(chunk.basic_header.chunk_stream_id, 3); diff --git a/crates/rtmp/src/chunk/tests/encoder.rs b/crates/rtmp/src/chunk/tests/encoder.rs index ac98271c8..41919abc3 100644 --- a/crates/rtmp/src/chunk/tests/encoder.rs +++ b/crates/rtmp/src/chunk/tests/encoder.rs @@ -1,7 +1,6 @@ use std::io; use bytes::Bytes; -use bytesio::bytes_writer::BytesWriter; use crate::chunk::{Chunk, ChunkEncodeError, ChunkEncoder}; use crate::messages::MessageTypeID; @@ -18,7 +17,7 @@ fn test_encoder_error_display() { #[test] fn test_encoder_write_small_chunk() { let encoder = ChunkEncoder::default(); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); let chunk = Chunk::new( 0, @@ -30,26 +29,24 @@ fn test_encoder_write_small_chunk() { encoder.write_chunk(&mut writer, chunk).unwrap(); - let result = writer.dispose(); - #[rustfmt::skip] assert_eq!( - result, - Bytes::from(vec![ + writer, + vec![ (0x00 << 6), // chunk basic header - fmt: 0, csid: 0 0x00, 0x00, 0x00, // timestamp (0) 0x00, 0x00, 0x08, // message length (8 bytes) 0x02, // message type id (abort) 0x00, 0x00, 0x00, 0x00, // message stream id (0) 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // message payload - ]) + ] ); } #[test] fn test_encoder_write_large_chunk() { let encoder = ChunkEncoder::default(); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); let mut payload = Vec::new(); for i in 0..129 { @@ -60,8 +57,6 @@ fn test_encoder_write_large_chunk() { encoder.write_chunk(&mut writer, chunk).unwrap(); - let result = writer.dispose(); - #[rustfmt::skip] let mut expected = vec![ 0x0A, // chunk basic header - fmt: 0, csid: 10 (the format should have been fixed to 0) @@ -78,13 +73,13 @@ fn test_encoder_write_large_chunk() { expected.push((0x03 << 6) | 0x0A); // chunk basic header - fmt: 3, csid: 10 expected.push(128); // The rest of the payload should have been written - assert_eq!(result, Bytes::from(expected)); + assert_eq!(writer, expected); } #[test] fn test_encoder_extended_timestamp() { let encoder = ChunkEncoder::default(); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); let chunk = Chunk::new( 0, @@ -96,12 +91,10 @@ fn test_encoder_extended_timestamp() { encoder.write_chunk(&mut writer, chunk).unwrap(); - let result = writer.dispose(); - #[rustfmt::skip] assert_eq!( - result, - Bytes::from(vec![ + writer, + vec![ (0x00 << 6), // chunk basic header - fmt: 0, csid: 0 0xFF, 0xFF, 0xFF, // timestamp (0xFFFFFF) 0x00, 0x00, 0x08, // message length (8 bytes) @@ -111,14 +104,14 @@ fn test_encoder_extended_timestamp() { 0xFF, 0xFF, 0xFF, 0xFF, // extended timestamp (1) 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // message payload - ]) + ] ); } #[test] fn test_encoder_extended_timestamp_ext() { let encoder = ChunkEncoder::default(); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); let mut payload = Vec::new(); for i in 0..129 { @@ -129,8 +122,6 @@ fn test_encoder_extended_timestamp_ext() { encoder.write_chunk(&mut writer, chunk).unwrap(); - let result = writer.dispose(); - #[rustfmt::skip] let mut expected = vec![ (0x00 << 6), // chunk basic header - fmt: 0, csid: 0 @@ -149,13 +140,13 @@ fn test_encoder_extended_timestamp_ext() { expected.extend(vec![0xFF, 0xFF, 0xFF, 0xFF]); // extended timestamp expected.push(128); // The rest of the payload should have been written - assert_eq!(result, Bytes::from(expected)); + assert_eq!(writer, expected); } #[test] fn test_encoder_extended_csid() { let encoder = ChunkEncoder::default(); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); let chunk = Chunk::new( 64, @@ -167,12 +158,10 @@ fn test_encoder_extended_csid() { encoder.write_chunk(&mut writer, chunk).unwrap(); - let result = writer.dispose(); - #[rustfmt::skip] assert_eq!( - result, - Bytes::from(vec![ + writer, + vec![ (0x00 << 6), // chunk basic header - fmt: 0, csid: 0 0x00, // extended csid (64 + 0) = 64 0x00, 0x00, 0x00, // timestamp (0) @@ -180,14 +169,14 @@ fn test_encoder_extended_csid() { 0x02, // message type id (abort) 0x00, 0x00, 0x00, 0x00, // message stream id (0) 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // message payload - ]) + ] ); } #[test] fn test_encoder_extended_csid_ext() { let encoder = ChunkEncoder::default(); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); let chunk = Chunk::new( 320, @@ -199,12 +188,10 @@ fn test_encoder_extended_csid_ext() { encoder.write_chunk(&mut writer, chunk).unwrap(); - let result = writer.dispose(); - #[rustfmt::skip] assert_eq!( - result, - Bytes::from(vec![ + writer, + vec![ 0x01, // chunk basic header - fmt: 0, csid: 1 0x00, // extended csid (64 + 0) = 64 0x01, // extended csid (256 * 1) = 256 + 64 + 0 = 320 @@ -213,6 +200,6 @@ fn test_encoder_extended_csid_ext() { 0x02, // message type id (abort) 0x00, 0x00, 0x00, 0x00, // message stream id (0) 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // message payload - ]) + ] ); } diff --git a/crates/rtmp/src/handshake/server.rs b/crates/rtmp/src/handshake/server.rs index f128cfcad..acc9888f4 100644 --- a/crates/rtmp/src/handshake/server.rs +++ b/crates/rtmp/src/handshake/server.rs @@ -1,9 +1,8 @@ -use std::io::Write; +use std::io; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use bytes::{Bytes, BytesMut}; use bytesio::bytes_reader::BytesReader; -use bytesio::bytes_writer::BytesWriter; use rand::Rng; use super::define::{RtmpVersion, SchemaVersion, ServerHandshakeState}; @@ -75,7 +74,7 @@ impl SimpleHandshakeServer { self.reader.extend_from_slice(data); } - pub fn handshake(&mut self, writer: &mut BytesWriter) -> Result<(), HandshakeError> { + pub fn handshake(&mut self, writer: &mut impl io::Write) -> Result<(), HandshakeError> { loop { match self.state { ServerHandshakeState::ReadC0C1 => { @@ -155,7 +154,7 @@ impl SimpleHandshakeServer { } /// Defined in RTMP Specification 1.0 - 5.2.2 - fn write_s0(&self, writer: &mut BytesWriter) -> Result<(), HandshakeError> { + fn write_s0(&self, writer: &mut impl io::Write) -> Result<(), HandshakeError> { // Version (8 bits): In S0, this field identifies the RTMP // version selected by the server. The version defined by this // specification is 3. A server that does not recognize the @@ -167,7 +166,7 @@ impl SimpleHandshakeServer { } /// Defined in RTMP Specification 1.0 - 5.2.3 - fn write_s1(&self, writer: &mut BytesWriter) -> Result<(), HandshakeError> { + fn write_s1(&self, writer: &mut impl io::Write) -> Result<(), HandshakeError> { // Time (4 bytes): This field contains a timestamp, which SHOULD be // used as the epoch for all future chunks sent from this endpoint. // This may be 0, or some arbitrary value. To synchronize multiple @@ -192,7 +191,7 @@ impl SimpleHandshakeServer { Ok(()) } - fn write_s2(&self, writer: &mut BytesWriter) -> Result<(), HandshakeError> { + fn write_s2(&self, writer: &mut impl io::Write) -> Result<(), HandshakeError> { // Time (4 bytes): This field MUST contain the timestamp sent by the C1 (for // S2). writer.write_u32::(self.c1_timestamp)?; @@ -217,7 +216,7 @@ impl ComplexHandshakeServer { self.reader.extend_from_slice(data); } - pub fn handshake(&mut self, writer: &mut BytesWriter) -> Result<(), HandshakeError> { + pub fn handshake(&mut self, writer: &mut impl io::Write) -> Result<(), HandshakeError> { loop { match self.state { ServerHandshakeState::ReadC0C1 => { @@ -289,7 +288,7 @@ impl ComplexHandshakeServer { Ok(()) } - fn write_s0(&self, writer: &mut BytesWriter) -> Result<(), HandshakeError> { + fn write_s0(&self, writer: &mut impl io::Write) -> Result<(), HandshakeError> { // The version of the protocol used in the handshake. // This server is using version 3 of the protocol. writer.write_u8(self.version as u8)?; // 8 bits version @@ -297,8 +296,8 @@ impl ComplexHandshakeServer { Ok(()) } - fn write_s1(&self, main_writer: &mut BytesWriter) -> Result<(), HandshakeError> { - let mut writer = BytesWriter::default(); + fn write_s1(&self, main_writer: &mut impl io::Write) -> Result<(), HandshakeError> { + let mut writer = Vec::new(); // The first 4 bytes of S1 are the timestamp. writer.write_u32::(utils::current_time())?; @@ -314,7 +313,7 @@ impl ComplexHandshakeServer { // The digest is loaded with the data that we just generated. let data_digest = DigestProcessor::new( - writer.dispose(), + Bytes::from(writer), Bytes::from_static(define::RTMP_SERVER_KEY_FIRST_HALF.as_bytes()), ); @@ -331,8 +330,8 @@ impl ComplexHandshakeServer { Ok(()) } - fn write_s2(&self, main_writer: &mut BytesWriter) -> Result<(), HandshakeError> { - let mut writer = BytesWriter::default(); + fn write_s2(&self, main_writer: &mut impl io::Write) -> Result<(), HandshakeError> { + let mut writer = Vec::new(); // We write the current time to the first 4 bytes. writer.write_u32::(utils::current_time())?; @@ -357,7 +356,7 @@ impl ComplexHandshakeServer { // We then extract the first 1504 bytes of the data. // define::RTMP_HANDSHAKE_SIZE - 32 = 1504 // 32 is the size of the digest. for C2S2 - let data = &writer.dispose()[..define::RTMP_HANDSHAKE_SIZE - define::RTMP_DIGEST_LENGTH]; + let data = &writer[..define::RTMP_HANDSHAKE_SIZE - define::RTMP_DIGEST_LENGTH]; // Create a digest of the random data using a key generated from the digest of // C1. @@ -431,7 +430,7 @@ impl HandshakeServer { } } - pub fn handshake(&mut self, writer: &mut BytesWriter) -> Result<(), HandshakeError> { + pub fn handshake(&mut self, writer: &mut impl io::Write) -> Result<(), HandshakeError> { if self.is_complex { let result = self.complex_handshaker.handshake(writer); if result.is_err() { diff --git a/crates/rtmp/src/handshake/tests.rs b/crates/rtmp/src/handshake/tests.rs index 9fa158779..6c52b8d83 100644 --- a/crates/rtmp/src/handshake/tests.rs +++ b/crates/rtmp/src/handshake/tests.rs @@ -1,9 +1,7 @@ -use std::io::{Cursor, Write}; +use std::io::{Cursor, Read, Write}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use bytes::Bytes; -use bytesio::bytes_reader::BytesCursor; -use bytesio::bytes_writer::BytesWriter; use super::{HandshakeError, HandshakeServer}; use crate::handshake::define::{ @@ -31,22 +29,24 @@ fn test_simple_handshake() { handshake_server.extend_data(&c0c1.into_inner()); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); handshake_server.handshake(&mut writer).unwrap(); - let mut reader = Cursor::new(writer.dispose()); + let mut reader = Cursor::new(writer); assert_eq!(reader.read_u8().unwrap(), 3); // version let timestamp = reader.read_u32::().unwrap(); // timestamp assert_eq!(reader.read_u32::().unwrap(), 0); // zero - let server_random = reader.read_slice(1528).unwrap(); + let mut server_random = vec![0; 1528]; + reader.read_exact(&mut server_random).unwrap(); assert_eq!(reader.read_u32::().unwrap(), 123); // our timestamp let timestamp2 = reader.read_u32::().unwrap(); // server timestamp assert!(timestamp2 >= timestamp); - let read_client_random = reader.read_slice(1528).unwrap(); + let mut read_client_random = vec![0; 1528]; + reader.read_exact(&mut read_client_random).unwrap(); assert_eq!(&write_client_random, &read_client_random); @@ -57,7 +57,7 @@ fn test_simple_handshake() { handshake_server.extend_data(&c2.into_inner()); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); handshake_server.handshake(&mut writer).unwrap(); assert_eq!(handshake_server.state(), ServerHandshakeState::Finish) @@ -90,20 +90,21 @@ fn test_complex_handshake() { handshake_server.extend_data(&second); handshake_server.extend_data(&third); - let mut writer = BytesWriter::default(); - handshake_server.handshake(&mut writer).unwrap(); - - let bytes = writer.dispose(); + let mut bytes = Vec::new(); + handshake_server.handshake(&mut bytes).unwrap(); - let s0 = bytes.slice(0..1); - let s1 = bytes.slice(1..1537); - let s2 = bytes.slice(1537..3073); + let s0 = &bytes[0..1]; + let s1 = &bytes[1..1537]; + let s2 = &bytes[1537..3073]; assert_eq!(s0[0], 3); // version assert_ne!((&s1[..4]).read_u32::().unwrap(), 0); // timestamp should not be zero assert_eq!((&s1[4..8]).read_u32::().unwrap(), define::RTMP_SERVER_VERSION); // RTMP version - let data_digest = DigestProcessor::new(s1, Bytes::from_static(define::RTMP_SERVER_KEY_FIRST_HALF.as_bytes())); + let data_digest = DigestProcessor::new( + Bytes::copy_from_slice(s1), + Bytes::from_static(define::RTMP_SERVER_KEY_FIRST_HALF.as_bytes()), + ); let (digest, schema) = data_digest.read_digest().unwrap(); assert_eq!(schema, SchemaVersion::Schema1); @@ -115,7 +116,7 @@ fn test_complex_handshake() { let data_digest = DigestProcessor::new(Bytes::new(), key_digest.make_digest(&second, &[]).unwrap()); - assert_eq!(data_digest.make_digest(&s2[..1504], &[]).unwrap(), s2.slice(1504..)); + assert_eq!(data_digest.make_digest(&s2[..1504], &[]).unwrap(), s2[1504..]); let data_digest = DigestProcessor::new(Bytes::new(), key_digest.make_digest(&digest, &[]).unwrap()); @@ -129,7 +130,7 @@ fn test_complex_handshake() { handshake_server.extend_data(&c2); handshake_server.extend_data(&digest); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); handshake_server.handshake(&mut writer).unwrap(); assert_eq!(handshake_server.state(), ServerHandshakeState::Finish) diff --git a/crates/rtmp/src/messages/tests.rs b/crates/rtmp/src/messages/tests.rs index a10eb7d07..823caea3c 100644 --- a/crates/rtmp/src/messages/tests.rs +++ b/crates/rtmp/src/messages/tests.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use amf0::{Amf0ReadError, Amf0Value, Amf0Writer}; -use bytesio::bytes_writer::BytesWriter; +use bytes::Bytes; use super::{MessageError, MessageParser, MessageTypeID, RtmpMessageData}; use crate::chunk::{Chunk, ChunkEncodeError}; @@ -22,13 +22,15 @@ fn test_error_display() { #[test] fn test_parse_command() { - let mut amf0_writer = BytesWriter::default(); + let mut amf0_writer = Vec::new(); Amf0Writer::write_string(&mut amf0_writer, "connect").unwrap(); Amf0Writer::write_number(&mut amf0_writer, 1.0).unwrap(); Amf0Writer::write_null(&mut amf0_writer).unwrap(); - let chunk = Chunk::new(0, 0, MessageTypeID::CommandAMF0, 0, amf0_writer.dispose()); + let amf_data = Bytes::from(amf0_writer); + + let chunk = Chunk::new(0, 0, MessageTypeID::CommandAMF0, 0, amf_data); let message = MessageParser::parse(chunk).expect("no errors").expect("message"); match message { @@ -88,7 +90,7 @@ fn test_parse_set_chunk_size() { #[test] fn test_parse_metadata() { - let mut amf0_writer = BytesWriter::default(); + let mut amf0_writer = Vec::new(); Amf0Writer::write_string(&mut amf0_writer, "onMetaData").unwrap(); Amf0Writer::write_object( @@ -97,8 +99,7 @@ fn test_parse_metadata() { ) .unwrap(); - let amf_data = amf0_writer.dispose(); - + let amf_data = Bytes::from(amf0_writer); let chunk = Chunk::new(0, 0, MessageTypeID::DataAMF0, 0, amf_data.clone()); let message = MessageParser::parse(chunk).expect("no errors").expect("message"); diff --git a/crates/rtmp/src/netconnection/tests.rs b/crates/rtmp/src/netconnection/tests.rs index 32532df8f..785b072de 100644 --- a/crates/rtmp/src/netconnection/tests.rs +++ b/crates/rtmp/src/netconnection/tests.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use amf0::{Amf0Reader, Amf0Value, Amf0WriteError}; -use bytesio::bytes_writer::BytesWriter; use super::NetConnection; use crate::chunk::{ChunkDecoder, ChunkEncodeError, ChunkEncoder}; @@ -19,7 +18,7 @@ fn test_error_display() { #[test] fn test_netconnection_connect_response() { let encoder = ChunkEncoder::default(); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); NetConnection::write_connect_response( &encoder, @@ -35,7 +34,7 @@ fn test_netconnection_connect_response() { .unwrap(); let mut decoder = ChunkDecoder::default(); - decoder.extend_data(&writer.dispose()); + decoder.extend_data(&writer); let chunk = decoder.read_chunk().unwrap().unwrap(); assert_eq!(chunk.basic_header.chunk_stream_id, 0x03); @@ -69,12 +68,12 @@ fn test_netconnection_connect_response() { #[test] fn test_netconnection_create_stream_response() { let encoder = ChunkEncoder::default(); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); NetConnection::write_create_stream_response(&encoder, &mut writer, 1.0, 1.0).unwrap(); let mut decoder = ChunkDecoder::default(); - decoder.extend_data(&writer.dispose()); + decoder.extend_data(&writer); let chunk = decoder.read_chunk().unwrap().unwrap(); assert_eq!(chunk.basic_header.chunk_stream_id, 0x03); diff --git a/crates/rtmp/src/netconnection/writer.rs b/crates/rtmp/src/netconnection/writer.rs index 6048e862a..bf8b88bc7 100644 --- a/crates/rtmp/src/netconnection/writer.rs +++ b/crates/rtmp/src/netconnection/writer.rs @@ -1,7 +1,8 @@ use std::collections::HashMap; +use std::io; use amf0::{Amf0Value, Amf0Writer}; -use bytesio::bytes_writer::BytesWriter; +use bytes::Bytes; use super::errors::NetConnectionError; use crate::chunk::{Chunk, ChunkEncoder, DefinedChunkStreamID}; @@ -10,12 +11,10 @@ use crate::messages::MessageTypeID; pub struct NetConnection; impl NetConnection { - fn write_chunk(encoder: &ChunkEncoder, amf0: BytesWriter, writer: &mut BytesWriter) -> Result<(), NetConnectionError> { - let data = amf0.dispose(); - + fn write_chunk(encoder: &ChunkEncoder, amf0: Bytes, writer: &mut impl io::Write) -> Result<(), NetConnectionError> { encoder.write_chunk( writer, - Chunk::new(DefinedChunkStreamID::Command as u32, 0, MessageTypeID::CommandAMF0, 0, data), + Chunk::new(DefinedChunkStreamID::Command as u32, 0, MessageTypeID::CommandAMF0, 0, amf0), )?; Ok(()) @@ -24,7 +23,7 @@ impl NetConnection { #[allow(clippy::too_many_arguments)] pub fn write_connect_response( encoder: &ChunkEncoder, - writer: &mut BytesWriter, + writer: &mut impl io::Write, transaction_id: f64, fmsver: &str, capabilities: f64, @@ -33,7 +32,7 @@ impl NetConnection { description: &str, encoding: f64, ) -> Result<(), NetConnectionError> { - let mut amf0_writer = BytesWriter::default(); + let mut amf0_writer = Vec::new(); Amf0Writer::write_string(&mut amf0_writer, "_result")?; Amf0Writer::write_number(&mut amf0_writer, transaction_id)?; @@ -54,22 +53,22 @@ impl NetConnection { ]), )?; - Self::write_chunk(encoder, amf0_writer, writer) + Self::write_chunk(encoder, Bytes::from(amf0_writer), writer) } pub fn write_create_stream_response( encoder: &ChunkEncoder, - writer: &mut BytesWriter, + writer: &mut impl io::Write, transaction_id: f64, stream_id: f64, ) -> Result<(), NetConnectionError> { - let mut amf0_writer = BytesWriter::default(); + let mut amf0_writer = Vec::new(); Amf0Writer::write_string(&mut amf0_writer, "_result")?; Amf0Writer::write_number(&mut amf0_writer, transaction_id)?; Amf0Writer::write_null(&mut amf0_writer)?; Amf0Writer::write_number(&mut amf0_writer, stream_id)?; - Self::write_chunk(encoder, amf0_writer, writer) + Self::write_chunk(encoder, Bytes::from(amf0_writer), writer) } } diff --git a/crates/rtmp/src/netstream/tests.rs b/crates/rtmp/src/netstream/tests.rs index dd187cb92..f5c698829 100644 --- a/crates/rtmp/src/netstream/tests.rs +++ b/crates/rtmp/src/netstream/tests.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use amf0::{Amf0Reader, Amf0Value, Amf0WriteError}; -use bytesio::bytes_writer::BytesWriter; use crate::chunk::{ChunkDecoder, ChunkEncodeError, ChunkEncoder}; use crate::netstream::{NetStreamError, NetStreamWriter}; @@ -18,12 +17,12 @@ fn test_error_display() { #[test] fn test_netstream_write_on_status() { let encoder = ChunkEncoder::default(); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); NetStreamWriter::write_on_status(&encoder, &mut writer, 1.0, "status", "idk", "description").unwrap(); let mut decoder = ChunkDecoder::default(); - decoder.extend_data(&writer.dispose()); + decoder.extend_data(&writer); let chunk = decoder.read_chunk().unwrap().unwrap(); assert_eq!(chunk.basic_header.chunk_stream_id, 0x03); diff --git a/crates/rtmp/src/netstream/writer.rs b/crates/rtmp/src/netstream/writer.rs index 027bb6228..fb85e8631 100644 --- a/crates/rtmp/src/netstream/writer.rs +++ b/crates/rtmp/src/netstream/writer.rs @@ -1,7 +1,8 @@ use std::collections::HashMap; +use std::io; use amf0::{Amf0Value, Amf0Writer}; -use bytesio::bytes_writer::BytesWriter; +use bytes::Bytes; use super::errors::NetStreamError; use crate::chunk::{Chunk, ChunkEncoder, DefinedChunkStreamID}; @@ -10,16 +11,16 @@ use crate::messages::MessageTypeID; pub struct NetStreamWriter {} impl NetStreamWriter { - fn write_chunk( - encoder: &ChunkEncoder, - amf0_writer: BytesWriter, - writer: &mut BytesWriter, - ) -> Result<(), NetStreamError> { - let data = amf0_writer.dispose(); - + fn write_chunk(encoder: &ChunkEncoder, amf0_writer: Bytes, writer: &mut impl io::Write) -> Result<(), NetStreamError> { encoder.write_chunk( writer, - Chunk::new(DefinedChunkStreamID::Command as u32, 0, MessageTypeID::CommandAMF0, 0, data), + Chunk::new( + DefinedChunkStreamID::Command as u32, + 0, + MessageTypeID::CommandAMF0, + 0, + amf0_writer, + ), )?; Ok(()) @@ -27,13 +28,13 @@ impl NetStreamWriter { pub fn write_on_status( encoder: &ChunkEncoder, - writer: &mut BytesWriter, + writer: &mut impl io::Write, transaction_id: f64, level: &str, code: &str, description: &str, ) -> Result<(), NetStreamError> { - let mut amf0_writer = BytesWriter::default(); + let mut amf0_writer = Vec::new(); Amf0Writer::write_string(&mut amf0_writer, "onStatus")?; Amf0Writer::write_number(&mut amf0_writer, transaction_id)?; @@ -47,6 +48,6 @@ impl NetStreamWriter { ]), )?; - Self::write_chunk(encoder, amf0_writer, writer) + Self::write_chunk(encoder, Bytes::from(amf0_writer), writer) } } diff --git a/crates/rtmp/src/protocol_control_messages/tests.rs b/crates/rtmp/src/protocol_control_messages/tests.rs index bc2881f0b..4ef95dfc3 100644 --- a/crates/rtmp/src/protocol_control_messages/tests.rs +++ b/crates/rtmp/src/protocol_control_messages/tests.rs @@ -1,5 +1,3 @@ -use bytesio::bytes_writer::BytesWriter; - use crate::chunk::{ChunkDecoder, ChunkEncodeError, ChunkEncoder}; use crate::protocol_control_messages::{ ProtocolControlMessageError, ProtocolControlMessageReader, ProtocolControlMessagesWriter, @@ -24,12 +22,12 @@ fn test_reader_read_set_chunk_size() { #[test] fn test_writer_write_set_chunk_size() { let encoder = ChunkEncoder::default(); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); ProtocolControlMessagesWriter::write_set_chunk_size(&encoder, &mut writer, 1).unwrap(); let mut decoder = ChunkDecoder::default(); - decoder.extend_data(&writer.dispose()); + decoder.extend_data(&writer); let chunk = decoder.read_chunk().unwrap().unwrap(); assert_eq!(chunk.basic_header.chunk_stream_id, 0x02); @@ -41,12 +39,12 @@ fn test_writer_write_set_chunk_size() { #[test] fn test_writer_window_acknowledgement_size() { let encoder = ChunkEncoder::default(); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); ProtocolControlMessagesWriter::write_window_acknowledgement_size(&encoder, &mut writer, 1).unwrap(); let mut decoder = ChunkDecoder::default(); - decoder.extend_data(&writer.dispose()); + decoder.extend_data(&writer); let chunk = decoder.read_chunk().unwrap().unwrap(); assert_eq!(chunk.basic_header.chunk_stream_id, 0x02); @@ -58,12 +56,12 @@ fn test_writer_window_acknowledgement_size() { #[test] fn test_writer_set_peer_bandwidth() { let encoder = ChunkEncoder::default(); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); ProtocolControlMessagesWriter::write_set_peer_bandwidth(&encoder, &mut writer, 1, 2).unwrap(); let mut decoder = ChunkDecoder::default(); - decoder.extend_data(&writer.dispose()); + decoder.extend_data(&writer); let chunk = decoder.read_chunk().unwrap().unwrap(); assert_eq!(chunk.basic_header.chunk_stream_id, 0x02); diff --git a/crates/rtmp/src/protocol_control_messages/writer.rs b/crates/rtmp/src/protocol_control_messages/writer.rs index e223ae282..5b29a09c8 100644 --- a/crates/rtmp/src/protocol_control_messages/writer.rs +++ b/crates/rtmp/src/protocol_control_messages/writer.rs @@ -1,6 +1,7 @@ +use std::io; + use byteorder::{BigEndian, WriteBytesExt}; use bytes::Bytes; -use bytesio::bytes_writer::BytesWriter; use super::errors::ProtocolControlMessageError; use crate::chunk::{Chunk, ChunkEncoder}; @@ -11,7 +12,7 @@ pub struct ProtocolControlMessagesWriter; impl ProtocolControlMessagesWriter { pub fn write_set_chunk_size( encoder: &ChunkEncoder, - writer: &mut BytesWriter, + writer: &mut impl io::Write, chunk_size: u32, // 31 bits ) -> Result<(), ProtocolControlMessageError> { // According to spec the first bit must be 0. @@ -33,7 +34,7 @@ impl ProtocolControlMessagesWriter { pub fn write_window_acknowledgement_size( encoder: &ChunkEncoder, - writer: &mut BytesWriter, + writer: &mut impl io::Write, window_size: u32, ) -> Result<(), ProtocolControlMessageError> { encoder.write_chunk( @@ -52,7 +53,7 @@ impl ProtocolControlMessagesWriter { pub fn write_set_peer_bandwidth( encoder: &ChunkEncoder, - writer: &mut BytesWriter, + writer: &mut impl io::Write, window_size: u32, limit_type: u8, ) -> Result<(), ProtocolControlMessageError> { diff --git a/crates/rtmp/src/session/server_session.rs b/crates/rtmp/src/session/server_session.rs index 0657b829c..a497bfc02 100644 --- a/crates/rtmp/src/session/server_session.rs +++ b/crates/rtmp/src/session/server_session.rs @@ -3,7 +3,6 @@ use std::time::Duration; use amf0::Amf0Value; use bytes::Bytes; -use bytesio::bytes_writer::BytesWriter; use bytesio::bytesio::{AsyncReadWrite, BytesIO}; use bytesio::bytesio_errors::BytesIOError; use scuffle_future_ext::FutureExt; @@ -141,9 +140,9 @@ impl Session { handshaker.extend_data(&buf[..]); } - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); handshaker.handshake(&mut writer)?; - self.write_data(writer.dispose()).await?; + self.write_data(Bytes::from(writer)).await?; if handshaker.state() == ServerHandshakeState::Finish { let over_read = handshaker.extract_remaining_bytes(); @@ -239,10 +238,10 @@ impl Session { /// Set the server chunk size to the client async fn send_set_chunk_size(&mut self) -> Result<(), SessionError> { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); ProtocolControlMessagesWriter::write_set_chunk_size(&self.chunk_encoder, &mut writer, CHUNK_SIZE as u32)?; self.chunk_encoder.set_chunk_size(CHUNK_SIZE); - self.write_data(writer.dispose()).await?; + self.write_data(Bytes::from(writer)).await?; Ok(()) } @@ -336,7 +335,7 @@ impl Session { command_obj: HashMap, _others: Vec, ) -> Result<(), SessionError> { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); ProtocolControlMessagesWriter::write_window_acknowledgement_size( &self.chunk_encoder, @@ -382,7 +381,7 @@ impl Session { 0.0, )?; - self.write_data(writer.dispose()).await?; + self.write_data(Bytes::from(writer)).await?; Ok(()) } @@ -398,10 +397,10 @@ impl Session { _command_obj: HashMap, _others: Vec, ) -> Result<(), SessionError> { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); // 1.0 is the Stream ID of the stream we are creating NetConnection::write_create_stream_response(&self.chunk_encoder, &mut writer, transaction_id, 1.0)?; - self.write_data(writer.dispose()).await?; + self.write_data(Bytes::from(writer)).await?; Ok(()) } @@ -417,7 +416,7 @@ impl Session { _command_obj: HashMap, others: Vec, ) -> Result<(), SessionError> { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); let stream_id = match others.first() { Some(Amf0Value::Number(stream_id)) => *stream_id, @@ -438,7 +437,7 @@ impl Session { "", )?; - self.write_data(writer.dispose()).await?; + self.write_data(Bytes::from(writer)).await?; Ok(()) } @@ -488,7 +487,7 @@ impl Session { self.is_publishing = true; self.stream_id = stream_id; - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); EventMessagesWriter::write_stream_begin(&self.chunk_encoder, &mut writer, stream_id)?; NetStreamWriter::write_on_status( @@ -500,7 +499,7 @@ impl Session { "", )?; - self.write_data(writer.dispose()).await?; + self.write_data(Bytes::from(writer)).await?; Ok(()) } diff --git a/crates/rtmp/src/user_control_messages/tests.rs b/crates/rtmp/src/user_control_messages/tests.rs index 5682a6fd5..1e300b53c 100644 --- a/crates/rtmp/src/user_control_messages/tests.rs +++ b/crates/rtmp/src/user_control_messages/tests.rs @@ -1,5 +1,4 @@ use bytes::Bytes; -use bytesio::bytes_writer::BytesWriter; use crate::chunk::{ChunkDecoder, ChunkEncodeError, ChunkEncoder}; use crate::user_control_messages::{EventMessagesError, EventMessagesWriter}; @@ -12,13 +11,13 @@ fn test_error_display() { #[test] fn test_write_stream_begin() { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); let encoder = ChunkEncoder::default(); EventMessagesWriter::write_stream_begin(&encoder, &mut writer, 1).unwrap(); let mut decoder = ChunkDecoder::default(); - decoder.extend_data(&writer.dispose()); + decoder.extend_data(&writer); let chunk = decoder.read_chunk().unwrap().unwrap(); assert_eq!(chunk.basic_header.chunk_stream_id, 0x02); diff --git a/crates/rtmp/src/user_control_messages/writer.rs b/crates/rtmp/src/user_control_messages/writer.rs index b3e49cfc3..9517521c8 100644 --- a/crates/rtmp/src/user_control_messages/writer.rs +++ b/crates/rtmp/src/user_control_messages/writer.rs @@ -1,5 +1,6 @@ +use std::io; + use byteorder::{BigEndian, WriteBytesExt}; -use bytesio::bytes_writer::BytesWriter; use super::define; use super::errors::EventMessagesError; @@ -11,7 +12,7 @@ pub struct EventMessagesWriter; impl EventMessagesWriter { pub fn write_stream_begin( encoder: &ChunkEncoder, - writer: &mut BytesWriter, + writer: &mut impl io::Write, stream_id: u32, ) -> Result<(), EventMessagesError> { let mut data = Vec::new(); diff --git a/crates/transmuxer/Cargo.toml b/crates/transmuxer/Cargo.toml index 5bdc225d3..6b14ed937 100644 --- a/crates/transmuxer/Cargo.toml +++ b/crates/transmuxer/Cargo.toml @@ -12,11 +12,12 @@ bytesio = { path = "../bytesio" } h264 = { path = "../h264" } h265 = { path = "../h265" } av1 = { path = "../av1" } -aac = { path = "../aac" } +scuffle-aac = { path = "../aac" } amf0 = { path = "../amf0" } flv = { path = "../flv" } mp4 = { path = "../mp4" } scuffle-workspace-hack.workspace = true +scuffle-bitio = { workspace = true } [dev-dependencies] serde = { version = "1.0", features = ["derive"] } diff --git a/crates/transmuxer/src/codecs/aac.rs b/crates/transmuxer/src/codecs/aac.rs index 8738a8f50..0ea8ba797 100644 --- a/crates/transmuxer/src/codecs/aac.rs +++ b/crates/transmuxer/src/codecs/aac.rs @@ -1,4 +1,3 @@ -use aac::AudioSpecificConfig; use bytes::Bytes; use flv::{SoundSize, SoundType}; use mp4::types::esds::descriptor::header::DescriptorHeader; @@ -11,6 +10,7 @@ use mp4::types::mp4a::Mp4a; use mp4::types::stsd::{AudioSampleEntry, SampleEntry}; use mp4::types::trun::{TrunSample, TrunSampleFlag}; use mp4::DynBox; +use scuffle_aac::PartialAudioSpecificConfig; use crate::TransmuxError; @@ -18,8 +18,8 @@ pub fn stsd_entry( sound_size: SoundSize, sound_type: SoundType, data: Bytes, -) -> Result<(DynBox, AudioSpecificConfig), TransmuxError> { - let aac_config = aac::AudioSpecificConfig::parse(data)?; +) -> Result<(DynBox, PartialAudioSpecificConfig), TransmuxError> { + let aac_config = scuffle_aac::PartialAudioSpecificConfig::parse(&data)?; Ok(( Mp4a::new( @@ -47,7 +47,7 @@ pub fn stsd_entry( 0, // avg bitrate Some(DecoderSpecificInfoDescriptor { header: DescriptorHeader::new(DecoderSpecificInfoDescriptor::TAG), - data: aac_config.data.clone(), + data, }), )), None, diff --git a/crates/transmuxer/src/codecs/av1.rs b/crates/transmuxer/src/codecs/av1.rs index aaea93474..e026912df 100644 --- a/crates/transmuxer/src/codecs/av1.rs +++ b/crates/transmuxer/src/codecs/av1.rs @@ -1,7 +1,6 @@ use av1::seq::SequenceHeaderObu; use av1::{AV1CodecConfigurationRecord, ObuHeader, ObuType}; use bytes::Bytes; -use bytesio::bit_reader::BitReader; use flv::FrameType; use mp4::types::av01::Av01; use mp4::types::av1c::Av1C; @@ -9,11 +8,12 @@ use mp4::types::colr::{ColorType, Colr}; use mp4::types::stsd::{SampleEntry, VisualSampleEntry}; use mp4::types::trun::{TrunSample, TrunSampleFlag}; use mp4::DynBox; +use scuffle_bitio::BitReader; use crate::TransmuxError; pub fn stsd_entry(config: AV1CodecConfigurationRecord) -> Result<(DynBox, SequenceHeaderObu), TransmuxError> { - let (header, data) = ObuHeader::parse(&mut BitReader::from(config.config_obu.clone()))?; + let (header, data) = ObuHeader::parse(&mut BitReader::new_from_slice(&config.config_obu))?; if header.obu_type != ObuType::SequenceHeader { return Err(TransmuxError::InvalidAv1DecoderConfigurationRecord); diff --git a/crates/transmuxer/src/lib.rs b/crates/transmuxer/src/lib.rs index 60aa8efdd..a365769c9 100644 --- a/crates/transmuxer/src/lib.rs +++ b/crates/transmuxer/src/lib.rs @@ -7,7 +7,6 @@ use std::io; use amf0::Amf0Value; use byteorder::{BigEndian, ReadBytesExt}; use bytes::{Buf, Bytes}; -use bytesio::bytes_writer::BytesWriter; use flv::{ AacPacket, Av1Packet, AvcPacket, EnhancedPacket, FlvTag, FlvTagAudioData, FlvTagData, FlvTagVideoData, FrameType, HevcPacket, SoundType, @@ -103,7 +102,7 @@ impl Transmuxer { /// Get the next transmuxed packet. This will return `None` if there is not /// enough data to create a packet. pub fn mux(&mut self) -> Result, TransmuxError> { - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); let Some((video_settings, _)) = &self.settings else { let Some((video_settings, audio_settings)) = self.init_sequence(&mut writer)? else { @@ -118,10 +117,8 @@ impl Transmuxer { self.settings = Some((video_settings.clone(), audio_settings.clone())); - let data = writer.dispose(); - return Ok(Some(TransmuxResult::InitSegment { - data, + data: Bytes::from(writer), audio_settings, video_settings, })); @@ -267,7 +264,7 @@ impl Transmuxer { if is_audio { self.audio_duration += total_duration as u64; return Ok(Some(TransmuxResult::MediaSegment(MediaSegment { - data: writer.dispose(), + data: Bytes::from(writer), ty: MediaType::Audio, keyframe: false, timestamp: self.audio_duration - total_duration as u64, @@ -276,7 +273,7 @@ impl Transmuxer { self.video_duration += total_duration as u64; self.last_video_timestamp = tag.timestamp; return Ok(Some(TransmuxResult::MediaSegment(MediaSegment { - data: writer.dispose(), + data: Bytes::from(writer), ty: MediaType::Video, keyframe: is_keyframe, timestamp: self.video_duration - total_duration as u64, @@ -351,7 +348,10 @@ impl Transmuxer { } /// Create the init segment. - fn init_sequence(&mut self, writer: &mut BytesWriter) -> Result, TransmuxError> { + fn init_sequence( + &mut self, + writer: &mut impl io::Write, + ) -> Result, TransmuxError> { // We need to find the tag that is the video sequence header // and the audio sequence header let (video_sequence_header, audio_sequence_header, scriptdata_tag) = self.find_tags(); diff --git a/crates/transmuxer/src/tests/mod.rs b/crates/transmuxer/src/tests/mod.rs index 7738c4d5e..f58da7b6a 100644 --- a/crates/transmuxer/src/tests/mod.rs +++ b/crates/transmuxer/src/tests/mod.rs @@ -4,10 +4,9 @@ use std::io::{ use std::path::PathBuf; use std::process::{Command, Stdio}; -use aac::AudioObjectType; -use bytesio::bytes_writer::BytesWriter; use flv::FlvHeader; use mp4::codec::{AudioCodec, VideoCodec}; +use scuffle_aac::AudioObjectType; use crate::define::{AudioSettings, VideoSettings}; use crate::{TransmuxResult, Transmuxer}; @@ -27,7 +26,7 @@ fn test_transmuxer_avc_aac() { let data = cursor.into_inner().slice(pos..); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); transmuxer.demux(data).unwrap(); @@ -90,12 +89,7 @@ fn test_transmuxer_avc_aac() { .spawn() .unwrap(); - ffprobe - .stdin - .as_mut() - .unwrap() - .write_all(&writer.dispose()) - .expect("write to stdin"); + ffprobe.stdin.as_mut().unwrap().write_all(&writer).expect("write to stdin"); let output = ffprobe.wait_with_output().unwrap(); assert!(output.status.success()); @@ -141,7 +135,7 @@ fn test_transmuxer_av1_aac() { let data = cursor.into_inner().slice(pos..); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); transmuxer.demux(data).unwrap(); @@ -213,7 +207,7 @@ fn test_transmuxer_av1_aac() { .spawn() .unwrap(); - ffprobe.stdin.as_mut().unwrap().write_all(&writer.dispose()).unwrap(); + ffprobe.stdin.as_mut().unwrap().write_all(&writer).unwrap(); let output = ffprobe.wait_with_output().unwrap(); assert!(output.status.success()); @@ -254,7 +248,7 @@ fn test_transmuxer_hevc_aac() { let data = cursor.into_inner().slice(pos..); - let mut writer = BytesWriter::default(); + let mut writer = Vec::new(); transmuxer.demux(data).unwrap(); @@ -321,12 +315,7 @@ fn test_transmuxer_hevc_aac() { .spawn() .unwrap(); - ffprobe - .stdin - .as_mut() - .unwrap() - .write_all(&writer.dispose()) - .expect("write to stdin"); + ffprobe.stdin.as_mut().unwrap().write_all(&writer).expect("write to stdin"); let output = ffprobe.wait_with_output().unwrap(); assert!(output.status.success());