1use std::io::{self, Read};
2
3use byteorder::{BigEndian, ReadBytesExt};
4use bytes::Bytes;
5use nutype_enum::nutype_enum;
6use scuffle_av1::{AV1CodecConfigurationRecord, AV1VideoDescriptor};
7use scuffle_bytes_util::BytesCursorExt;
8use scuffle_h265::HEVCDecoderConfigurationRecord;
9
10use super::av1::Av1Packet;
11use super::avc::AvcPacket;
12use super::hevc::HevcPacket;
13
14nutype_enum! {
15 pub enum FrameType(u8) {
21 Keyframe = 1,
23 Interframe = 2,
25 DisposableInterframe = 3,
27 GeneratedKeyframe = 4,
29 VideoInfoOrCommandFrame = 5,
32 }
33}
34
35#[derive(Debug, Clone, PartialEq)]
42pub struct VideoTagHeader {
43 pub frame_type: FrameType,
45 pub body: VideoTagBody,
47}
48
49impl VideoTagHeader {
50 pub fn demux(reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
52 let byte = reader.read_u8()?;
53 let enhanced = (byte & 0b1000_0000) != 0;
54 let frame_type_byte = (byte >> 4) & 0b0111;
55 let packet_type_byte = byte & 0b0000_1111;
56 let frame_type = FrameType::from(frame_type_byte);
57 let body = if frame_type == FrameType::VideoInfoOrCommandFrame {
58 let command_packet = CommandPacket::from(reader.read_u8()?);
59 VideoTagBody::Command(command_packet)
60 } else {
61 VideoTagBody::demux(VideoPacketType::new(packet_type_byte, enhanced), reader)?
62 };
63
64 Ok(VideoTagHeader { frame_type, body })
65 }
66}
67
68nutype_enum! {
69 pub enum VideoCodecId(u8) {
78 SorensonH263 = 2,
80 ScreenVideo = 3,
82 On2VP6 = 4,
84 On2VP6WithAlphaChannel = 5,
86 ScreenVideoVersion2 = 6,
88 Avc = 7,
90 }
91}
92
93#[derive(Debug, Clone, PartialEq)]
103pub enum VideoTagBody {
104 Avc(AvcPacket),
107 Enhanced(EnhancedPacket),
110 Command(CommandPacket),
113 Unknown { codec_id: VideoCodecId, data: Bytes },
115}
116
117nutype_enum! {
118 pub enum CommandPacket(u8) {
123 StartOfClientSeeking = 1,
125 EndOfClientSeeking = 2,
127 }
128}
129
130#[derive(Debug, Clone, PartialEq, Copy, Eq, PartialOrd, Ord, Hash)]
140pub enum VideoPacketType {
141 CodecId(VideoCodecId),
143 Enhanced(EnhancedPacketType),
145}
146
147impl VideoPacketType {
148 pub fn new(byte: u8, enhanced: bool) -> Self {
149 if enhanced {
150 Self::Enhanced(EnhancedPacketType::from(byte))
151 } else {
152 Self::CodecId(VideoCodecId::from(byte))
153 }
154 }
155}
156
157impl VideoTagBody {
158 pub fn demux(packet_type: VideoPacketType, reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
161 match packet_type {
162 VideoPacketType::CodecId(codec_id) => match codec_id {
163 VideoCodecId::Avc => Ok(VideoTagBody::Avc(AvcPacket::demux(reader)?)),
164 _ => Ok(VideoTagBody::Unknown {
165 codec_id,
166 data: reader.extract_remaining(),
167 }),
168 },
169 VideoPacketType::Enhanced(packet_type) => {
170 let mut video_codec = [0; 4];
171 reader.read_exact(&mut video_codec)?;
172 let video_codec = VideoFourCC::from(video_codec);
173
174 match packet_type {
175 EnhancedPacketType::SequenceEnd => {
176 return Ok(VideoTagBody::Enhanced(EnhancedPacket::SequenceEnd { video_codec }))
177 }
178 EnhancedPacketType::Metadata => {
179 return Ok(VideoTagBody::Enhanced(EnhancedPacket::Metadata {
180 video_codec,
181 data: reader.extract_remaining(),
182 }))
183 }
184 _ => {}
185 }
186
187 match (video_codec, packet_type) {
188 (VideoFourCC::Av1, EnhancedPacketType::SequenceStart) => Ok(VideoTagBody::Enhanced(
189 EnhancedPacket::Av1(Av1Packet::SequenceStart(AV1CodecConfigurationRecord::demux(reader)?)),
190 )),
191 (VideoFourCC::Av1, EnhancedPacketType::Mpeg2SequenceStart) => {
192 Ok(VideoTagBody::Enhanced(EnhancedPacket::Av1(Av1Packet::SequenceStart(
193 AV1VideoDescriptor::demux(reader)?.codec_configuration_record,
194 ))))
195 }
196 (VideoFourCC::Av1, EnhancedPacketType::CodedFrames) => Ok(VideoTagBody::Enhanced(EnhancedPacket::Av1(
197 Av1Packet::Raw(reader.extract_remaining()),
198 ))),
199 (VideoFourCC::Hevc, EnhancedPacketType::SequenceStart) => Ok(VideoTagBody::Enhanced(
200 EnhancedPacket::Hevc(HevcPacket::SequenceStart(HEVCDecoderConfigurationRecord::demux(reader)?)),
201 )),
202 (VideoFourCC::Hevc, EnhancedPacketType::CodedFrames) => {
203 Ok(VideoTagBody::Enhanced(EnhancedPacket::Hevc(HevcPacket::Nalu {
204 composition_time: Some(reader.read_i24::<BigEndian>()?),
205 data: reader.extract_remaining(),
206 })))
207 }
208 (VideoFourCC::Hevc, EnhancedPacketType::CodedFramesX) => {
209 Ok(VideoTagBody::Enhanced(EnhancedPacket::Hevc(HevcPacket::Nalu {
210 composition_time: None,
211 data: reader.extract_remaining(),
212 })))
213 }
214 _ => Ok(VideoTagBody::Enhanced(EnhancedPacket::Unknown {
215 packet_type,
216 video_codec,
217 data: reader.extract_remaining(),
218 })),
219 }
220 }
221 }
222 }
223}
224
225#[derive(Debug, Clone, PartialEq)]
234pub enum EnhancedPacket {
235 Metadata { video_codec: VideoFourCC, data: Bytes },
237 SequenceEnd { video_codec: VideoFourCC },
239 Av1(Av1Packet),
241 Hevc(HevcPacket),
243 Unknown {
245 packet_type: EnhancedPacketType,
246 video_codec: VideoFourCC,
247 data: Bytes,
248 },
249}
250
251nutype_enum! {
252 pub enum VideoFourCC([u8; 4]) {
260 Av1 = *b"av01",
262 Vp9 = *b"vp09",
264 Hevc = *b"hvc1",
266 }
267}
268
269nutype_enum! {
270 pub enum EnhancedPacketType(u8) {
278 SequenceStart = 0,
280 CodedFrames = 1,
282 SequenceEnd = 2,
284 CodedFramesX = 3,
286 Metadata = 4,
288 Mpeg2SequenceStart = 5,
290 }
291}
292
293#[cfg(test)]
294#[cfg_attr(all(test, coverage_nightly), coverage(off))]
295mod tests {
296 use super::*;
297 use crate::avc::AvcPacketType;
298
299 #[test]
300 fn test_video_fourcc() {
301 let cases = [
302 (VideoFourCC::Av1, *b"av01", "VideoFourCC::Av1"),
303 (VideoFourCC::Vp9, *b"vp09", "VideoFourCC::Vp9"),
304 (VideoFourCC::Hevc, *b"hvc1", "VideoFourCC::Hevc"),
305 (VideoFourCC(*b"av02"), *b"av02", "VideoFourCC([97, 118, 48, 50])"),
306 ];
307
308 for (expected, bytes, name) in cases {
309 assert_eq!(VideoFourCC::from(bytes), expected);
310 assert_eq!(format!("{:?}", VideoFourCC::from(bytes)), name);
311 }
312 }
313
314 #[test]
315 fn test_enhanced_packet_type() {
316 let cases = [
317 (EnhancedPacketType::SequenceStart, 0, "EnhancedPacketType::SequenceStart"),
318 (EnhancedPacketType::CodedFrames, 1, "EnhancedPacketType::CodedFrames"),
319 (EnhancedPacketType::SequenceEnd, 2, "EnhancedPacketType::SequenceEnd"),
320 (EnhancedPacketType::CodedFramesX, 3, "EnhancedPacketType::CodedFramesX"),
321 (EnhancedPacketType::Metadata, 4, "EnhancedPacketType::Metadata"),
322 (
323 EnhancedPacketType::Mpeg2SequenceStart,
324 5,
325 "EnhancedPacketType::Mpeg2SequenceStart",
326 ),
327 (EnhancedPacketType(6), 6, "EnhancedPacketType(6)"),
328 (EnhancedPacketType(7), 7, "EnhancedPacketType(7)"),
329 ];
330
331 for (expected, value, name) in cases {
332 assert_eq!(EnhancedPacketType::from(value), expected);
333 assert_eq!(format!("{:?}", EnhancedPacketType::from(value)), name);
334 }
335 }
336
337 #[test]
338 fn test_frame_type() {
339 let cases = [
340 (FrameType::Keyframe, 1, "FrameType::Keyframe"),
341 (FrameType::Interframe, 2, "FrameType::Interframe"),
342 (FrameType::DisposableInterframe, 3, "FrameType::DisposableInterframe"),
343 (FrameType::GeneratedKeyframe, 4, "FrameType::GeneratedKeyframe"),
344 (FrameType::VideoInfoOrCommandFrame, 5, "FrameType::VideoInfoOrCommandFrame"),
345 (FrameType(6), 6, "FrameType(6)"),
346 (FrameType(7), 7, "FrameType(7)"),
347 ];
348
349 for (expected, value, name) in cases {
350 assert_eq!(FrameType::from(value), expected);
351 assert_eq!(format!("{:?}", FrameType::from(value)), name);
352 }
353 }
354
355 #[test]
356 fn test_video_codec_id() {
357 let cases = [
358 (VideoCodecId::SorensonH263, 2, "VideoCodecId::SorensonH263"),
359 (VideoCodecId::ScreenVideo, 3, "VideoCodecId::ScreenVideo"),
360 (VideoCodecId::On2VP6, 4, "VideoCodecId::On2VP6"),
361 (
362 VideoCodecId::On2VP6WithAlphaChannel,
363 5,
364 "VideoCodecId::On2VP6WithAlphaChannel",
365 ),
366 (VideoCodecId::ScreenVideoVersion2, 6, "VideoCodecId::ScreenVideoVersion2"),
367 (VideoCodecId::Avc, 7, "VideoCodecId::Avc"),
368 (VideoCodecId(10), 10, "VideoCodecId(10)"),
369 (VideoCodecId(11), 11, "VideoCodecId(11)"),
370 (VideoCodecId(15), 15, "VideoCodecId(15)"),
371 ];
372
373 for (expected, value, name) in cases {
374 assert_eq!(VideoCodecId::from(value), expected);
375 assert_eq!(format!("{:?}", VideoCodecId::from(value)), name);
376 }
377 }
378
379 #[test]
380 fn test_command_packet() {
381 let cases = [
382 (CommandPacket::StartOfClientSeeking, 1, "CommandPacket::StartOfClientSeeking"),
383 (CommandPacket::EndOfClientSeeking, 2, "CommandPacket::EndOfClientSeeking"),
384 (CommandPacket(3), 3, "CommandPacket(3)"),
385 (CommandPacket(4), 4, "CommandPacket(4)"),
386 ];
387
388 for (expected, value, name) in cases {
389 assert_eq!(CommandPacket::from(value), expected);
390 assert_eq!(format!("{:?}", CommandPacket::from(value)), name);
391 }
392 }
393
394 #[test]
395 fn test_video_packet_type() {
396 let cases = [
397 (1, true, VideoPacketType::Enhanced(EnhancedPacketType::CodedFrames)),
398 (7, false, VideoPacketType::CodecId(VideoCodecId::Avc)),
399 ];
400
401 for (value, enhanced, expected) in cases {
402 assert_eq!(VideoPacketType::new(value, enhanced), expected);
403 }
404 }
405
406 #[test]
407 fn test_video_data_body_metadata() {
408 let mut reader = io::Cursor::new(Bytes::from_static(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]));
409 let packet_type = VideoPacketType::new(0x4, true);
410 let body = VideoTagBody::demux(packet_type, &mut reader).unwrap();
411 assert_eq!(
412 body,
413 VideoTagBody::Enhanced(EnhancedPacket::Metadata {
414 video_codec: VideoFourCC([1, 2, 3, 4]),
415 data: Bytes::from_static(&[0x05, 0x06, 0x07, 0x08]),
416 })
417 );
418 }
419
420 #[test]
421 fn test_video_data_body_avc() {
422 let mut reader = io::Cursor::new(Bytes::from_static(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]));
423 let packet_type = VideoPacketType::new(0x7, false);
424 let body = VideoTagBody::demux(packet_type, &mut reader).unwrap();
425 assert_eq!(
426 body,
427 VideoTagBody::Avc(AvcPacket::Nalu {
428 composition_time: 0x020304,
430 data: Bytes::from_static(&[0x05, 0x06, 0x07, 0x08]),
431 })
432 );
433
434 let mut reader = io::Cursor::new(Bytes::from_static(&[0x05, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]));
435 let packet_type = VideoPacketType::new(0x7, false);
436 let body = VideoTagBody::demux(packet_type, &mut reader).unwrap();
437 assert_eq!(
438 body,
439 VideoTagBody::Avc(AvcPacket::Unknown {
440 avc_packet_type: AvcPacketType(5),
441 composition_time: 0x020304,
442 data: Bytes::from_static(&[0x05, 0x06, 0x07, 0x08]),
443 })
444 );
445 }
446
447 #[test]
448 fn test_video_data_body_hevc() {
449 let mut reader = io::Cursor::new(Bytes::from_static(&[
450 b'h', b'v', b'c', b'1', 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ]));
454 let packet_type = VideoPacketType::new(0x3, true);
455 let body = VideoTagBody::demux(packet_type, &mut reader).unwrap();
456 assert_eq!(
457 body,
458 VideoTagBody::Enhanced(EnhancedPacket::Hevc(HevcPacket::Nalu {
459 composition_time: None,
460 data: Bytes::from_static(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]),
461 }))
462 );
463
464 let mut reader = io::Cursor::new(Bytes::from_static(&[
465 b'h', b'v', b'c', b'2', 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ]));
469 let packet_type = VideoPacketType::new(0x3, true);
470 let body = VideoTagBody::demux(packet_type, &mut reader).unwrap();
471 assert_eq!(
472 body,
473 VideoTagBody::Enhanced(EnhancedPacket::Unknown {
474 packet_type: EnhancedPacketType::CodedFramesX,
475 video_codec: VideoFourCC([b'h', b'v', b'c', b'2']),
476 data: Bytes::from_static(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]),
477 })
478 );
479 }
480
481 #[test]
482 fn test_video_data_body_av1() {
483 let mut reader = io::Cursor::new(Bytes::from_static(&[
484 b'a', b'v', b'0', b'1', 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ]));
488 let packet_type = VideoPacketType::new(0x04, true);
489 let body = VideoTagBody::demux(packet_type, &mut reader).unwrap();
490 assert_eq!(
491 body,
492 VideoTagBody::Enhanced(EnhancedPacket::Metadata {
493 video_codec: VideoFourCC([b'a', b'v', b'0', b'1']),
494 data: Bytes::from_static(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]),
495 })
496 );
497 }
498
499 #[test]
500 fn test_video_data_command_packet() {
501 let mut reader = io::Cursor::new(Bytes::from_static(&[
502 0b01010000, 0x01, ]));
505 let body = VideoTagHeader::demux(&mut reader).unwrap();
506 assert_eq!(
507 body,
508 VideoTagHeader {
509 frame_type: FrameType::VideoInfoOrCommandFrame,
510 body: VideoTagBody::Command(CommandPacket::StartOfClientSeeking),
511 }
512 );
513 }
514
515 #[test]
516 fn test_video_data_demux_enhanced() {
517 let mut reader = io::Cursor::new(Bytes::from_static(&[
518 0b10010010, b'a', b'v', b'0', b'1', ]));
521 let body = VideoTagHeader::demux(&mut reader).unwrap();
522 assert_eq!(
523 body,
524 VideoTagHeader {
525 frame_type: FrameType::Keyframe,
526 body: VideoTagBody::Enhanced(EnhancedPacket::SequenceEnd {
527 video_codec: VideoFourCC([b'a', b'v', b'0', b'1']),
528 }),
529 }
530 );
531 }
532
533 #[test]
534 fn test_video_data_demux_h263() {
535 let mut reader = io::Cursor::new(Bytes::from_static(&[
536 0b00010010, 0, 1, 2, 3, ]));
539 let body = VideoTagHeader::demux(&mut reader).unwrap();
540 assert_eq!(
541 body,
542 VideoTagHeader {
543 frame_type: FrameType::Keyframe,
544 body: VideoTagBody::Unknown {
545 codec_id: VideoCodecId::SorensonH263,
546 data: Bytes::from_static(&[0, 1, 2, 3]),
547 },
548 }
549 );
550 }
551
552 #[test]
553 fn test_av1_mpeg2_sequence_start() {
554 let mut reader = io::Cursor::new(Bytes::from_static(&[
555 0b10010101, b'a', b'v', b'0', b'1', 0x80, 0x4, 129, 13, 12, 0, 10, 15, 0, 0, 0, 106, 239, 191, 225, 188, 2, 25, 144, 16, 16, 16, 64,
558 ]));
559
560 let body = VideoTagHeader::demux(&mut reader).unwrap();
561 assert_eq!(
562 body,
563 VideoTagHeader {
564 frame_type: FrameType::Keyframe,
565 body: VideoTagBody::Enhanced(EnhancedPacket::Av1(Av1Packet::SequenceStart(AV1CodecConfigurationRecord {
566 seq_profile: 0,
567 seq_level_idx_0: 13,
568 seq_tier_0: false,
569 high_bitdepth: false,
570 twelve_bit: false,
571 monochrome: false,
572 chroma_subsampling_x: true,
573 chroma_subsampling_y: true,
574 chroma_sample_position: 0,
575 hdr_wcg_idc: 0,
576 initial_presentation_delay_minus_one: None,
577 config_obu: Bytes::from_static(b"\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@"),
578 }))),
579 }
580 );
581 }
582}