1#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]
20#![deny(missing_docs)]
21#![deny(unsafe_code)]
22#![deny(unreachable_pub)]
23
24pub mod audio;
25pub mod common;
26pub mod error;
27pub mod file;
28pub mod header;
29pub mod script;
30pub mod tag;
31pub mod video;
32
33#[cfg(test)]
34#[cfg_attr(all(test, coverage_nightly), coverage(off))]
35mod tests {
36 use std::io;
37 use std::path::PathBuf;
38
39 use bytes::Bytes;
40 use scuffle_aac::{AudioObjectType, PartialAudioSpecificConfig};
41 use scuffle_amf0::Amf0Value;
42 use scuffle_av1::ObuHeader;
43 use scuffle_av1::seq::SequenceHeaderObu;
44 use scuffle_bytes_util::StringCow;
45 use scuffle_h264::Sps;
46 use scuffle_h265::{ConstantFrameRate, NumTemporalLayers};
47
48 use crate::audio::AudioData;
49 use crate::audio::body::AudioTagBody;
50 use crate::audio::body::legacy::LegacyAudioTagBody;
51 use crate::audio::body::legacy::aac::AacAudioData;
52 use crate::audio::header::AudioTagHeader;
53 use crate::audio::header::legacy::{LegacyAudioTagHeader, SoundFormat, SoundRate, SoundSize, SoundType};
54 use crate::file::FlvFile;
55 use crate::script::{OnMetaDataAudioCodecId, OnMetaDataVideoCodecId, ScriptData};
56 use crate::tag::FlvTagData;
57 use crate::video::VideoData;
58 use crate::video::body::VideoTagBody;
59 use crate::video::body::enhanced::{ExVideoTagBody, VideoPacket, VideoPacketSequenceStart};
60 use crate::video::body::legacy::LegacyVideoTagBody;
61 use crate::video::header::enhanced::VideoFourCc;
62 use crate::video::header::legacy::{LegacyVideoTagHeader, LegacyVideoTagHeaderAvcPacket, VideoCodecId};
63 use crate::video::header::{VideoFrameType, VideoTagHeader, VideoTagHeaderData};
64
65 #[test]
66 fn test_demux_flv_avc_aac() {
67 let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../assets");
68
69 let data = Bytes::from(std::fs::read(dir.join("avc_aac.flv")).expect("failed to read file"));
70 let mut reader = io::Cursor::new(data);
71
72 let flv = FlvFile::demux(&mut reader).expect("failed to demux flv");
73
74 assert_eq!(flv.header.version, 1);
75 assert!(flv.header.is_audio_present);
76 assert!(flv.header.is_video_present);
77 assert_eq!(flv.header.extra.len(), 0);
78
79 let mut tags = flv.tags.into_iter();
80
81 {
83 let tag = tags.next().expect("expected tag");
84 assert_eq!(tag.timestamp_ms, 0);
85 assert_eq!(tag.stream_id, 0);
86
87 let on_meta_data = match tag.data {
89 FlvTagData::ScriptData(ScriptData::OnMetaData(data)) => data,
90 _ => panic!("expected script data"),
91 };
92
93 assert_eq!(on_meta_data.audiosamplesize, Some(16.0));
94 assert_eq!(on_meta_data.audiosamplerate, Some(48000.0));
95 assert_eq!(on_meta_data.stereo, Some(true));
96 assert_eq!(
97 on_meta_data.audiocodecid,
98 Some(OnMetaDataAudioCodecId::Legacy(SoundFormat::Aac))
99 ); assert_eq!(
101 on_meta_data.videocodecid,
102 Some(OnMetaDataVideoCodecId::Legacy(VideoCodecId::Avc))
103 ); assert_eq!(on_meta_data.duration, Some(1.088)); assert_eq!(on_meta_data.width, Some(3840.0));
106 assert_eq!(on_meta_data.height, Some(2160.0));
107 assert_eq!(on_meta_data.framerate, Some(60.0));
108 assert!(on_meta_data.videodatarate.is_some());
109 assert!(on_meta_data.audiodatarate.is_some());
110
111 let minor_version = match on_meta_data.other.get(&StringCow::from_static("minor_version")) {
113 Some(Amf0Value::String(number)) => number,
114 _ => panic!("expected minor version"),
115 };
116
117 assert_eq!(minor_version, "512");
118
119 let major_brand = match on_meta_data.other.get(&StringCow::from_static("major_brand")) {
121 Some(Amf0Value::String(string)) => string,
122 _ => panic!("expected major brand"),
123 };
124
125 assert_eq!(major_brand, "iso5");
126
127 let compatible_brands = match on_meta_data.other.get(&StringCow::from_static("compatible_brands")) {
129 Some(Amf0Value::String(string)) => string,
130 _ => panic!("expected compatible brands"),
131 };
132
133 assert_eq!(compatible_brands, "iso5iso6mp41");
134 }
135
136 {
138 let tag = tags.next().expect("expected tag");
139 assert_eq!(tag.timestamp_ms, 0);
140 assert_eq!(tag.stream_id, 0);
141
142 let (frame_type, avc_decoder_configuration_record) = match tag.data {
144 FlvTagData::Video(VideoData {
145 header: VideoTagHeader { frame_type, .. },
146 body: VideoTagBody::Legacy(LegacyVideoTagBody::AvcVideoPacketSeqHdr(avc_decoder_configuration_record)),
147 }) => (frame_type, avc_decoder_configuration_record),
148 _ => panic!("expected video data"),
149 };
150
151 assert_eq!(frame_type, VideoFrameType::KeyFrame);
152
153 assert_eq!(avc_decoder_configuration_record.profile_indication, 100);
156 assert_eq!(avc_decoder_configuration_record.profile_compatibility, 0);
157 assert_eq!(avc_decoder_configuration_record.level_indication, 51); assert_eq!(avc_decoder_configuration_record.length_size_minus_one, 3);
159 assert_eq!(avc_decoder_configuration_record.sps.len(), 1);
160 assert_eq!(avc_decoder_configuration_record.pps.len(), 1);
161 assert_eq!(avc_decoder_configuration_record.extended_config, None);
162
163 let sps =
164 Sps::parse_with_emulation_prevention(&mut std::io::Cursor::new(&avc_decoder_configuration_record.sps[0]))
165 .expect("expected sequence parameter set");
166
167 insta::assert_debug_snapshot!(sps, @r"
168 Sps {
169 nal_ref_idc: 3,
170 nal_unit_type: NALUnitType::SPS,
171 profile_idc: 100,
172 constraint_set0_flag: false,
173 constraint_set1_flag: false,
174 constraint_set2_flag: false,
175 constraint_set3_flag: false,
176 constraint_set4_flag: false,
177 constraint_set5_flag: false,
178 level_idc: 51,
179 seq_parameter_set_id: 0,
180 ext: Some(
181 SpsExtended {
182 chroma_format_idc: 1,
183 separate_color_plane_flag: false,
184 bit_depth_luma_minus8: 0,
185 bit_depth_chroma_minus8: 0,
186 qpprime_y_zero_transform_bypass_flag: false,
187 scaling_matrix: [],
188 },
189 ),
190 log2_max_frame_num_minus4: 0,
191 pic_order_cnt_type: 0,
192 log2_max_pic_order_cnt_lsb_minus4: Some(
193 4,
194 ),
195 pic_order_cnt_type1: None,
196 max_num_ref_frames: 4,
197 gaps_in_frame_num_value_allowed_flag: false,
198 pic_width_in_mbs_minus1: 239,
199 pic_height_in_map_units_minus1: 134,
200 mb_adaptive_frame_field_flag: None,
201 direct_8x8_inference_flag: true,
202 frame_crop_info: None,
203 sample_aspect_ratio: Some(
204 SarDimensions {
205 aspect_ratio_idc: AspectRatioIdc::Square,
206 sar_width: 0,
207 sar_height: 0,
208 },
209 ),
210 overscan_appropriate_flag: None,
211 color_config: None,
212 chroma_sample_loc: None,
213 timing_info: Some(
214 TimingInfo {
215 num_units_in_tick: 1,
216 time_scale: 120,
217 },
218 ),
219 }
220 ");
221 }
222
223 {
225 let tag = tags.next().expect("expected tag");
226 assert_eq!(tag.timestamp_ms, 0);
227 assert_eq!(tag.stream_id, 0);
228
229 let (data, sound_rate, sound_size, sound_type) = match tag.data {
230 FlvTagData::Audio(AudioData {
231 header:
232 AudioTagHeader::Legacy(LegacyAudioTagHeader {
233 sound_rate,
234 sound_size,
235 sound_type,
236 ..
237 }),
238 body,
239 }) => (body, sound_rate, sound_size, sound_type),
240 _ => panic!("expected audio data"),
241 };
242
243 assert_eq!(sound_rate, SoundRate::Hz44000);
244 assert_eq!(sound_size, SoundSize::Bit16);
245 assert_eq!(sound_type, SoundType::Stereo);
246
247 let data = match data {
249 AudioTagBody::Legacy(LegacyAudioTagBody::Aac(AacAudioData::SequenceHeader(data))) => data,
250 _ => panic!("expected aac sequence header"),
251 };
252
253 let aac_decoder_configuration_record =
256 PartialAudioSpecificConfig::parse(&data).expect("expected aac decoder configuration record");
257
258 assert_eq!(
259 aac_decoder_configuration_record.audio_object_type,
260 AudioObjectType::AacLowComplexity
261 );
262 assert_eq!(aac_decoder_configuration_record.sampling_frequency, 48000);
263 assert_eq!(aac_decoder_configuration_record.channel_configuration, 2);
264 }
265
266 let mut last_timestamp = 0;
268 let mut read_seq_end = false;
269 for tag in tags {
270 assert!(tag.timestamp_ms >= last_timestamp);
271 assert_eq!(tag.stream_id, 0);
272
273 last_timestamp = tag.timestamp_ms;
274
275 match tag.data {
276 FlvTagData::Audio(AudioData {
277 body,
278 header:
279 AudioTagHeader::Legacy(LegacyAudioTagHeader {
280 sound_rate,
281 sound_size,
282 sound_type,
283 ..
284 }),
285 }) => {
286 assert_eq!(sound_rate, SoundRate::Hz44000);
287 assert_eq!(sound_size, SoundSize::Bit16);
288 assert_eq!(sound_type, SoundType::Stereo);
289 match body {
290 AudioTagBody::Legacy(LegacyAudioTagBody::Aac(AacAudioData::Raw(data))) => data,
291 _ => panic!("expected aac raw packet"),
292 };
293 }
294 FlvTagData::Video(VideoData {
295 header:
296 VideoTagHeader {
297 frame_type,
298 data: VideoTagHeaderData::Legacy(data),
299 },
300 ..
301 }) => {
302 match frame_type {
303 VideoFrameType::KeyFrame => (),
304 VideoFrameType::InterFrame => (),
305 _ => panic!("expected keyframe or interframe"),
306 }
307
308 match data {
309 LegacyVideoTagHeader::AvcPacket(LegacyVideoTagHeaderAvcPacket::Nalu { .. }) => {
310 assert!(!read_seq_end)
311 }
312 LegacyVideoTagHeader::AvcPacket(LegacyVideoTagHeaderAvcPacket::EndOfSequence) => {
313 assert!(!read_seq_end);
314 read_seq_end = true;
315 }
316 _ => panic!("expected avc nalu packet: {data:?}"),
317 }
318 }
319 _ => panic!("unexpected data"),
320 };
321 }
322
323 assert!(read_seq_end);
324 }
325
326 #[test]
327 fn test_demux_flv_av1_aac() {
328 let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../assets");
329
330 let data = Bytes::from(std::fs::read(dir.join("av1_aac.flv")).expect("failed to read file"));
331 let mut reader = io::Cursor::new(data);
332
333 let flv = FlvFile::demux(&mut reader).expect("failed to demux flv");
334
335 assert_eq!(flv.header.version, 1);
336 assert!(flv.header.is_audio_present);
337 assert!(flv.header.is_video_present);
338 assert_eq!(flv.header.extra.len(), 0);
339
340 let mut tags = flv.tags.into_iter();
341
342 {
344 let tag = tags.next().expect("expected tag");
345 assert_eq!(tag.timestamp_ms, 0);
346 assert_eq!(tag.stream_id, 0);
347
348 let on_meta_data = match tag.data {
350 FlvTagData::ScriptData(ScriptData::OnMetaData(data)) => data,
351 _ => panic!("expected script data"),
352 };
353
354 assert_eq!(on_meta_data.audiosamplesize, Some(16.0));
355 assert_eq!(on_meta_data.audiosamplerate, Some(48000.0));
356 assert_eq!(on_meta_data.stereo, Some(true));
357 assert_eq!(
358 on_meta_data.audiocodecid,
359 Some(OnMetaDataAudioCodecId::Legacy(SoundFormat::Aac))
360 ); assert_eq!(
362 on_meta_data.videocodecid,
363 Some(OnMetaDataVideoCodecId::Legacy(VideoCodecId::Avc))
364 ); assert_eq!(on_meta_data.duration, Some(0.0)); assert_eq!(on_meta_data.width, Some(2560.0));
367 assert_eq!(on_meta_data.height, Some(1440.0));
368 assert_eq!(on_meta_data.framerate, Some(144.0));
369 assert!(on_meta_data.videodatarate.is_some());
370 assert!(on_meta_data.audiodatarate.is_some());
371 }
372
373 {
375 let tag = tags.next().expect("expected tag");
376 assert_eq!(tag.timestamp_ms, 0);
377 assert_eq!(tag.stream_id, 0);
378
379 let (body, sound_rate, sound_size, sound_type) = match tag.data {
380 FlvTagData::Audio(AudioData {
381 body,
382 header:
383 AudioTagHeader::Legacy(LegacyAudioTagHeader {
384 sound_rate,
385 sound_size,
386 sound_type,
387 ..
388 }),
389 }) => (body, sound_rate, sound_size, sound_type),
390 _ => panic!("expected audio data"),
391 };
392
393 assert_eq!(sound_rate, SoundRate::Hz44000);
394 assert_eq!(sound_size, SoundSize::Bit16);
395 assert_eq!(sound_type, SoundType::Stereo);
396
397 let data = match body {
399 AudioTagBody::Legacy(LegacyAudioTagBody::Aac(AacAudioData::SequenceHeader(data))) => data,
400 _ => panic!("expected aac sequence header"),
401 };
402
403 let aac_decoder_configuration_record =
406 PartialAudioSpecificConfig::parse(&data).expect("expected aac decoder configuration record");
407
408 assert_eq!(
409 aac_decoder_configuration_record.audio_object_type,
410 AudioObjectType::AacLowComplexity
411 );
412 assert_eq!(aac_decoder_configuration_record.sampling_frequency, 48000);
413 assert_eq!(aac_decoder_configuration_record.channel_configuration, 2);
414 }
415
416 {
418 let tag = tags.next().expect("expected tag");
419 assert_eq!(tag.timestamp_ms, 0);
420 assert_eq!(tag.stream_id, 0);
421
422 let frame_type = match tag.data {
424 FlvTagData::Video(VideoData {
425 header: VideoTagHeader { frame_type, .. },
426 ..
427 }) => frame_type,
428 _ => panic!("expected video data"),
429 };
430
431 assert_eq!(frame_type, VideoFrameType::KeyFrame);
432
433 let config = match tag.data {
435 FlvTagData::Video(VideoData {
436 body:
437 VideoTagBody::Enhanced(ExVideoTagBody::NoMultitrack {
438 video_four_cc: VideoFourCc::Av1,
439 packet: VideoPacket::SequenceStart(VideoPacketSequenceStart::Av1(config)),
440 }),
441 ..
442 }) => config,
443 _ => panic!("expected video data"),
444 };
445
446 assert_eq!(config.chroma_sample_position, 0);
447 assert!(config.chroma_subsampling_x); assert!(config.chroma_subsampling_y);
449 assert!(!config.high_bitdepth);
450 assert!(!config.twelve_bit);
451
452 let mut reader = std::io::Cursor::new(config.config_obu);
453
454 let header = ObuHeader::parse(&mut reader).expect("expected obu header");
455
456 let seq_obu = SequenceHeaderObu::parse(header, &mut reader).expect("expected sequence obu");
457
458 assert_eq!(seq_obu.max_frame_height, 1440);
459 assert_eq!(seq_obu.max_frame_width, 2560);
460 }
461
462 let mut last_timestamp = 0;
464 let mut read_seq_end = false;
465 for tag in tags {
466 assert!(tag.timestamp_ms >= last_timestamp || tag.timestamp_ms == 0); assert_eq!(tag.stream_id, 0);
468
469 if tag.timestamp_ms != 0 {
470 last_timestamp = tag.timestamp_ms;
471 }
472
473 match tag.data {
474 FlvTagData::Audio(AudioData {
475 body,
476 header:
477 AudioTagHeader::Legacy(LegacyAudioTagHeader {
478 sound_rate,
479 sound_size,
480 sound_type,
481 ..
482 }),
483 }) => {
484 assert_eq!(sound_rate, SoundRate::Hz44000);
485 assert_eq!(sound_size, SoundSize::Bit16);
486 assert_eq!(sound_type, SoundType::Stereo);
487 match body {
488 AudioTagBody::Legacy(LegacyAudioTagBody::Aac(AacAudioData::Raw(data))) => data,
489 _ => panic!("expected aac raw packet"),
490 };
491 }
492 FlvTagData::Video(VideoData {
493 header: VideoTagHeader { frame_type, .. },
494 body: VideoTagBody::Enhanced(body),
495 }) => {
496 match frame_type {
497 VideoFrameType::KeyFrame => (),
498 VideoFrameType::InterFrame => (),
499 _ => panic!("expected keyframe or interframe"),
500 }
501
502 match body {
503 ExVideoTagBody::NoMultitrack {
504 video_four_cc: VideoFourCc::Av1,
505 packet: VideoPacket::CodedFrames(_),
506 } => {
507 assert!(!read_seq_end);
508 }
509 ExVideoTagBody::NoMultitrack {
510 video_four_cc: VideoFourCc::Av1,
511 packet: VideoPacket::CodedFramesX { .. },
512 } => {
513 assert!(!read_seq_end);
514 }
515 ExVideoTagBody::ManyTracks(tracks) => {
516 assert!(!read_seq_end);
517 assert!(tracks.is_empty());
518 }
519 ExVideoTagBody::NoMultitrack {
520 video_four_cc: VideoFourCc::Av1,
521 packet: VideoPacket::SequenceEnd,
522 } => {
523 assert!(!read_seq_end);
524 read_seq_end = true;
525 }
526 _ => panic!("expected av1 raw packet: {body:?}"),
527 };
528 }
529 _ => panic!("unexpected data"),
530 };
531 }
532
533 assert!(read_seq_end);
534 }
535
536 #[test]
537 fn test_demux_flv_hevc_aac() {
538 let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../assets");
539
540 let data = Bytes::from(std::fs::read(dir.join("hevc_aac.flv")).expect("failed to read file"));
541 let mut reader = io::Cursor::new(data);
542
543 let flv = FlvFile::demux(&mut reader).expect("failed to demux flv");
544
545 assert_eq!(flv.header.version, 1);
546 assert!(flv.header.is_audio_present);
547 assert!(flv.header.is_video_present);
548 assert_eq!(flv.header.extra.len(), 0);
549
550 let mut tags = flv.tags.into_iter();
551
552 {
554 let tag = tags.next().expect("expected tag");
555 assert_eq!(tag.timestamp_ms, 0);
556 assert_eq!(tag.stream_id, 0);
557
558 let on_meta_data = match tag.data {
559 FlvTagData::ScriptData(ScriptData::OnMetaData(data)) => data,
560 _ => panic!("expected script data"),
561 };
562
563 assert_eq!(on_meta_data.audiosamplesize, Some(16.0));
564 assert_eq!(on_meta_data.audiosamplerate, Some(48000.0));
565 assert_eq!(on_meta_data.stereo, Some(true));
566 assert_eq!(
567 on_meta_data.audiocodecid,
568 Some(OnMetaDataAudioCodecId::Legacy(SoundFormat::Aac))
569 ); assert_eq!(
571 on_meta_data.videocodecid,
572 Some(OnMetaDataVideoCodecId::Enhanced(VideoFourCc::Hevc))
573 ); assert_eq!(on_meta_data.duration, Some(2.038));
575 assert_eq!(on_meta_data.width, Some(3840.0));
576 assert_eq!(on_meta_data.height, Some(2160.0));
577 assert_eq!(on_meta_data.framerate, Some(60.0));
578 assert!(on_meta_data.videodatarate.is_some());
579 assert!(on_meta_data.audiodatarate.is_some());
580 }
581
582 {
584 let tag = tags.next().expect("expected tag");
585 assert_eq!(tag.timestamp_ms, 0);
586 assert_eq!(tag.stream_id, 0);
587
588 let (frame_type, config) = match tag.data {
590 FlvTagData::Video(VideoData {
591 header: VideoTagHeader { frame_type, .. },
592 body:
593 VideoTagBody::Enhanced(ExVideoTagBody::NoMultitrack {
594 video_four_cc: VideoFourCc::Hevc,
595 packet: VideoPacket::SequenceStart(VideoPacketSequenceStart::Hevc(config)),
596 }),
597 }) => (frame_type, config),
598 _ => panic!("expected video data"),
599 };
600
601 assert_eq!(frame_type, VideoFrameType::KeyFrame);
602
603 assert_eq!(config.avg_frame_rate, 0);
604 assert_eq!(config.constant_frame_rate, ConstantFrameRate::Unknown);
605 assert_eq!(config.num_temporal_layers, NumTemporalLayers::NotScalable);
606
607 let Some(sps) = config
609 .arrays
610 .iter()
611 .find(|a| a.nal_unit_type == scuffle_h265::NALUnitType::SpsNut)
612 .and_then(|v| v.nalus.first())
613 else {
614 panic!("expected sps");
615 };
616
617 let Some(_) = config
619 .arrays
620 .iter()
621 .find(|a| a.nal_unit_type == scuffle_h265::NALUnitType::PpsNut)
622 .and_then(|v| v.nalus.first())
623 else {
624 panic!("expected pps");
625 };
626
627 let sps = scuffle_h265::SpsNALUnit::parse(io::Cursor::new(sps.clone())).expect("expected sps");
629
630 insta::assert_debug_snapshot!(sps);
631 }
632
633 {
635 let tag = tags.next().expect("expected tag");
636 assert_eq!(tag.timestamp_ms, 0);
637 assert_eq!(tag.stream_id, 0);
638
639 let (body, sound_rate, sound_size, sound_type) = match tag.data {
640 FlvTagData::Audio(AudioData {
641 body,
642 header:
643 AudioTagHeader::Legacy(LegacyAudioTagHeader {
644 sound_rate,
645 sound_size,
646 sound_type,
647 ..
648 }),
649 }) => (body, sound_rate, sound_size, sound_type),
650 _ => panic!("expected audio data"),
651 };
652
653 assert_eq!(sound_rate, SoundRate::Hz44000);
654 assert_eq!(sound_size, SoundSize::Bit16);
655 assert_eq!(sound_type, SoundType::Stereo);
656
657 let data = match body {
659 AudioTagBody::Legacy(LegacyAudioTagBody::Aac(AacAudioData::SequenceHeader(data))) => data,
660 _ => panic!("expected aac sequence header"),
661 };
662
663 let aac_decoder_configuration_record =
666 PartialAudioSpecificConfig::parse(&data).expect("expected aac decoder configuration record");
667
668 assert_eq!(
669 aac_decoder_configuration_record.audio_object_type,
670 AudioObjectType::AacLowComplexity
671 );
672 assert_eq!(aac_decoder_configuration_record.sampling_frequency, 48000);
673 assert_eq!(aac_decoder_configuration_record.channel_configuration, 2);
674 }
675
676 let mut last_timestamp = 0;
678 for tag in tags {
679 assert!(tag.timestamp_ms >= last_timestamp || tag.timestamp_ms == 0); assert_eq!(tag.stream_id, 0);
681
682 if tag.timestamp_ms != 0 {
683 last_timestamp = tag.timestamp_ms;
684 }
685
686 match tag.data {
687 FlvTagData::Audio(AudioData {
688 body,
689 header:
690 AudioTagHeader::Legacy(LegacyAudioTagHeader {
691 sound_rate,
692 sound_size,
693 sound_type,
694 ..
695 }),
696 }) => {
697 assert_eq!(sound_rate, SoundRate::Hz44000);
698 assert_eq!(sound_size, SoundSize::Bit16);
699 assert_eq!(sound_type, SoundType::Stereo);
700 match body {
701 AudioTagBody::Legacy(LegacyAudioTagBody::Aac(AacAudioData::Raw(data))) => data,
702 _ => panic!("expected aac raw packet"),
703 };
704 }
705 FlvTagData::Video(VideoData {
706 header: VideoTagHeader { frame_type, .. },
707 body:
708 VideoTagBody::Enhanced(ExVideoTagBody::NoMultitrack {
709 video_four_cc: VideoFourCc::Hevc,
710 ..
711 }),
712 }) => match frame_type {
713 VideoFrameType::KeyFrame => (),
714 VideoFrameType::InterFrame => (),
715 VideoFrameType::Command => (),
716 _ => panic!("expected keyframe, interframe or command"),
717 },
718 _ => panic!("unexpected data"),
719 };
720 }
721 }
722}