flv: Implement header parsing.

This commit is contained in:
David Wendt 2023-04-08 17:00:24 -04:00 committed by kmeisthax
parent 5b93a9e316
commit 9cbc371a6a
3 changed files with 105 additions and 3 deletions

View File

@ -1,14 +1,61 @@
use crate::reader::FlvReader;
use bitflags::bitflags;
bitflags! {
#[derive(PartialEq, Eq, Debug)]
pub struct TypeFlags: u8 {
const HAS_AUDIO = 0b1000_0000;
const HAS_VIDEO = 0b0010_0000;
}
}
#[derive(PartialEq, Eq, Debug)]
pub struct Header {
version: u8,
type_flags: TypeFlags,
data_offset: u32,
pub version: u8,
pub type_flags: TypeFlags,
pub data_offset: u32,
}
impl Header {
/// Parse an FLV header.
///
/// If this yields `None`, then the given data stream is either not an FLV
/// container or too short to parse.
pub fn parse(reader: &mut FlvReader<'_>) -> Option<Self> {
let signature = reader.read_u24()?;
if signature != 0x464C56 {
return None;
}
let version = reader.read_u8()?;
let type_flags = TypeFlags::from_bits_retain(reader.read_u8()?);
let data_offset = reader.read_u32()?;
Some(Header {
version,
type_flags,
data_offset,
})
}
}
#[cfg(test)]
mod tests {
use crate::header::{Header, TypeFlags};
use crate::reader::FlvReader;
#[test]
fn read_header() {
let data = [0x46, 0x4C, 0x56, 0x01, 0xA0, 0x12, 0x34, 0x56, 0x78];
let mut reader = FlvReader::from_source(&data);
assert_eq!(
Header::parse(&mut reader),
Some(Header {
version: 1,
type_flags: TypeFlags::HAS_AUDIO | TypeFlags::HAS_VIDEO,
data_offset: 0x12345678
})
);
}
}

View File

@ -1,3 +1,8 @@
mod header;
mod sound;
mod tag;
mod reader;
pub use header::Header;
pub use reader::FlvReader;

50
flv/src/reader.rs Normal file
View File

@ -0,0 +1,50 @@
/// A reader that allows demuxing an FLV container.
pub struct FlvReader<'a> {
source: &'a [u8],
position: usize,
}
impl<'a> FlvReader<'a> {
pub fn from_source(source: &'a [u8]) -> Self {
FlvReader {
source,
position: 0,
}
}
/// Read a certain number of bytes from the buffer.
///
/// This works like `Read`, but returns borrowed slices of the source
/// buffer. The buffer position will be advanced so that repeated reads
/// yield new data.
///
/// If the requested number of bytes are not available, `None` is returned.
pub fn read(&mut self, count: usize) -> Option<&'a [u8]> {
let start = self.position;
let end = self.position.checked_add(count)?;
if end > self.source.len() {
return None;
}
self.position = end;
Some(&self.source[start..end])
}
pub fn read_u8(&mut self) -> Option<u8> {
Some(self.read(1)?[0])
}
pub fn read_u24(&mut self) -> Option<u32> {
let bytes = self.read(3)?;
Some(u32::from_be_bytes([0, bytes[0], bytes[1], bytes[2]]))
}
pub fn read_u32(&mut self) -> Option<u32> {
Some(u32::from_be_bytes(
self.read(4)?.try_into().expect("four bytes"),
))
}
}