use crate::error::ParseError;
use std::convert::TryFrom;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Header {
pub version: u8,
pub audio_flag: bool,
pub video_flag: bool,
pub data_offset: u32,
}
impl Header {
pub const SIGNATURE: [u8; 3] = [b'F', b'L', b'V'];
pub const SIZE: usize = 9;
}
impl TryFrom<[u8; Header::SIZE]> for Header {
type Error = ParseError;
fn try_from(
[f, l, v, version, flag, d1, d2, d3, d4]: [u8; Header::SIZE],
) -> Result<Self, ParseError> {
match [f, l, v] {
Self::SIGNATURE => {}
[s1, s2, s3] => return Err(ParseError::HeaderSignature(s1, s2, s3)),
}
let reserved_flag = 0b11111010 & flag;
if reserved_flag != 0 {
return Err(ParseError::HeaderTypeFlagsReserved(reserved_flag));
}
let audio_flag = 0b00000100 & flag != 0;
let video_flag = 0b00000001 & flag != 0;
let data_offset = u32::from_be_bytes([d1, d2, d3, d4]);
Ok(Self {
version,
audio_flag,
video_flag,
data_offset,
})
}
}
impl From<Header> for [u8; Header::SIZE] {
fn from(h: Header) -> Self {
let flag =
if h.audio_flag { 0b0000100 } else { 0 } | if h.video_flag { 0b00000001 } else { 0 };
let [o1, o2, o3, o4] = h.data_offset.to_be_bytes();
[
Header::SIGNATURE[0],
Header::SIGNATURE[1],
Header::SIGNATURE[2],
h.version,
flag,
o1,
o2,
o3,
o4,
]
}
}
#[test]
fn parse_header() {
let header = Header {
version: 1,
audio_flag: true,
video_flag: true,
data_offset: 0x12345678,
};
let bytes: [u8; Header::SIZE] = header.into();
let parsed = Header::try_from(bytes);
assert_eq!(Ok(header), parsed);
assert_eq!(Ok(bytes), parsed.map(|h| h.into()));
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TagType {
Audio,
Video,
ScriptData,
Reserved(u8),
}
impl From<TagType> for u8 {
fn from(tt: TagType) -> Self {
match tt {
TagType::Audio => 8,
TagType::Video => 9,
TagType::ScriptData => 18,
TagType::Reserved(n) => n,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TagHeader {
pub tag_type: TagType,
pub data_size: u32,
pub timestamp: i32,
}
impl TagHeader {
pub const SIZE: usize = (8 + 24 + 24 + 8 + 24) / 8;
pub const MAX_DATA_SIZE: usize = 0x00ffffff;
}
impl From<[u8; TagHeader::SIZE]> for TagHeader {
fn from([tt, s1, s2, s3, t1, t2, t3, t0, _, _, _]: [u8; TagHeader::SIZE]) -> Self {
let tag_type = match tt {
8 => TagType::Audio,
9 => TagType::Video,
18 => TagType::ScriptData,
n => TagType::Reserved(n),
};
let data_size = u32::from_be_bytes([0, s1, s2, s3]);
let timestamp = i32::from_be_bytes([t0, t1, t2, t3]);
TagHeader {
tag_type,
data_size,
timestamp,
}
}
}
impl From<TagHeader> for [u8; TagHeader::SIZE] {
fn from(h: TagHeader) -> Self {
let tt = h.tag_type.into();
let [_, s1, s2, s3] = h.data_size.to_be_bytes();
let [t0, t1, t2, t3] = h.timestamp.to_be_bytes();
[tt, s1, s2, s3, t1, t2, t3, t0, 0, 0, 0]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SoundFormat {
LinearPCMPlatformEndian = 0,
ADPCM = 1,
MP3 = 2,
LinearPCMLittleEndian = 3,
Nellymoser16 = 4,
Nellymoser8 = 5,
Nellymoser = 6,
G711ALaw = 7,
G711MuLaw = 8,
Reserved = 9,
AAC = 10,
Speex = 11,
MP38kHz = 14,
DeviceSpecific = 15,
}
impl TryFrom<u8> for SoundFormat {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use SoundFormat::*;
Ok(match (value & 0b_1111_0000) >> 4 {
0 => LinearPCMPlatformEndian,
1 => ADPCM,
2 => MP3,
3 => LinearPCMLittleEndian,
4 => Nellymoser16,
5 => Nellymoser8,
6 => Nellymoser,
7 => G711ALaw,
8 => G711MuLaw,
9 => Reserved,
10 => AAC,
11 => Speex,
14 => MP38kHz,
15 => DeviceSpecific,
n => return Err(ParseError::SoundFormat(n)),
})
}
}
impl From<SoundFormat> for u8 {
fn from(sf: SoundFormat) -> Self {
(sf as u8) << 4
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SoundRate {
R5p5kHz = 0,
R11kHz = 1,
R22kHz = 2,
R44kHz = 3,
}
impl TryFrom<u8> for SoundRate {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use SoundRate::*;
Ok(match (value & 0b_0000_1100) >> 2 {
0 => R5p5kHz,
1 => R11kHz,
2 => R22kHz,
3 => R44kHz,
n => return Err(ParseError::SoundRate(n)),
})
}
}
impl From<SoundRate> for u8 {
fn from(sr: SoundRate) -> Self {
(sr as u8) << 2
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SoundSize {
S8Bit = 0,
S16Bit = 1,
}
impl TryFrom<u8> for SoundSize {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use SoundSize::*;
Ok(match (value & 0b_0000_0010) >> 1 {
0 => S8Bit,
1 => S16Bit,
n => return Err(ParseError::SoundSize(n)),
})
}
}
impl From<SoundSize> for u8 {
fn from(ss: SoundSize) -> Self {
(ss as u8) << 1
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SoundType {
Mono = 0,
Stereo = 1,
}
impl TryFrom<u8> for SoundType {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use SoundType::*;
Ok(match value & 0b_0000_0001 {
0 => Mono,
1 => Stereo,
n => return Err(ParseError::SoundType(n)),
})
}
}
impl From<SoundType> for u8 {
fn from(st: SoundType) -> Self {
st as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AudioDataHeader {
pub sound_format: SoundFormat,
pub sound_rate: SoundRate,
pub sound_size: SoundSize,
pub sound_type: SoundType,
}
impl TryFrom<u8> for AudioDataHeader {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
let sound_format = SoundFormat::try_from(value)?;
let sound_rate = SoundRate::try_from(value)?;
let sound_size = SoundSize::try_from(value)?;
let sound_type = SoundType::try_from(value)?;
Ok(Self {
sound_format,
sound_rate,
sound_size,
sound_type,
})
}
}
impl From<AudioDataHeader> for u8 {
fn from(h: AudioDataHeader) -> Self {
u8::from(h.sound_format)
| u8::from(h.sound_rate)
| u8::from(h.sound_size)
| u8::from(h.sound_type)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum VideoFrameType {
KeyFrame = 1,
InterFrame = 2,
DisposableInterFrame = 3,
GeneratedKeyFrame = 4,
VideoInfoOrCommandFrame = 5,
}
impl TryFrom<u8> for VideoFrameType {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use VideoFrameType::*;
Ok(match (value & 0xf0) >> 4 {
1 => KeyFrame,
2 => InterFrame,
3 => DisposableInterFrame,
4 => GeneratedKeyFrame,
5 => VideoInfoOrCommandFrame,
n => return Err(ParseError::VideoFrameType(n)),
})
}
}
impl From<VideoFrameType> for u8 {
fn from(vft: VideoFrameType) -> Self {
(vft as u8) << 4
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum VideoCodecId {
JPEG = 1,
SorensonH263 = 2,
ScreenVideo = 3,
On2VP6 = 4,
On2VP6WithAlpha = 5,
ScreenVideoVersion2 = 6,
AVC = 7,
}
impl TryFrom<u8> for VideoCodecId {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use VideoCodecId::*;
Ok(match value & 0xf {
1 => JPEG,
2 => SorensonH263,
3 => ScreenVideo,
4 => On2VP6,
5 => On2VP6WithAlpha,
6 => ScreenVideoVersion2,
7 => AVC,
n => return Err(ParseError::VideoCodecId(n)),
})
}
}
impl From<VideoCodecId> for u8 {
fn from(vci: VideoCodecId) -> Self {
vci as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct VideoDataHeader {
pub frame_type: VideoFrameType,
pub codec_id: VideoCodecId,
}
impl TryFrom<u8> for VideoDataHeader {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
let frame_type = VideoFrameType::try_from(value)?;
let codec_id = VideoCodecId::try_from(value)?;
Ok(Self {
frame_type,
codec_id,
})
}
}
impl From<VideoDataHeader> for u8 {
fn from(h: VideoDataHeader) -> Self {
u8::from(h.frame_type) | u8::from(h.codec_id)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SeekFlag {
Start = 0,
End = 1,
}
impl TryFrom<u8> for SeekFlag {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use SeekFlag::*;
Ok(match value {
0 => Start,
1 => End,
n => return Err(ParseError::SeekFlag(n)),
})
}
}
impl From<SeekFlag> for u8 {
fn from(sf: SeekFlag) -> Self {
sf as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct MetaData {
pub duration: f64,
pub width: f64,
pub height: f64,
pub videodatarate: f64,
pub framerate: f64,
pub videocodecid: f64,
pub audiosamplerate: f64,
pub audiosamplesize: f64,
pub stereo: bool,
pub audiocodecid: f64,
pub filesize: f64,
}