mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-09-05 11:07:37 +00:00
fmp4mux: Add support for AC-3 / EAC-3
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2208>
This commit is contained in:
parent
8256601d1d
commit
03e9a9f0fe
4 changed files with 638 additions and 8 deletions
|
@ -3253,7 +3253,7 @@
|
||||||
"long-name": "CMAFMux",
|
"long-name": "CMAFMux",
|
||||||
"pad-templates": {
|
"pad-templates": {
|
||||||
"sink": {
|
"sink": {
|
||||||
"caps": "video/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-av1:\n stream-format: obu-stream\n alignment: tu\n profile: { (string)main, (string)high, (string)professional }\n chroma-format: { (string)4:0:0, (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\naudio/mpeg:\n mpegversion: 4\n stream-format: raw\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\naudio/x-opus:\nchannel-mapping-family: [ 0, 255 ]\n channels: [ 1, 8 ]\n rate: [ 1, 2147483647 ]\n",
|
"caps": "video/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-av1:\n stream-format: obu-stream\n alignment: tu\n profile: { (string)main, (string)high, (string)professional }\n chroma-format: { (string)4:0:0, (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\naudio/mpeg:\n mpegversion: 4\n stream-format: raw\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\naudio/x-opus:\nchannel-mapping-family: [ 0, 255 ]\n channels: [ 1, 8 ]\n rate: [ 1, 2147483647 ]\naudio/x-eac3:\n framed: true\n alignment: iec61937\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\n",
|
||||||
"direction": "sink",
|
"direction": "sink",
|
||||||
"presence": "always",
|
"presence": "always",
|
||||||
"type": "GstFMP4MuxPad"
|
"type": "GstFMP4MuxPad"
|
||||||
|
@ -3290,7 +3290,7 @@
|
||||||
"long-name": "DASHMP4Mux",
|
"long-name": "DASHMP4Mux",
|
||||||
"pad-templates": {
|
"pad-templates": {
|
||||||
"sink": {
|
"sink": {
|
||||||
"caps": "video/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp8:\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp9:\n profile: { (string)0, (string)1, (string)2, (string)3 }\n chroma-format: { (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-av1:\n stream-format: obu-stream\n alignment: tu\n profile: { (string)main, (string)high, (string)professional }\n chroma-format: { (string)4:0:0, (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\naudio/mpeg:\n mpegversion: 4\n stream-format: raw\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\naudio/x-opus:\nchannel-mapping-family: [ 0, 255 ]\n channels: [ 1, 8 ]\n rate: [ 1, 2147483647 ]\n",
|
"caps": "video/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp8:\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp9:\n profile: { (string)0, (string)1, (string)2, (string)3 }\n chroma-format: { (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-av1:\n stream-format: obu-stream\n alignment: tu\n profile: { (string)main, (string)high, (string)professional }\n chroma-format: { (string)4:0:0, (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\naudio/mpeg:\n mpegversion: 4\n stream-format: raw\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\naudio/x-opus:\nchannel-mapping-family: [ 0, 255 ]\n channels: [ 1, 8 ]\n rate: [ 1, 2147483647 ]\naudio/x-ac3:\n framed: true\n alignment: frame\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\naudio/x-eac3:\n framed: true\n alignment: iec61937\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\n",
|
||||||
"direction": "sink",
|
"direction": "sink",
|
||||||
"presence": "always",
|
"presence": "always",
|
||||||
"type": "GstFMP4MuxPad"
|
"type": "GstFMP4MuxPad"
|
||||||
|
@ -3319,7 +3319,7 @@
|
||||||
"long-name": "ISOFMP4Mux",
|
"long-name": "ISOFMP4Mux",
|
||||||
"pad-templates": {
|
"pad-templates": {
|
||||||
"sink_%%u": {
|
"sink_%%u": {
|
||||||
"caps": "video/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp8:\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp9:\n profile: { (string)0, (string)1, (string)2, (string)3 }\n chroma-format: { (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-av1:\n stream-format: obu-stream\n alignment: tu\n profile: { (string)main, (string)high, (string)professional }\n chroma-format: { (string)4:0:0, (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\naudio/mpeg:\n mpegversion: 4\n stream-format: raw\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\naudio/x-opus:\nchannel-mapping-family: [ 0, 255 ]\n channels: [ 1, 8 ]\n rate: [ 1, 2147483647 ]\naudio/x-flac:\n framed: true\n channels: [ 1, 8 ]\n rate: [ 1, 655350 ]\n",
|
"caps": "video/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp8:\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp9:\n profile: { (string)0, (string)1, (string)2, (string)3 }\n chroma-format: { (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-av1:\n stream-format: obu-stream\n alignment: tu\n profile: { (string)main, (string)high, (string)professional }\n chroma-format: { (string)4:0:0, (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\naudio/mpeg:\n mpegversion: 4\n stream-format: raw\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\naudio/x-opus:\nchannel-mapping-family: [ 0, 255 ]\n channels: [ 1, 8 ]\n rate: [ 1, 2147483647 ]\naudio/x-flac:\n framed: true\n channels: [ 1, 8 ]\n rate: [ 1, 655350 ]\naudio/x-ac3:\n framed: true\n alignment: frame\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\naudio/x-eac3:\n framed: true\n alignment: iec61937\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\n",
|
||||||
"direction": "sink",
|
"direction": "sink",
|
||||||
"presence": "request",
|
"presence": "request",
|
||||||
"type": "GstFMP4MuxPad"
|
"type": "GstFMP4MuxPad"
|
||||||
|
|
|
@ -164,6 +164,9 @@ fn cmaf_brands_from_caps(caps: &gst::CapsRef, compatible_brands: &mut Vec<&'stat
|
||||||
"audio/mpeg" => {
|
"audio/mpeg" => {
|
||||||
compatible_brands.push(b"caac");
|
compatible_brands.push(b"caac");
|
||||||
}
|
}
|
||||||
|
"audio/x-eac3" => {
|
||||||
|
compatible_brands.push(b"ceac");
|
||||||
|
}
|
||||||
"audio/x-opus" => {
|
"audio/x-opus" => {
|
||||||
compatible_brands.push(b"opus");
|
compatible_brands.push(b"opus");
|
||||||
}
|
}
|
||||||
|
@ -671,7 +674,7 @@ fn write_tkhd(
|
||||||
let s = stream.caps.structure(0).unwrap();
|
let s = stream.caps.structure(0).unwrap();
|
||||||
match s.name().as_str() {
|
match s.name().as_str() {
|
||||||
"audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw"
|
"audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw"
|
||||||
| "audio/x-adpcm" => v.extend((1u16 << 8).to_be_bytes()),
|
| "audio/x-adpcm" | "audio/x-ac3" | "audio/x-eac3" => v.extend((1u16 << 8).to_be_bytes()),
|
||||||
_ => v.extend(0u16.to_be_bytes()),
|
_ => v.extend(0u16.to_be_bytes()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -798,7 +801,9 @@ fn write_hdlr(
|
||||||
"video/x-h264" | "video/x-h265" | "video/x-vp8" | "video/x-vp9" | "video/x-av1"
|
"video/x-h264" | "video/x-h265" | "video/x-vp8" | "video/x-vp9" | "video/x-av1"
|
||||||
| "image/jpeg" => (b"vide", b"VideoHandler\0".as_slice()),
|
| "image/jpeg" => (b"vide", b"VideoHandler\0".as_slice()),
|
||||||
"audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw"
|
"audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw"
|
||||||
| "audio/x-adpcm" => (b"soun", b"SoundHandler\0".as_slice()),
|
| "audio/x-adpcm" | "audio/x-ac3" | "audio/x-eac3" => {
|
||||||
|
(b"soun", b"SoundHandler\0".as_slice())
|
||||||
|
}
|
||||||
"application/x-onvif-metadata" => (b"meta", b"MetadataHandler\0".as_slice()),
|
"application/x-onvif-metadata" => (b"meta", b"MetadataHandler\0".as_slice()),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
@ -829,7 +834,7 @@ fn write_minf(
|
||||||
write_full_box(v, b"vmhd", FULL_BOX_VERSION_0, 1, |v| write_vmhd(v, cfg))?
|
write_full_box(v, b"vmhd", FULL_BOX_VERSION_0, 1, |v| write_vmhd(v, cfg))?
|
||||||
}
|
}
|
||||||
"audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw"
|
"audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw"
|
||||||
| "audio/x-adpcm" => {
|
| "audio/x-adpcm" | "audio/x-ac3" | "audio/x-eac3" => {
|
||||||
write_full_box(v, b"smhd", FULL_BOX_VERSION_0, FULL_BOX_FLAGS_NONE, |v| {
|
write_full_box(v, b"smhd", FULL_BOX_VERSION_0, FULL_BOX_FLAGS_NONE, |v| {
|
||||||
write_smhd(v, cfg)
|
write_smhd(v, cfg)
|
||||||
})?
|
})?
|
||||||
|
@ -939,7 +944,9 @@ fn write_stsd(
|
||||||
"video/x-h264" | "video/x-h265" | "video/x-vp8" | "video/x-vp9" | "video/x-av1"
|
"video/x-h264" | "video/x-h265" | "video/x-vp8" | "video/x-vp9" | "video/x-av1"
|
||||||
| "image/jpeg" => write_visual_sample_entry(v, cfg, stream)?,
|
| "image/jpeg" => write_visual_sample_entry(v, cfg, stream)?,
|
||||||
"audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw"
|
"audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw"
|
||||||
| "audio/x-adpcm" => write_audio_sample_entry(v, cfg, stream)?,
|
| "audio/x-adpcm" | "audio/x-ac3" | "audio/x-eac3" => {
|
||||||
|
write_audio_sample_entry(v, cfg, stream)?
|
||||||
|
}
|
||||||
"application/x-onvif-metadata" => write_xml_meta_data_sample_entry(v, cfg, stream)?,
|
"application/x-onvif-metadata" => write_xml_meta_data_sample_entry(v, cfg, stream)?,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -1382,6 +1389,8 @@ fn write_audio_sample_entry(
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"audio/x-ac3" => b"ac-3",
|
||||||
|
"audio/x-eac3" => b"ec-3",
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1442,6 +1451,16 @@ fn write_audio_sample_entry(
|
||||||
"audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => {
|
"audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => {
|
||||||
// Nothing to do here
|
// Nothing to do here
|
||||||
}
|
}
|
||||||
|
"audio/x-ac3" => {
|
||||||
|
assert!(!stream.codec_specific_boxes.is_empty());
|
||||||
|
assert!(&stream.codec_specific_boxes[4..8] == b"dac3");
|
||||||
|
v.extend_from_slice(&stream.codec_specific_boxes);
|
||||||
|
}
|
||||||
|
"audio/x-eac3" => {
|
||||||
|
assert!(!stream.codec_specific_boxes.is_empty());
|
||||||
|
assert!(&stream.codec_specific_boxes[4..8] == b"dec3");
|
||||||
|
v.extend_from_slice(&stream.codec_specific_boxes);
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2388,3 +2407,532 @@ pub(crate) fn create_mfra(
|
||||||
|
|
||||||
Ok(gst::Buffer::from_mut_slice(v))
|
Ok(gst::Buffer::from_mut_slice(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create AC-3 `dac3` box.
|
||||||
|
pub(crate) fn create_dac3(buffer: &gst::BufferRef) -> Result<Vec<u8>, Error> {
|
||||||
|
use bitstream_io::{BitRead as _, BitWrite as _};
|
||||||
|
|
||||||
|
let map = buffer
|
||||||
|
.map_readable()
|
||||||
|
.context("Mapping AC-3 buffer readable")?;
|
||||||
|
let mut reader = bitstream_io::BitReader::endian(
|
||||||
|
std::io::Cursor::new(map.as_slice()),
|
||||||
|
bitstream_io::BigEndian,
|
||||||
|
);
|
||||||
|
|
||||||
|
let header = reader
|
||||||
|
.parse::<ac3::Header>()
|
||||||
|
.context("Parsing AC-3 header")?;
|
||||||
|
|
||||||
|
let mut dac3 = Vec::with_capacity(11);
|
||||||
|
let mut writer = bitstream_io::BitWriter::endian(&mut dac3, bitstream_io::BigEndian);
|
||||||
|
writer
|
||||||
|
.build(&ac3::Dac3 { header })
|
||||||
|
.context("Writing dac3 box")?;
|
||||||
|
|
||||||
|
Ok(dac3)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create EAC-3 `dec3` box.
|
||||||
|
pub(crate) fn create_dec3(buffer: &gst::BufferRef) -> Result<Vec<u8>, Error> {
|
||||||
|
use bitstream_io::{BitRead as _, BitWrite as _};
|
||||||
|
|
||||||
|
let map = buffer
|
||||||
|
.map_readable()
|
||||||
|
.context("Mapping EAC-3 buffer readable")?;
|
||||||
|
|
||||||
|
let mut slice = map.as_slice();
|
||||||
|
let mut headers = Vec::new();
|
||||||
|
|
||||||
|
while !slice.is_empty() {
|
||||||
|
let mut reader =
|
||||||
|
bitstream_io::BitReader::endian(std::io::Cursor::new(slice), bitstream_io::BigEndian);
|
||||||
|
let header = reader
|
||||||
|
.parse::<eac3::Header>()
|
||||||
|
.context("Parsing EAC-3 header")?;
|
||||||
|
|
||||||
|
let framesize = (header.bsi.frmsiz as usize + 1) * 2;
|
||||||
|
if slice.len() < framesize {
|
||||||
|
bail!("Incomplete EAC-3 frame");
|
||||||
|
}
|
||||||
|
|
||||||
|
headers.push(header);
|
||||||
|
|
||||||
|
slice = &slice[framesize..];
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dec3 = Vec::new();
|
||||||
|
let mut writer = bitstream_io::BitWriter::endian(&mut dec3, bitstream_io::BigEndian);
|
||||||
|
writer
|
||||||
|
.build(&eac3::Dec3 { headers })
|
||||||
|
.context("Writing dec3 box")?;
|
||||||
|
|
||||||
|
Ok(dec3)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod ac3 {
|
||||||
|
use anyhow::{bail, Context, Error};
|
||||||
|
use bitstream_io::{FromBitStream, ToBitStream};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Header {
|
||||||
|
syncinfo: SyncInfo,
|
||||||
|
bsi: Bsi,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBitStream for Header {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn from_reader<R: bitstream_io::BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let syncinfo = r.parse::<SyncInfo>().context("syncinfo")?;
|
||||||
|
let bsi = r.parse::<Bsi>().context("bsi")?;
|
||||||
|
|
||||||
|
Ok(Header { syncinfo, bsi })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct SyncInfo {
|
||||||
|
// skipping crc1
|
||||||
|
fscod: u8,
|
||||||
|
frmsizecod: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBitStream for SyncInfo {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn from_reader<R: bitstream_io::BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let _syncword = r.read_to::<u16>().context("syncword")?;
|
||||||
|
if _syncword != 0x0b77 {
|
||||||
|
bail!("Invalid syncword");
|
||||||
|
}
|
||||||
|
|
||||||
|
r.skip(16).context("crc1")?;
|
||||||
|
|
||||||
|
let fscod = r.read::<2, u8>().context("fscod")?;
|
||||||
|
let frmsizecod = r.read::<6, u8>().context("frmsizecod")?;
|
||||||
|
|
||||||
|
Ok(SyncInfo { fscod, frmsizecod })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct Bsi {
|
||||||
|
bsid: u8,
|
||||||
|
bsmod: u8,
|
||||||
|
acmod: u8,
|
||||||
|
// skipping cmixlev, surmixlev, dsurmod
|
||||||
|
lfeon: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBitStream for Bsi {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn from_reader<R: bitstream_io::BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let bsid = r.read::<5, u8>().context("bsid")?;
|
||||||
|
let bsmod = r.read::<3, u8>().context("bsmod")?;
|
||||||
|
let acmod = r.read::<3, u8>().context("acmod")?;
|
||||||
|
|
||||||
|
if acmod & 0x01 != 0 && acmod != 0x01 {
|
||||||
|
r.skip(2).context("cmixlev")?;
|
||||||
|
}
|
||||||
|
if acmod & 0x04 != 0 {
|
||||||
|
r.skip(2).context("surmixlev")?;
|
||||||
|
}
|
||||||
|
if acmod == 0x02 {
|
||||||
|
r.skip(2).context("dsurmod")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lfeon = r.read_bit().context("lfeon")?;
|
||||||
|
|
||||||
|
Ok(Bsi {
|
||||||
|
bsid,
|
||||||
|
bsmod,
|
||||||
|
acmod,
|
||||||
|
lfeon,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub(crate) struct Dac3 {
|
||||||
|
pub header: Header,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToBitStream for Dac3 {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn to_writer<W: bitstream_io::BitWrite + ?Sized>(
|
||||||
|
&self,
|
||||||
|
w: &mut W,
|
||||||
|
) -> Result<(), Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
w.write_from::<u32>(11).context("size")?;
|
||||||
|
w.write_bytes(b"dac3").context("type")?;
|
||||||
|
|
||||||
|
w.write::<2, u8>(self.header.syncinfo.fscod)
|
||||||
|
.context("fscod")?;
|
||||||
|
w.write::<5, u8>(self.header.bsi.bsid).context("bsid")?;
|
||||||
|
w.write::<3, u8>(self.header.bsi.bsmod).context("bsmod")?;
|
||||||
|
w.write::<3, u8>(self.header.bsi.acmod).context("acmod")?;
|
||||||
|
w.write_bit(self.header.bsi.lfeon).context("lfeon")?;
|
||||||
|
w.write::<5, u8>(self.header.syncinfo.frmsizecod >> 1)
|
||||||
|
.context("bit_rate_code")?;
|
||||||
|
w.write::<5, u8>(0).context("reserved")?;
|
||||||
|
|
||||||
|
assert!(w.byte_aligned());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod eac3 {
|
||||||
|
use anyhow::{bail, Context, Error};
|
||||||
|
use bitstream_io::{FromBitStream, ToBitStream};
|
||||||
|
|
||||||
|
const NUM_BLOCKS: [u8; 4] = [1, 2, 3, 6];
|
||||||
|
const SAMPLE_RATES: [u16; 4] = [48000, 44100, 32000, 0];
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Header {
|
||||||
|
#[expect(unused)]
|
||||||
|
pub syncinfo: SyncInfo,
|
||||||
|
pub bsi: Bsi,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBitStream for Header {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn from_reader<R: bitstream_io::BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let syncinfo = r.parse::<SyncInfo>().context("syncinfo")?;
|
||||||
|
let bsi = r.parse::<Bsi>().context("bsi")?;
|
||||||
|
|
||||||
|
Ok(Header { syncinfo, bsi })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct SyncInfo {
|
||||||
|
// No fields for EAC-3
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBitStream for SyncInfo {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn from_reader<R: bitstream_io::BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let _syncword = r.read_to::<u16>().context("syncword")?;
|
||||||
|
if _syncword != 0x0b77 {
|
||||||
|
bail!("Invalid syncword");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SyncInfo {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Bsi {
|
||||||
|
#[expect(unused)]
|
||||||
|
pub strmtyp: u8,
|
||||||
|
pub substreamid: u8,
|
||||||
|
pub frmsiz: u16,
|
||||||
|
pub fscod: u8,
|
||||||
|
pub fscod2: Option<u8>,
|
||||||
|
pub numblkscod: u8,
|
||||||
|
pub acmod: u8,
|
||||||
|
pub lfeon: bool,
|
||||||
|
pub bsid: u8,
|
||||||
|
// skipping dialnorm, compre, compr, dialnorm2, compr2e
|
||||||
|
pub chanmap: Option<u16>,
|
||||||
|
// skipping ...
|
||||||
|
pub bsmod: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBitStream for Bsi {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn from_reader<R: bitstream_io::BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let strmtyp = r.read::<2, u8>().context("strmtyp")?;
|
||||||
|
let substreamid = r.read::<3, u8>().context("substreamid")?;
|
||||||
|
let frmsiz = r.read::<11, u16>().context("frmsiz")?;
|
||||||
|
let fscod = r.read::<2, u8>().context("fscod")?;
|
||||||
|
|
||||||
|
let fscod2;
|
||||||
|
let numblkscod;
|
||||||
|
if fscod == 0x3 {
|
||||||
|
fscod2 = Some(r.read::<2, u8>().context("fscod2")?);
|
||||||
|
|
||||||
|
numblkscod = 6;
|
||||||
|
} else {
|
||||||
|
fscod2 = None;
|
||||||
|
numblkscod = r.read::<2, u8>().context("numblkscod")?;
|
||||||
|
}
|
||||||
|
let number_of_blocks_per_sync_frame = NUM_BLOCKS[numblkscod as usize];
|
||||||
|
|
||||||
|
let acmod = r.read::<3, u8>().context("acmod")?;
|
||||||
|
let lfeon = r.read_bit().context("lfeon")?;
|
||||||
|
let bsid = r.read::<5, u8>().context("bsid")?;
|
||||||
|
|
||||||
|
r.skip(5).context("dialnorm")?;
|
||||||
|
let compre = r.read_bit().context("compre")?;
|
||||||
|
if compre {
|
||||||
|
r.skip(8).context("compr")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if acmod == 0x00 {
|
||||||
|
r.skip(5).context("dialnorm2")?;
|
||||||
|
let compr2e = r.read_bit().context("compr2e")?;
|
||||||
|
if compr2e {
|
||||||
|
r.skip(8).context("compr2")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut chanmap = None;
|
||||||
|
if strmtyp == 0x1 {
|
||||||
|
let chanmape = r.read_bit().context("chanmap2")?;
|
||||||
|
if chanmape {
|
||||||
|
chanmap = Some(r.read::<16, u16>().context("chanmap")?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mixmdate = r.read_bit().context("mixmdate")?;
|
||||||
|
if mixmdate {
|
||||||
|
if acmod > 0x2 {
|
||||||
|
r.skip(2).context("dmixmod")?;
|
||||||
|
}
|
||||||
|
if acmod & 0x1 != 0 && acmod > 0x2 {
|
||||||
|
r.skip(3).context("ltrtcmixlev")?;
|
||||||
|
r.skip(3).context("lorocmixlev")?;
|
||||||
|
}
|
||||||
|
if acmod & 0x4 != 0 {
|
||||||
|
r.skip(3).context("ltrtsurmixlev")?;
|
||||||
|
r.skip(3).context("lorosurmixlev")?;
|
||||||
|
}
|
||||||
|
if lfeon {
|
||||||
|
let lfemixlevcode = r.read_bit().context("lfemixlevcode")?;
|
||||||
|
if lfemixlevcode {
|
||||||
|
r.skip(5).context("lfemixlevcod")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if strmtyp == 0x0 {
|
||||||
|
let pgmscle = r.read_bit().context("pgmscle")?;
|
||||||
|
if pgmscle {
|
||||||
|
r.skip(6).context("pgmscl")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if acmod == 0x0 {
|
||||||
|
let pgmscl2e = r.read_bit().context("pgmscl2e")?;
|
||||||
|
if pgmscl2e {
|
||||||
|
r.skip(6).context("pgmscl2")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let extpgmscle = r.read_bit().context("extpgmscle")?;
|
||||||
|
if extpgmscle {
|
||||||
|
r.skip(6).context("extpgmscl")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mixdef = r.read::<2, u8>().context("mixdef")?;
|
||||||
|
match mixdef {
|
||||||
|
0x0 => {}
|
||||||
|
0x1 => {
|
||||||
|
r.skip(1).context("premixcmpsel")?;
|
||||||
|
r.skip(1).context("drcsrc")?;
|
||||||
|
r.skip(3).context("premixcmpscl")?;
|
||||||
|
}
|
||||||
|
0x2 => {
|
||||||
|
r.skip(12).context("mixdata")?;
|
||||||
|
}
|
||||||
|
0x3 => {
|
||||||
|
let mixdeflen = r.read::<5, u8>().context("mixdeflen")?;
|
||||||
|
r.skip((mixdeflen as u32 + 2) * 8).context("mixdata")?;
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if acmod < 0x2 {
|
||||||
|
let paninfoe = r.read_bit().context("paninfoe")?;
|
||||||
|
if paninfoe {
|
||||||
|
r.skip(8).context("panmean")?;
|
||||||
|
r.skip(6).context("paninfo")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if acmod == 0x00 {
|
||||||
|
let paninfo2e = r.read_bit().context("paninfo2e")?;
|
||||||
|
if paninfo2e {
|
||||||
|
r.skip(8).context("panmean2")?;
|
||||||
|
r.skip(6).context("paninfo2")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let frmmixcfginfoe = r.read_bit().context("frmmixcfginfoe")?;
|
||||||
|
if frmmixcfginfoe {
|
||||||
|
if numblkscod == 0 {
|
||||||
|
r.skip(5).context("blkmixcfginfo")?;
|
||||||
|
} else {
|
||||||
|
for _ in 0..number_of_blocks_per_sync_frame {
|
||||||
|
let blkmixcfginfoe = r.read_bit().context("blkmixcfginfoe")?;
|
||||||
|
if blkmixcfginfoe {
|
||||||
|
r.skip(5).context("blkmixcfginfo")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let infomdate = r.read_bit().context("infomdate")?;
|
||||||
|
let mut bsmod = 0;
|
||||||
|
if infomdate {
|
||||||
|
bsmod = r.read::<3, u8>().context("bsmod")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Bsi {
|
||||||
|
strmtyp,
|
||||||
|
substreamid,
|
||||||
|
frmsiz,
|
||||||
|
fscod,
|
||||||
|
fscod2,
|
||||||
|
numblkscod,
|
||||||
|
acmod,
|
||||||
|
lfeon,
|
||||||
|
bsid,
|
||||||
|
chanmap,
|
||||||
|
bsmod,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct Dec3 {
|
||||||
|
pub headers: Vec<Header>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToBitStream for Dec3 {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn to_writer<W: bitstream_io::BitWrite + ?Sized>(
|
||||||
|
&self,
|
||||||
|
w: &mut W,
|
||||||
|
) -> Result<(), Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
struct IndSub {
|
||||||
|
header: Header,
|
||||||
|
num_dep_sub: u8,
|
||||||
|
chan_loc: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut num_ind_sub = 0;
|
||||||
|
let mut ind_subs = Vec::new();
|
||||||
|
|
||||||
|
// We assume the stream is well-formed and don't validate increasing
|
||||||
|
// substream ids and that each first substream of an id is an independent
|
||||||
|
// stream.
|
||||||
|
for substream in self
|
||||||
|
.headers
|
||||||
|
.chunk_by(|h1, h2| h1.bsi.substreamid == h2.bsi.substreamid)
|
||||||
|
{
|
||||||
|
num_ind_sub += 1;
|
||||||
|
|
||||||
|
let mut num_dep_sub = 0;
|
||||||
|
|
||||||
|
let independent_stream = substream[0];
|
||||||
|
|
||||||
|
let mut chan_loc = 0;
|
||||||
|
for dependent_stream in substream.iter().skip(1) {
|
||||||
|
num_dep_sub += 1;
|
||||||
|
chan_loc |= dependent_stream
|
||||||
|
.bsi
|
||||||
|
.chanmap
|
||||||
|
.map(|chanmap| (chanmap >> 5) & 0x1f)
|
||||||
|
.unwrap_or(independent_stream.bsi.acmod as u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
ind_subs.push(IndSub {
|
||||||
|
header: independent_stream,
|
||||||
|
num_dep_sub,
|
||||||
|
chan_loc,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = 4
|
||||||
|
+ 4
|
||||||
|
+ 2
|
||||||
|
+ ind_subs
|
||||||
|
.iter()
|
||||||
|
.map(|s| 3 + if s.num_dep_sub > 0 { 1 } else { 0 })
|
||||||
|
.sum::<u32>();
|
||||||
|
|
||||||
|
w.write_from::<u32>(len).context("size")?;
|
||||||
|
w.write_bytes(b"dec3").context("type")?;
|
||||||
|
|
||||||
|
let data_rate = self
|
||||||
|
.headers
|
||||||
|
.iter()
|
||||||
|
.map(|header| {
|
||||||
|
((header.bsi.frmsiz as u32 + 1)
|
||||||
|
* if let Some(fscod2) = header.bsi.fscod2 {
|
||||||
|
SAMPLE_RATES[fscod2 as usize] as u32 / 2
|
||||||
|
} else {
|
||||||
|
SAMPLE_RATES[header.bsi.fscod as usize] as u32
|
||||||
|
})
|
||||||
|
/ NUM_BLOCKS[header.bsi.numblkscod as usize] as u32
|
||||||
|
})
|
||||||
|
.sum::<u32>();
|
||||||
|
w.write::<13, u16>((data_rate / 1000) as u16)
|
||||||
|
.context("data_rate")?;
|
||||||
|
|
||||||
|
w.write::<3, u8>(num_ind_sub).context("num_ind_sub")?;
|
||||||
|
|
||||||
|
for ind_sub in ind_subs {
|
||||||
|
w.write::<2, u8>(ind_sub.header.bsi.fscod)
|
||||||
|
.context("fscod")?;
|
||||||
|
w.write::<5, u8>(ind_sub.header.bsi.bsid).context("bsid")?;
|
||||||
|
w.write::<1, u8>(0).context("reserved")?;
|
||||||
|
w.write::<1, u8>(0).context("asvc")?;
|
||||||
|
w.write::<3, u8>(ind_sub.header.bsi.bsmod)
|
||||||
|
.context("bsmod")?;
|
||||||
|
w.write::<3, u8>(ind_sub.header.bsi.acmod)
|
||||||
|
.context("acmod")?;
|
||||||
|
w.write_bit(ind_sub.header.bsi.lfeon).context("lfeon")?;
|
||||||
|
w.write::<3, u8>(0).context("reserved")?;
|
||||||
|
|
||||||
|
w.write::<4, u8>(ind_sub.num_dep_sub)
|
||||||
|
.context("num_dep_sub")?;
|
||||||
|
if ind_sub.num_dep_sub > 0 {
|
||||||
|
w.write::<9, u16>(ind_sub.chan_loc).context("chan_loc")?;
|
||||||
|
} else {
|
||||||
|
w.write::<1, u8>(0).context("reserved")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.byte_align().context("reserved")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -273,8 +273,12 @@ struct Stream {
|
||||||
/// Mapping between running time and UTC time in ONVIF mode.
|
/// Mapping between running time and UTC time in ONVIF mode.
|
||||||
running_time_utc_time_mapping: Option<(gst::Signed<gst::ClockTime>, gst::ClockTime)>,
|
running_time_utc_time_mapping: Option<(gst::Signed<gst::ClockTime>, gst::ClockTime)>,
|
||||||
|
|
||||||
|
/// More data to be included in the fragmented stream header
|
||||||
extra_header_data: Option<Vec<u8>>,
|
extra_header_data: Option<Vec<u8>>,
|
||||||
|
|
||||||
|
/// Codec-specific boxes to be included in the sample entry
|
||||||
|
codec_specific_boxes: Vec<u8>,
|
||||||
|
|
||||||
/// Earliest PTS of the whole stream
|
/// Earliest PTS of the whole stream
|
||||||
earliest_pts: Option<gst::ClockTime>,
|
earliest_pts: Option<gst::ClockTime>,
|
||||||
/// Current end PTS of the whole stream
|
/// Current end PTS of the whole stream
|
||||||
|
@ -969,7 +973,9 @@ impl FMP4Mux {
|
||||||
]
|
]
|
||||||
.as_slice(),
|
.as_slice(),
|
||||||
"audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw"
|
"audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw"
|
||||||
| "audio/x-adpcm" => ["channels", "rate", "layout", "bitrate", "codec_data"].as_slice(),
|
| "audio/x-ac3" | "audio/x-eac3" | "audio/x-adpcm" => {
|
||||||
|
["channels", "rate", "layout", "bitrate", "codec_data"].as_slice()
|
||||||
|
}
|
||||||
"application/x-onvif-metadata" => [].as_slice(),
|
"application/x-onvif-metadata" => [].as_slice(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
@ -3516,6 +3522,7 @@ impl FMP4Mux {
|
||||||
|
|
||||||
let mut delta_frames = DeltaFrames::IntraOnly;
|
let mut delta_frames = DeltaFrames::IntraOnly;
|
||||||
let mut discard_header_buffers = false;
|
let mut discard_header_buffers = false;
|
||||||
|
let mut codec_specific_boxes = Vec::new();
|
||||||
match s.name().as_str() {
|
match s.name().as_str() {
|
||||||
"video/x-h264" | "video/x-h265" => {
|
"video/x-h264" | "video/x-h265" => {
|
||||||
if !s.has_field_with_type("codec_data", gst::Buffer::static_type()) {
|
if !s.has_field_with_type("codec_data", gst::Buffer::static_type()) {
|
||||||
|
@ -3571,6 +3578,46 @@ impl FMP4Mux {
|
||||||
return Err(gst::FlowError::NotNegotiated);
|
return Err(gst::FlowError::NotNegotiated);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
"audio/x-ac3" | "audio/x-eac3" => {
|
||||||
|
let Some(first_buffer) = pad.peek_buffer() else {
|
||||||
|
gst::error!(
|
||||||
|
CAT,
|
||||||
|
obj = pad,
|
||||||
|
"Need first buffer for AC-3 / EAC-3 when creating header"
|
||||||
|
);
|
||||||
|
return Err(gst::FlowError::NotNegotiated);
|
||||||
|
};
|
||||||
|
|
||||||
|
match s.name().as_str() {
|
||||||
|
"audio/x-ac3" => {
|
||||||
|
codec_specific_boxes = match boxes::create_dac3(&first_buffer) {
|
||||||
|
Ok(boxes) => boxes,
|
||||||
|
Err(err) => {
|
||||||
|
gst::error!(
|
||||||
|
CAT,
|
||||||
|
obj = pad,
|
||||||
|
"Failed to create AC-3 codec specific box: {err}"
|
||||||
|
);
|
||||||
|
return Err(gst::FlowError::NotNegotiated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"audio/x-eac3" => {
|
||||||
|
codec_specific_boxes = match boxes::create_dec3(&first_buffer) {
|
||||||
|
Ok(boxes) => boxes,
|
||||||
|
Err(err) => {
|
||||||
|
gst::error!(
|
||||||
|
CAT,
|
||||||
|
obj = pad,
|
||||||
|
"Failed to create EAC-3 codec specific box: {err}"
|
||||||
|
);
|
||||||
|
return Err(gst::FlowError::NotNegotiated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
"audio/x-alaw" | "audio/x-mulaw" => (),
|
"audio/x-alaw" | "audio/x-mulaw" => (),
|
||||||
"audio/x-adpcm" => (),
|
"audio/x-adpcm" => (),
|
||||||
"application/x-onvif-metadata" => (),
|
"application/x-onvif-metadata" => (),
|
||||||
|
@ -3593,6 +3640,7 @@ impl FMP4Mux {
|
||||||
current_position: gst::ClockTime::ZERO,
|
current_position: gst::ClockTime::ZERO,
|
||||||
running_time_utc_time_mapping: None,
|
running_time_utc_time_mapping: None,
|
||||||
extra_header_data: None,
|
extra_header_data: None,
|
||||||
|
codec_specific_boxes,
|
||||||
earliest_pts: None,
|
earliest_pts: None,
|
||||||
end_pts: None,
|
end_pts: None,
|
||||||
language_code,
|
language_code,
|
||||||
|
@ -3685,6 +3733,7 @@ impl FMP4Mux {
|
||||||
delta_frames: s.delta_frames,
|
delta_frames: s.delta_frames,
|
||||||
caps: s.caps.clone(),
|
caps: s.caps.clone(),
|
||||||
extra_header_data: s.extra_header_data.clone(),
|
extra_header_data: s.extra_header_data.clone(),
|
||||||
|
codec_specific_boxes: s.codec_specific_boxes.clone(),
|
||||||
language_code: s.language_code,
|
language_code: s.language_code,
|
||||||
orientation: s.orientation(),
|
orientation: s.orientation(),
|
||||||
max_bitrate: s.max_bitrate,
|
max_bitrate: s.max_bitrate,
|
||||||
|
@ -4900,6 +4949,18 @@ impl ElementImpl for ISOFMP4Mux {
|
||||||
.field("channels", gst::IntRange::<i32>::new(1, 8))
|
.field("channels", gst::IntRange::<i32>::new(1, 8))
|
||||||
.field("rate", gst::IntRange::<i32>::new(1, 10 * u16::MAX as i32))
|
.field("rate", gst::IntRange::<i32>::new(1, 10 * u16::MAX as i32))
|
||||||
.build(),
|
.build(),
|
||||||
|
gst::Structure::builder("audio/x-ac3")
|
||||||
|
.field("framed", true)
|
||||||
|
.field("alignment", "frame")
|
||||||
|
.field("channels", gst::IntRange::<i32>::new(1, u16::MAX as i32))
|
||||||
|
.field("rate", gst::IntRange::<i32>::new(1, i32::MAX))
|
||||||
|
.build(),
|
||||||
|
gst::Structure::builder("audio/x-eac3")
|
||||||
|
.field("framed", true)
|
||||||
|
.field("alignment", "iec61937")
|
||||||
|
.field("channels", gst::IntRange::<i32>::new(1, u16::MAX as i32))
|
||||||
|
.field("rate", gst::IntRange::<i32>::new(1, i32::MAX))
|
||||||
|
.build(),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<gst::Caps>(),
|
.collect::<gst::Caps>(),
|
||||||
|
@ -5001,6 +5062,12 @@ impl ElementImpl for CMAFMux {
|
||||||
.field("channels", gst::IntRange::new(1i32, 8))
|
.field("channels", gst::IntRange::new(1i32, 8))
|
||||||
.field("rate", gst::IntRange::new(1, i32::MAX))
|
.field("rate", gst::IntRange::new(1, i32::MAX))
|
||||||
.build(),
|
.build(),
|
||||||
|
gst::Structure::builder("audio/x-eac3")
|
||||||
|
.field("framed", true)
|
||||||
|
.field("alignment", "iec61937")
|
||||||
|
.field("channels", gst::IntRange::<i32>::new(1, u16::MAX as i32))
|
||||||
|
.field("rate", gst::IntRange::<i32>::new(1, i32::MAX))
|
||||||
|
.build(),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<gst::Caps>(),
|
.collect::<gst::Caps>(),
|
||||||
|
@ -5114,6 +5181,18 @@ impl ElementImpl for DASHMP4Mux {
|
||||||
.field("channels", gst::IntRange::new(1i32, 8))
|
.field("channels", gst::IntRange::new(1i32, 8))
|
||||||
.field("rate", gst::IntRange::new(1, i32::MAX))
|
.field("rate", gst::IntRange::new(1, i32::MAX))
|
||||||
.build(),
|
.build(),
|
||||||
|
gst::Structure::builder("audio/x-ac3")
|
||||||
|
.field("framed", true)
|
||||||
|
.field("alignment", "frame")
|
||||||
|
.field("channels", gst::IntRange::<i32>::new(1, u16::MAX as i32))
|
||||||
|
.field("rate", gst::IntRange::<i32>::new(1, i32::MAX))
|
||||||
|
.build(),
|
||||||
|
gst::Structure::builder("audio/x-eac3")
|
||||||
|
.field("framed", true)
|
||||||
|
.field("alignment", "iec61937")
|
||||||
|
.field("channels", gst::IntRange::<i32>::new(1, u16::MAX as i32))
|
||||||
|
.field("rate", gst::IntRange::<i32>::new(1, i32::MAX))
|
||||||
|
.build(),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<gst::Caps>(),
|
.collect::<gst::Caps>(),
|
||||||
|
|
|
@ -215,6 +215,9 @@ pub(crate) struct HeaderStream {
|
||||||
// More data to be included in the fragmented stream header
|
// More data to be included in the fragmented stream header
|
||||||
extra_header_data: Option<Vec<u8>>,
|
extra_header_data: Option<Vec<u8>>,
|
||||||
|
|
||||||
|
// Codec-specific boxes to be included in the sample entry
|
||||||
|
codec_specific_boxes: Vec<u8>,
|
||||||
|
|
||||||
// Tags meta for audio language and video orientation
|
// Tags meta for audio language and video orientation
|
||||||
language_code: Option<[u8; 3]>,
|
language_code: Option<[u8; 3]>,
|
||||||
orientation: &'static TransformMatrix,
|
orientation: &'static TransformMatrix,
|
||||||
|
|
Loading…
Reference in a new issue