use byteorder::{BigEndian, ReadBytesExt};
use bytes::Bytes;
use nutype_enum::nutype_enum;
use scuffle_bytes_util::BytesCursorExt;
use super::audio::AudioData;
use super::script::ScriptData;
use super::video::VideoData;
use crate::error::FlvError;
#[derive(Debug, Clone, PartialEq)]
pub struct FlvTag<'a> {
pub timestamp_ms: u32,
pub stream_id: u32,
pub data: FlvTagData<'a>,
}
impl FlvTag<'_> {
pub fn demux(reader: &mut std::io::Cursor<Bytes>) -> Result<Self, FlvError> {
let first_byte = reader.read_u8()?;
let filter = (first_byte & 0b0010_0000) != 0;
let tag_type = FlvTagType::from(first_byte & 0b00011111);
let data_size = reader.read_u24::<BigEndian>()?;
let timestamp_ms = reader.read_u24::<BigEndian>()? | ((reader.read_u8()? as u32) << 24);
let stream_id = reader.read_u24::<BigEndian>()?;
let data = reader.extract_bytes(data_size as usize)?;
let data = if !filter {
FlvTagData::demux(tag_type, &mut std::io::Cursor::new(data))?
} else {
FlvTagData::Encrypted { data }
};
Ok(FlvTag {
timestamp_ms,
stream_id,
data,
})
}
}
nutype_enum! {
pub enum FlvTagType(u8) {
Audio = 8,
Video = 9,
ScriptData = 18,
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum FlvTagData<'a> {
Audio(AudioData),
Video(VideoData<'a>),
ScriptData(ScriptData<'a>),
Encrypted {
data: Bytes,
},
Unknown {
tag_type: FlvTagType,
data: Bytes,
},
}
impl FlvTagData<'_> {
pub fn demux(tag_type: FlvTagType, reader: &mut std::io::Cursor<Bytes>) -> Result<Self, FlvError> {
match tag_type {
FlvTagType::Audio => Ok(FlvTagData::Audio(AudioData::demux(reader)?)),
FlvTagType::Video => Ok(FlvTagData::Video(VideoData::demux(reader)?)),
FlvTagType::ScriptData => Ok(FlvTagData::ScriptData(ScriptData::demux(reader)?)),
_ => Ok(FlvTagData::Unknown {
tag_type,
data: reader.extract_remaining(),
}),
}
}
}