use alloc::vec::Vec;
use broadcast_common::Serialize;
use crate::aac_asc::HeAacProfile;
use crate::ac3::{Ac3SpecificBox, Ec3SpecificBox};
use crate::ac4::Ac4SpecificBox;
use crate::av1::Av1ConfigurationBox;
use crate::avc_config::AVCConfigurationBox;
use crate::dts::DtsSpecificBox;
use crate::error::{Error, Result};
use crate::flac::FlacSpecificBox;
use crate::hevc_config::HEVCConfigurationBox;
use crate::mp4esds::{DecoderSpecificInfo, EsdsBox};
use crate::opus::OpusSpecificBox;
use crate::vp9::Vp9ConfigurationBox;
use crate::vvc_config::VvcConfigurationBox;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum DataCarriage {
Pes,
Sections,
}
impl DataCarriage {
pub fn name(&self) -> &'static str {
match self {
DataCarriage::Pes => "pes",
DataCarriage::Sections => "sections",
}
}
}
broadcast_common::impl_spec_display!(DataCarriage);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SubtitleFormat {
Ttml,
WebVtt,
DvbBitmap,
Teletext,
}
impl SubtitleFormat {
pub fn name(&self) -> &'static str {
match self {
SubtitleFormat::Ttml => "TTML (stpp)",
SubtitleFormat::WebVtt => "WebVTT (wvtt)",
SubtitleFormat::DvbBitmap => "DVB bitmap subtitle",
SubtitleFormat::Teletext => "Teletext",
}
}
}
broadcast_common::impl_spec_display!(SubtitleFormat);
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum SubtitleSampleEntry {
Ttml(crate::subtitle_entries::XmlSubtitleSampleEntry),
WebVtt(crate::subtitle_entries::WvttSampleEntry),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum CodecConfig {
Avc {
config: AVCConfigurationBox,
width: u16,
height: u16,
},
Hevc {
config: HEVCConfigurationBox,
width: u16,
height: u16,
},
Vvc {
config: VvcConfigurationBox,
width: u16,
height: u16,
},
Aac {
esds: EsdsBox,
channel_count: u16,
sample_rate: u32,
sample_size: u16,
},
Ac3 {
config: Ac3SpecificBox,
channel_count: u16,
sample_rate: u32,
sample_size: u16,
},
Eac3 {
config: Ec3SpecificBox,
channel_count: u16,
sample_rate: u32,
sample_size: u16,
},
Av1 {
config: Av1ConfigurationBox,
width: u16,
height: u16,
},
Vp9 {
config: Vp9ConfigurationBox,
width: u16,
height: u16,
},
Opus {
config: OpusSpecificBox,
channel_count: u16,
sample_rate: u32,
sample_size: u16,
},
Flac {
config: FlacSpecificBox,
channel_count: u16,
sample_rate: u32,
sample_size: u16,
},
Ac4 {
config: Ac4SpecificBox,
channel_count: u16,
sample_rate: u32,
sample_size: u16,
},
MpegH {
config: crate::mpegh::MHADecoderConfigurationRecord,
channel_count: u16,
sample_rate: u32,
sample_size: u16,
},
Mpeg2Video {
esds: EsdsBox,
width: u16,
height: u16,
},
MpegAudio {
esds: EsdsBox,
layer: crate::mpeg_legacy::MpegAudioLayer,
channel_count: u16,
sample_rate: u32,
sample_size: u16,
},
Dts {
config: DtsSpecificBox,
codec_fourcc: [u8; 4],
channel_count: u16,
sample_rate: u32,
sample_size: u16,
},
Vp8 {
width: u16,
height: u16,
},
Vorbis {
codec_private: Vec<u8>,
channels: u16,
sample_rate: u32,
},
Subtitle {
format: SubtitleFormat,
sample_entry: Option<SubtitleSampleEntry>,
},
Data {
stream_type: u8,
descriptors: Vec<u8>,
carriage: DataCarriage,
},
}
const HE_AAC_V2_OUTPUT_CHANNELS: u16 = 2;
impl CodecConfig {
pub fn with_explicit_he_aac(self, profile: HeAacProfile) -> Result<Self> {
let CodecConfig::Aac {
mut esds,
channel_count,
sample_rate,
sample_size,
} = self
else {
return Err(Error::UnsupportedCodec {
codec: "explicit HE-AAC signalling needs an AAC track",
});
};
let asc = esds
.audio_specific_config()?
.with_explicit_he_aac(profile)?;
let bytes = asc.try_to_bytes()?;
let sample_rate = asc.extension_sampling_frequency().unwrap_or(sample_rate);
if let Some(dc) = esds.es_descriptor.decoder_config.as_mut() {
dc.decoder_specific_info = Some(DecoderSpecificInfo::new(bytes));
}
let channel_count = match profile {
HeAacProfile::V1 => match crate::flv::aac_channel_count(&asc) {
crate::flv::AAC_CHANNEL_COUNT_UNKNOWN => channel_count,
n => n,
},
HeAacProfile::V2 => HE_AAC_V2_OUTPUT_CHANNELS,
};
Ok(CodecConfig::Aac {
esds,
channel_count,
sample_rate,
sample_size,
})
}
pub(crate) fn is_audio(&self) -> bool {
matches!(
self,
CodecConfig::Aac { .. }
| CodecConfig::Ac3 { .. }
| CodecConfig::Eac3 { .. }
| CodecConfig::Opus { .. }
| CodecConfig::Flac { .. }
| CodecConfig::Ac4 { .. }
| CodecConfig::MpegH { .. }
| CodecConfig::Dts { .. }
| CodecConfig::MpegAudio { .. }
| CodecConfig::Vorbis { .. }
)
}
pub fn audio_sample_rate(&self) -> Option<u32> {
match self {
CodecConfig::Aac { sample_rate, .. }
| CodecConfig::Ac3 { sample_rate, .. }
| CodecConfig::Eac3 { sample_rate, .. }
| CodecConfig::Opus { sample_rate, .. }
| CodecConfig::Flac { sample_rate, .. }
| CodecConfig::Ac4 { sample_rate, .. }
| CodecConfig::MpegH { sample_rate, .. }
| CodecConfig::Dts { sample_rate, .. }
| CodecConfig::MpegAudio { sample_rate, .. }
| CodecConfig::Vorbis { sample_rate, .. } => Some(*sample_rate),
_ => None,
}
}
pub(crate) fn audio_channel_field(&self) -> Option<u16> {
match self {
CodecConfig::Aac { channel_count, .. }
| CodecConfig::Ac3 { channel_count, .. }
| CodecConfig::Eac3 { channel_count, .. }
| CodecConfig::Opus { channel_count, .. }
| CodecConfig::Flac { channel_count, .. }
| CodecConfig::Ac4 { channel_count, .. }
| CodecConfig::MpegH { channel_count, .. }
| CodecConfig::Dts { channel_count, .. }
| CodecConfig::MpegAudio { channel_count, .. } => Some(*channel_count),
CodecConfig::Vorbis { channels, .. } => Some(*channels),
_ => None,
}
}
pub fn channels_with_lfe(&self) -> Option<u16> {
let n = match self {
CodecConfig::Ac3 { config, .. } => u16::from(config.channels_with_lfe()),
CodecConfig::Eac3 { config, .. } => u16::from(config.channels_with_lfe()),
CodecConfig::Aac { channel_count, .. }
| CodecConfig::Opus { channel_count, .. }
| CodecConfig::Flac { channel_count, .. }
| CodecConfig::Ac4 { channel_count, .. }
| CodecConfig::MpegH { channel_count, .. }
| CodecConfig::Dts { channel_count, .. }
| CodecConfig::MpegAudio { channel_count, .. } => *channel_count,
CodecConfig::Vorbis { channels, .. } => *channels,
_ => return None,
};
(n != 0).then_some(n)
}
pub fn nominal_bitrate(&self) -> Option<u32> {
let bps = match self {
CodecConfig::Ac3 { config, .. } => config.nominal_bitrate_kbps()?.checked_mul(1000)?,
CodecConfig::Eac3 { config, .. } => config.nominal_bitrate_kbps().checked_mul(1000)?,
CodecConfig::Aac { esds, .. } | CodecConfig::MpegAudio { esds, .. } => esds
.es_descriptor
.decoder_config
.as_ref()
.map_or(0, |dc| dc.avg_bitrate),
CodecConfig::Dts { config, .. } => config.avg_bitrate,
_ => return None,
};
(bps != 0).then_some(bps)
}
pub(crate) fn is_opaque_data(&self) -> bool {
matches!(self, CodecConfig::Data { .. })
}
pub(crate) fn is_subtitle_without_entry(&self) -> bool {
matches!(
self,
CodecConfig::Subtitle {
sample_entry: None,
..
}
)
}
pub fn is_muxable_in_bmff(&self) -> bool {
!(self.is_opaque_data() || self.is_subtitle_without_entry())
}
pub fn is_video(&self) -> bool {
matches!(
self,
CodecConfig::Avc { .. }
| CodecConfig::Hevc { .. }
| CodecConfig::Vvc { .. }
| CodecConfig::Av1 { .. }
| CodecConfig::Vp9 { .. }
| CodecConfig::Vp8 { .. }
| CodecConfig::Mpeg2Video { .. }
)
}
}