mod audio;
mod script;
mod video;
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::vec::Vec;
use nom::{
number::streaming::{be_u24, be_u32, be_u8},
IResult,
};
pub use self::{audio::*, script::*, video::*};
const FLV_HEADER_SIGNATURE: [u8; 3] = [0x46, 0x4c, 0x56];
#[derive(Clone, Debug, PartialEq)]
pub struct FlvFile<'a> {
pub header: FlvFileHeader,
pub body: FlvFileBody<'a>,
}
impl<'a> FlvFile<'a> {
pub fn parse(input: &'a [u8]) -> IResult<&'a [u8], FlvFile<'a>> {
do_parse!(
input,
header: call!(FlvFileHeader::parse) >>
body: call!(FlvFileBody::parse) >>
(FlvFile { header, body })
)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct FlvFileHeader {
pub signature: [u8; 3],
pub version: u8,
pub flags: u8,
pub has_audio: bool,
pub has_video: bool,
pub data_offset: u32,
}
impl FlvFileHeader {
pub fn parse(input: &[u8]) -> IResult<&[u8], FlvFileHeader> {
do_parse!(
input,
tag!(FLV_HEADER_SIGNATURE) >>
version: be_u8 >>
flags: be_u8 >>
data_offset: be_u32 >>
(FlvFileHeader {
signature: FLV_HEADER_SIGNATURE,
version,
flags,
has_audio: flags & 4 == 4,
has_video: flags & 1 == 1,
data_offset,
})
)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct FlvFileBody<'a> {
pub first_previous_tag_size: u32,
pub tags: Vec<(FlvTag<'a>, u32)>,
}
impl<'a> FlvFileBody<'a> {
pub fn parse(input: &'a [u8]) -> IResult<&'a [u8], FlvFileBody<'a>> {
do_parse!(
input,
first_previous_tag_size: be_u32 >>
tags: many0!(complete!(tuple!(call!(FlvTag::parse), be_u32))) >>
(FlvFileBody { first_previous_tag_size, tags })
)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct FlvTag<'a> {
pub header: FlvTagHeader,
pub data: FlvTagData<'a>,
}
impl<'a> FlvTag<'a> {
pub fn parse(input: &'a [u8]) -> IResult<&'a [u8], FlvTag<'a>> {
do_parse!(
input,
header: call!(FlvTagHeader::parse) >>
data: call!(FlvTagData::parse, header.tag_type, header.data_size as usize) >>
(FlvTag { header, data })
)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct FlvTagHeader {
pub tag_type: FlvTagType,
pub data_size: u32,
pub timestamp: u32,
pub stream_id: u32,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FlvTagType {
Audio = 0x08,
Video = 0x09,
Script = 0x18,
}
impl FlvTagHeader {
pub fn parse(input: &[u8]) -> IResult<&[u8], FlvTagHeader> {
do_parse!(
input,
tag_type: switch!(be_u8,
8 => value!(FlvTagType::Audio) |
9 => value!(FlvTagType::Video) |
18 => value!(FlvTagType::Script)
) >>
data_size: be_u24 >>
timestamp: be_u24 >>
timestamp_extended: be_u8 >>
stream_id: be_u24 >>
(FlvTagHeader {
tag_type,
data_size,
timestamp: (u32::from(timestamp_extended) << 24) + timestamp,
stream_id,
})
)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum FlvTagData<'a> {
Audio(AudioTag<'a>),
Video(VideoTag<'a>),
Script(ScriptTag<'a>),
}
impl<'a> FlvTagData<'a> {
pub fn parse(
input: &'a [u8],
tag_type: FlvTagType,
size: usize,
) -> IResult<&'a [u8], FlvTagData<'a>> {
match tag_type {
FlvTagType::Audio => map!(input, call!(AudioTag::parse, size), FlvTagData::Audio),
FlvTagType::Video => map!(input, call!(VideoTag::parse, size), FlvTagData::Video),
FlvTagType::Script => map!(input, call!(ScriptTag::parse, size), FlvTagData::Script),
}
}
}