Skip to content

Commit

Permalink
small improve
Browse files Browse the repository at this point in the history
  • Loading branch information
TroyKomodo committed Jan 13, 2025
1 parent 711b1d5 commit d6d0ed8
Show file tree
Hide file tree
Showing 8 changed files with 292 additions and 169 deletions.
80 changes: 70 additions & 10 deletions crates/flv/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,38 @@ use scuffle_bytes_util::BytesCursorExt;
use super::aac::{AacPacket, AacPacketType};
use crate::macros::nutype_enum;

/// FLV Tag Audio Data
///
/// This is the container for the audio data.
///
/// Defined by:
/// - video_file_format_spec_v10.pdf (Chapter 1 - The FLV File Format - Audio tags)
/// - video_file_format_spec_v10_1.pdf (Annex E.4.2.1 - AUDIODATA)
#[derive(Debug, Clone, PartialEq)]
pub struct AudioData {
/// The sound rate of the audio data. (2 bits)
pub sound_rate: SoundRate,
/// The sound size of the audio data. (1 bit)
pub sound_size: SoundSize,
/// The sound type of the audio data. (1 bit)
pub sound_type: SoundType,
/// The body of the audio data.
pub body: AudioDataBody,
}

impl AudioData {
pub fn demux(reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
let byte = reader.read_u8()?;
// SoundFormat is the first 4 bits of the byte
let sound_format = SoundFormat::from(byte >> 4);
// SoundRate is the next 2 bits of the byte
let sound_rate = SoundRate::from((byte >> 2) & 0b11);
// SoundSize is the next bit of the byte
let sound_size = SoundSize::from((byte >> 1) & 0b1);
// SoundType is the last bit of the byte
let sound_type = SoundType::from(byte & 0b1);

// Now we can demux the body of the audio data
let body = AudioDataBody::demux(sound_format, reader)?;

Ok(AudioData {
Expand All @@ -35,28 +52,49 @@ impl AudioData {

nutype_enum! {
/// FLV Sound Format
/// Defined in the FLV specification. Chapter 1 - AudioTags
/// The SoundFormat indicates the codec used to encode the sound.
///
/// Denotes the type of the underlying data packet
///
/// Defined by:
/// - video_file_format_spec_v10.pdf (Chapter 1 - The FLV File Format - Audio tags)
/// - video_file_format_spec_v10_1.pdf (Annex E.4.2.1 - AUDIODATA)
pub enum SoundFormat(u8) {
/// Linear PCM, platform endian
LinearPcmPlatformEndian = 0,
/// ADPCM
Adpcm = 1,
/// MP3
Mp3 = 2,
/// Linear PCM, little endian
LinearPcmLittleEndian = 3,
/// Nellymoser 16Khz Mono
Nellymoser16KhzMono = 4,
/// Nellymoser 8Khz Mono
Nellymoser8KhzMono = 5,
/// Nellymoser
Nellymoser = 6,
/// G.711 A-Law logarithmic PCM
G711ALaw = 7,
/// G.711 Mu-Law logarithmic PCM
G711MuLaw = 8,
Reserved = 9,
/// AAC
Aac = 10,
/// Speex
Speex = 11,
/// Mp3 8Khz
Mp38Khz = 14,
/// Device specific sound
DeviceSpecificSound = 15,
}
}

/// FLV Tag Audio Data Body
/// Defined by [video_file_format_spec_v10_1 - Annex E.4.2.2 AUDIODATA]
///
/// This is the container for the audio data body.
///
/// Defined by:
/// - video_file_format_spec_v10.pdf (Chapter 1 - The FLV File Format - Audio tags)
/// - video_file_format_spec_v10_1.pdf (Annex E.4.2.1 - AUDIODATA)
#[derive(Debug, Clone, PartialEq)]
pub enum AudioDataBody {
/// AAC Audio Packet
Expand All @@ -69,6 +107,8 @@ impl AudioDataBody {
pub fn demux(sound_format: SoundFormat, reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
match sound_format {
SoundFormat::Aac => {
// For some reason the spec adds a specific byte before the AAC data.
// This byte is the AAC packet type.
let aac_packet_type = AacPacketType::from(reader.read_u8()?);
Ok(Self::Aac(AacPacket::demux(aac_packet_type, reader)?))
}
Expand All @@ -82,32 +122,52 @@ impl AudioDataBody {

nutype_enum! {
/// FLV Sound Rate
/// Defined in the FLV specification. Chapter 1 - AudioTags
/// The SoundRate indicates the sampling rate of the audio data.
///
/// Denotes the sampling rate of the audio data.
///
/// Defined by:
/// - video_file_format_spec_v10.pdf (Chapter 1 - The FLV File Format - Audio tags)
/// - video_file_format_spec_v10_1.pdf (Annex E.4.2.1 - AUDIODATA)
pub enum SoundRate(u8) {
/// 5.5 KHz
Hz5500 = 0,
/// 11 KHz
Hz11000 = 1,
/// 22 KHz
Hz22000 = 2,
/// 44 KHz
Hz44000 = 3,
}
}

nutype_enum! {
/// FLV Sound Size
/// Defined in the FLV specification. Chapter 1 - AudioTags
/// The SoundSize indicates the size of each sample in the audio data.
///
/// Denotes the size of each sample in the audio data.
///
/// Defined by:
/// - video_file_format_spec_v10.pdf (Chapter 1 - The FLV File Format - Audio tags)
/// - video_file_format_spec_v10_1.pdf (Annex E.4.2.1 - AUDIODATA)
pub enum SoundSize(u8) {
/// 8 bit
Bit8 = 0,
/// 16 bit
Bit16 = 1,
}
}

nutype_enum! {
/// FLV Sound Type
/// Defined in the FLV specification. Chapter 1 - AudioTags
/// The SoundType indicates the number of channels in the audio data.
///
/// Denotes the number of channels in the audio data.
///
/// Defined by:
/// - video_file_format_spec_v10.pdf (Chapter 1 - The FLV File Format - Audio tags)
/// - video_file_format_spec_v10_1.pdf (Annex E.4.2.1 - AUDIODATA)
pub enum SoundType(u8) {
/// Mono
Mono = 0,
/// Stereo
Stereo = 1,
}
}
14 changes: 9 additions & 5 deletions crates/flv/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ use bytes::{Buf, Bytes};
use super::header::FlvHeader;
use super::tag::FlvTag;

/// An FLV file
/// Defined by [video_file_format_spec_v10_1 - Annex E. The FLV File Format]
/// Specifically this is a combonation of a [`FlvHeader`] followed by the
/// `FLVFileBody` (which is just a series of [`FlvTag`]s)
/// An FLV file is a combination of a [`FlvHeader`] followed by the `FLVFileBody` (which is just a series of [`FlvTag`]s)
///
/// The `FLVFileBody` is defined by:
/// - video_file_format_spec_v10.pdf (Chapter 1 - The FLV File Format - Page 8)
/// - video_file_format_spec_v10_1.pdf (Annex E.3 - The FLV File Body)
#[derive(Debug, Clone, PartialEq)]
pub struct FlvFile {
pub header: FlvHeader,
Expand All @@ -20,12 +21,15 @@ impl FlvFile {

let mut tags = Vec::new();
while reader.has_remaining() {
reader.read_u32::<BigEndian>()?; // previous tag size
// We don't care about the previous tag size, its only really used for seeking backwards.
reader.read_u32::<BigEndian>()?;

// If there is no more data, we can stop reading.
if !reader.has_remaining() {
break;
}

// Demux the tag from the reader.
let tag = FlvTag::demux(reader)?;
tags.push(tag);
}
Expand Down
14 changes: 12 additions & 2 deletions crates/flv/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,33 @@ use bytes::Bytes;
use scuffle_bytes_util::BytesCursorExt;

/// The FLV Header
/// Defined by [video_file_format_spec_v10_1 - Annex E.2 The FLV Header]
/// Whenever a FLV file is read these are the first 9 bytes of the file.
///
/// Defined by:
/// - video_file_format_spec_v10.pdf (Chapter 1 - The FLV Header - Page 8)
/// - video_file_format_spec_v10_1.pdf (Annex E.2 - The FLV Header)
#[derive(Debug, Clone, PartialEq)]
pub struct FlvHeader {
/// The version of the FLV file.
pub version: u8,
/// Whether the FLV file has audio.
pub has_audio: bool,
/// Whether the FLV file has video.
pub has_video: bool,
/// The extra data in the FLV file.
/// Since the header provides a data offset, this is the bytes between the end of the header and the start of the data.
pub extra: Bytes,
}

impl FlvHeader {
/// Demux the FLV header from the given reader.
/// The reader will be returned in the position of the start of the data offset.
pub fn demux(reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
let start = reader.position() as usize;

let signature = reader.read_u24::<BigEndian>()?;

// 0 byte at the end because we are only reading 3 bytes not 4.
// 0 byte at the beginning because we are only reading 3 bytes not 4.
if signature != u32::from_be_bytes([0, b'F', b'L', b'V']) {
return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid signature"));
}
Expand Down
Loading

0 comments on commit d6d0ed8

Please sign in to comment.