flv: `parse` now consumes the back pointer.

We also guarantee that when parsing fails we leave the reader where it is to allow reading later.
This commit is contained in:
David Wendt 2023-04-10 20:37:36 -04:00 committed by kmeisthax
parent 3e2a5eb43b
commit 7bf28074c0
1 changed files with 64 additions and 31 deletions

View File

@ -3,6 +3,8 @@ use crate::script::ScriptData;
use crate::sound::AudioData;
use crate::video::VideoData;
use std::io::{Seek, SeekFrom};
#[repr(u8)]
#[derive(PartialEq, Debug)]
pub enum TagData<'a> {
@ -21,34 +23,64 @@ pub struct Tag<'a> {
impl<'a> Tag<'a> {
/// Parse a single FLV tag structure.
///
/// This assumes the reader is currently pointing at the tag itself, not
/// the back-pointers in between the tags.
/// FLV files are constructed as a list of tags. Back pointers to prior
/// tags are provided to allow reverse seeking. This function ignores the
/// back pointer and parses the tag at the current location. At the end of
/// parsing, the reader will be pointing to the start of the next tag. Thus,
/// repeated calls to `parse` will yield further tags until the end of the
/// file.
///
/// `None` indicates that either:
///
/// * There is not enough data in the reader to read the next tag
/// * The data in the reader is corrupt and not a valid FLV
///
/// If encountered, the position of the reader will be unchanged.
pub fn parse(reader: &mut FlvReader<'a>) -> Option<Self> {
let tag_type = reader.read_u8()?;
let data_size = reader.read_u24()?;
let timestamp = reader.read_u24()?;
let timestamp_extended = reader.read_u8()?;
let stream_id = reader.read_u24()?;
let old_position = reader.stream_position().ok()?;
let timestamp = ((timestamp_extended as u32) << 24 | timestamp) as i32;
let ret = (|| {
let _previous_tag_size = reader.read_u32()?;
match tag_type {
8 => Some(Tag {
timestamp,
stream_id,
data: TagData::Audio(AudioData::parse(reader, data_size)?),
}),
9 => Some(Tag {
timestamp,
stream_id,
data: TagData::Video(VideoData::parse(reader, data_size)?),
}),
18 => Some(Tag {
timestamp,
stream_id,
data: TagData::Script(ScriptData::parse(reader)?),
}),
_ => None,
let tag_type = reader.read_u8()?;
let data_size = reader.read_u24()?;
let timestamp = reader.read_u24()?;
let timestamp_extended = reader.read_u8()?;
let stream_id = reader.read_u24()?;
let timestamp = ((timestamp_extended as u32) << 24 | timestamp) as i32;
let new_position = reader.stream_position().ok()? + data_size as u64;
Some((
match tag_type {
8 => Tag {
timestamp,
stream_id,
data: TagData::Audio(AudioData::parse(reader, data_size)?),
},
9 => Tag {
timestamp,
stream_id,
data: TagData::Video(VideoData::parse(reader, data_size)?),
},
18 => Tag {
timestamp,
stream_id,
data: TagData::Script(ScriptData::parse(reader)?),
},
_ => return None,
},
new_position,
))
})();
if let Some((tag, new_position)) = ret {
reader.seek(SeekFrom::Start(new_position)).ok()?;
Some(tag)
} else {
reader.seek(SeekFrom::Start(old_position)).ok()?;
None
}
}
}
@ -64,8 +96,8 @@ mod tests {
#[test]
fn read_tag_sounddata() {
let data = [
0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0xFB, 0x12, 0x34,
0x56, 0x78,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
0x00, 0xFB, 0x12, 0x34, 0x56, 0x78,
];
let mut reader = FlvReader::from_source(&data);
@ -88,8 +120,8 @@ mod tests {
#[test]
fn read_tag_videodata() {
let data = [
0x09, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x21, 0x12, 0x34,
0x56, 0x78,
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
0x00, 0x21, 0x12, 0x34, 0x56, 0x78,
];
let mut reader = FlvReader::from_source(&data);
@ -110,8 +142,9 @@ mod tests {
#[test]
fn read_tag_scriptdata() {
let data = [
0x12, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x03, 0x01,
0x02, 0x03, 0x06, 0x00, 0x03, 0x01, 0x02, 0x03, 0x05, 0x00, 0x00, 0x09,
0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
0x00, 0x00, 0x03, 0x01, 0x02, 0x03, 0x06, 0x00, 0x03, 0x01, 0x02, 0x03, 0x05, 0x00,
0x00, 0x09,
];
let mut reader = FlvReader::from_source(&data);