scuffle_flv/video/header/
legacy.rs

1//! Legacy video header types and functions.
2
3use std::io;
4
5use byteorder::{BigEndian, ReadBytesExt};
6use bytes::Bytes;
7use nutype_enum::nutype_enum;
8
9use super::{VideoCommand, VideoFrameType};
10
11nutype_enum! {
12    /// FLV Video Codec ID
13    ///
14    /// Denotes the different types of video codecs.
15    ///
16    /// Defined by:
17    /// - Legacy FLV spec, Annex E.4.3.1
18    pub enum VideoCodecId(u8) {
19        /// Sorenson H.263
20        SorensonH263 = 2,
21        /// Screen Video
22        ScreenVideo = 3,
23        /// On2 VP6
24        On2VP6 = 4,
25        /// On2 VP6 with alpha channel
26        On2VP6WithAlphaChannel = 5,
27        /// Screen Video Version 2
28        ScreenVideoVersion2 = 6,
29        /// AVC (H.264)
30        Avc = 7,
31    }
32}
33
34nutype_enum! {
35    /// FLV AVC Packet Type
36    ///
37    /// The AVC packet type is used to determine if the video data is a sequence
38    /// header or a NALU.
39    ///
40    /// Defined by:
41    /// - Legacy FLV spec, Annex E.4.3.1
42    pub enum AvcPacketType(u8) {
43        /// AVC sequence header
44        SeqHdr = 0,
45        /// AVC NALU
46        Nalu = 1,
47        /// AVC end of sequence (lower level NALU sequence ender is not required or supported)
48        EndOfSequence = 2,
49    }
50}
51
52/// AVC packet header
53#[derive(Debug, Clone, PartialEq)]
54pub enum LegacyVideoTagHeaderAvcPacket {
55    /// AVC sequence header
56    SequenceHeader,
57    /// AVC NALU
58    Nalu {
59        /// The composition time offset of the NALU.
60        composition_time_offset: u32,
61    },
62    /// AVC end of sequence
63    EndOfSequence,
64    /// Unknown
65    Unknown {
66        /// The AVC packet type.
67        avc_packet_type: AvcPacketType,
68        /// The composition time offset of the packet.
69        composition_time_offset: u32,
70    },
71}
72
73impl LegacyVideoTagHeaderAvcPacket {
74    /// Demux the AVC packet header from the given reader.
75    pub fn demux(reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
76        let avc_packet_type = AvcPacketType::from(reader.read_u8()?);
77        let composition_time_offset = reader.read_u24::<BigEndian>()?;
78
79        match avc_packet_type {
80            AvcPacketType::SeqHdr => Ok(Self::SequenceHeader),
81            AvcPacketType::Nalu => Ok(Self::Nalu { composition_time_offset }),
82            AvcPacketType::EndOfSequence => Ok(Self::EndOfSequence),
83            _ => Ok(Self::Unknown {
84                avc_packet_type,
85                composition_time_offset,
86            }),
87        }
88    }
89}
90
91/// FLV `VideoTagHeader`
92///
93/// Defined by:
94/// - Legacy FLV spec, Annex E.4.3.1
95#[derive(Debug, Clone, PartialEq)]
96pub enum LegacyVideoTagHeader {
97    /// A video command with frame type [`VideoFrameType::Command`].
98    VideoCommand(VideoCommand),
99    /// AVC video packet.
100    AvcPacket(LegacyVideoTagHeaderAvcPacket),
101    /// Any other video data.
102    Other {
103        /// The codec id of the video data.
104        video_codec_id: VideoCodecId,
105    },
106}
107
108impl LegacyVideoTagHeader {
109    /// Demux the video tag header from the given reader.
110    pub fn demux(reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
111        let first_byte = reader.read_u8()?;
112        let frame_type = VideoFrameType::from(first_byte >> 4); // 0b1111_0000
113        let video_codec_id = VideoCodecId::from(first_byte & 0b0000_1111);
114
115        if video_codec_id == VideoCodecId::Avc {
116            let avc_packet = LegacyVideoTagHeaderAvcPacket::demux(reader)?;
117            return Ok(Self::AvcPacket(avc_packet));
118        }
119
120        if frame_type == VideoFrameType::Command {
121            return Ok(Self::VideoCommand(VideoCommand::from(reader.read_u8()?)));
122        }
123
124        Ok(Self::Other { video_codec_id })
125    }
126}