flowly_flv/parser/tag/video.rs
1use flowly::Fourcc;
2
3use crate::{
4 error::Error,
5 parser::{FlvParser, Parser},
6 reader::FlvReader,
7 tag::video::{
8 AvMultitrackType, AvcPacketType, CodecID, PacketExData, VideoFrameType, VideoPacketType,
9 VideoTag, VideoTagBody, VideoTagHeader,
10 mpeg4_avc::{Mpeg4AvcNALUSeq, Mpeg4AvcRecord},
11 },
12};
13
14impl<E> Parser<E, VideoTagHeader> for FlvParser {
15 type Error = Error<E>;
16
17 fn parse(&mut self, reader: &mut impl FlvReader) -> Result<VideoTagHeader, Self::Error> {
18 let header = reader.read_u8()?;
19 let frame_type = VideoFrameType::from(header & 0x70 >> 4);
20 let enhanced = (header >> 7) & 1 > 0;
21
22 let mut fourcc = Fourcc::default();
23 let mut multitrack = false;
24 let mut has_body = true;
25 let mut multitrack_type = AvMultitrackType::OneTrack;
26 let mut _video_command = None;
27 let mut pkt_type = VideoPacketType::from(header & 0x0F);
28
29 if enhanced {
30 while let VideoPacketType::ModEx = pkt_type {
31 let ex_data: PacketExData = self.parse(reader)?;
32
33 pkt_type = ex_data.pkt_type;
34 }
35
36 if pkt_type != VideoPacketType::Metadata && frame_type == VideoFrameType::Command {
37 _video_command = Some(reader.read_u8()?);
38
39 // ExVideoTagBody has no payload if we got here.
40 // Set boolean to not try to process the video body.
41 has_body = false;
42 } else if pkt_type == VideoPacketType::Multitrack {
43 multitrack = true;
44 // Fetch VideoPacketType for all video tracks in the video message.
45 // This fetch MUST not result in a VideoPacketType.Multitrack
46 multitrack_type = AvMultitrackType::from(reader.read_u8()?);
47
48 if multitrack_type != AvMultitrackType::ManyTracksManyCodecs {
49 fourcc = Fourcc::from(reader.read_u32()?);
50 }
51 } else {
52 fourcc = Fourcc::from(reader.read_u32()?);
53 }
54 } else {
55 fourcc = match CodecID::from(header & 0x0F) {
56 CodecID::SorensonH263 => Fourcc::from_static("M263"),
57 CodecID::Screen1 => Fourcc::from_static("SCR1"),
58 CodecID::VP6 => Fourcc::from_static("VP06"),
59 CodecID::VP6Alpha => Fourcc::from_static("VP6a"),
60 CodecID::Screen2 => Fourcc::from_static("SCR2"),
61 CodecID::AVC => Fourcc::VIDEO_AVC,
62 CodecID::RealH263 => Fourcc::from_static("H263"),
63 CodecID::Hevc => Fourcc::VIDEO_HEVC,
64 CodecID::Unknown(_) => Fourcc::default(),
65 };
66 }
67
68 Ok(VideoTagHeader {
69 pkt_type,
70 multitrack,
71 has_body,
72 multitrack_type,
73 fourcc,
74 frame_type,
75 enhanced,
76 })
77 }
78}
79
80impl<E> Parser<E, VideoTag> for FlvParser {
81 type Error = Error<E>;
82
83 fn parse(&mut self, reader: &mut impl FlvReader) -> Result<VideoTag, Self::Error> {
84 let mut header: VideoTagHeader = self.parse(reader)?;
85 let mut track_id = 0;
86 let mut pts_offset = 0;
87 let mut param_count = 0;
88 let mut nalus = Vec::new();
89
90 if header.enhanced {
91 if header.multitrack_type == AvMultitrackType::ManyTracksManyCodecs {
92 // Each track has a codec assigned to it. Fetch the FOURCC for the next track.
93 header.fourcc = Fourcc::from(reader.read_u32()?)
94 }
95
96 // Track Ordering:
97 //
98 // For identifying the highest priority (a.k.a., default track)
99 // or highest quality track, it is RECOMMENDED to use trackId
100 // set to zero. For tracks of lesser priority or quality, use
101 // multiple instances of trackId with ascending numerical values.
102 // The concept of priority or quality can have multiple
103 // interpretations, including but not limited to bitrate,
104 // resolution, default angle, and language. This recommendation
105 // serves as a guideline intended to standardize track numbering
106 // across various applications.
107 track_id = reader.read_u8()?;
108
109 if header.multitrack_type != AvMultitrackType::OneTrack {
110 // The `sizeOfVideoTrack` specifies the size in bytes of the
111 // current track that is being processed. This size starts
112 // counting immediately after the position where the `sizeOfVideoTrack`
113 // value is located. You can use this value as an offset to locate the
114 // next video track in a multitrack system. The data pointer is
115 // positioned immediately after this field. Depending on the MultiTrack
116 // type, the offset points to either a `fourCc` or a `trackId.`
117 let _size_of_video_track = reader.read_u24()?;
118 }
119
120 match header.pkt_type {
121 VideoPacketType::SequenceStart => {
122 // if self.mpeg4_avc_parser.is_none() {
123 // self.mpeg4_avc_parser = Some(Default::default());
124 // }
125
126 // let x: Mpeg4AvcRecord =
127 // self.mpeg4_avc_parser.as_mut().unwrap().parse(reader)?;
128
129 // params = Some([x.sps, x.pps].concat());
130 }
131 VideoPacketType::CodedFrames => {
132 match header.fourcc {
133 Fourcc::VIDEO_AVC => {
134 // See ISO/IEC 14496-12:2015, 8.6.1 for the description of the composition
135 // time offset. The offset in an FLV file is always in milliseconds
136 pts_offset = reader.read_i24()? as i32;
137
138 // let x: Mpeg4AvcNALUSeq =
139 // self.mpeg4_avc_parser.as_mut().unwrap().parse(reader)?;
140
141 // nalus = x.nalus;
142 nalus = vec![reader.read_to_end()?];
143 }
144 Fourcc::VIDEO_HEVC => {
145 // body contains a configuration record to start the sequence.
146 // See ISO/IEC 14496-15:2022, 8.3.3.2 for the description of
147 // HEVCDecoderConfigurationRecord
148 pts_offset = reader.read_i24()? as i32;
149 // let x: Mpeg4AvcNALUSeq =
150 // self.mpeg4_avc_parser.as_mut().unwrap().parse(reader)?;
151
152 // nalus = x.nalus;
153 nalus = vec![reader.read_to_end()?];
154 }
155 _ => {
156 nalus = vec![reader.read_to_end()?];
157 }
158 }
159 }
160 VideoPacketType::SequenceEnd => {}
161 VideoPacketType::CodedFramesX => {
162 nalus = vec![reader.read_to_end()?];
163 }
164 VideoPacketType::Metadata => {}
165 VideoPacketType::MPEG2TSSequenceStart => {}
166 VideoPacketType::Multitrack => {}
167 VideoPacketType::ModEx => {}
168 VideoPacketType::Unknown(_) => {}
169 }
170 } else {
171 match header.fourcc {
172 Fourcc::VIDEO_AVC | Fourcc::VIDEO_HEVC => {
173 if self.mpeg4_avc_parser.is_none() {
174 self.mpeg4_avc_parser = Some(Default::default());
175 }
176
177 let packet_type = AvcPacketType::from(reader.read_u8()?);
178 pts_offset = reader.read_i24()? as i32;
179
180 match packet_type {
181 AvcPacketType::SequenceHeader => {
182 let mut x: Mpeg4AvcRecord =
183 self.mpeg4_avc_parser.as_mut().unwrap().parse(reader)?;
184
185 param_count += x.sps.len() as u32;
186 nalus.extend(x.sps.drain(..));
187
188 param_count += x.pps.len() as u32;
189 nalus.extend(x.pps.drain(..));
190 }
191 AvcPacketType::NALU => {
192 let x: Mpeg4AvcNALUSeq =
193 self.mpeg4_avc_parser.as_mut().unwrap().parse(reader)?;
194
195 nalus = x.nalus;
196 }
197 _ => {}
198 }
199 }
200 _ => {
201 nalus.push(reader.read_to_end()?);
202 }
203 }
204 }
205
206 Ok(VideoTag {
207 header,
208 track_id,
209 body: VideoTagBody {
210 pts_offset,
211 param_count,
212 nalus,
213 },
214 })
215 }
216}