flv: Implement header parsing.
This commit is contained in:
parent
5b93a9e316
commit
9cbc371a6a
|
@ -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
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
mod header;
|
||||
mod sound;
|
||||
mod tag;
|
||||
|
||||
mod reader;
|
||||
|
||||
pub use header::Header;
|
||||
pub use reader::FlvReader;
|
||||
|
|
|
@ -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"),
|
||||
))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue