mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-09-03 10:13:47 +00:00
fmp4mux: Create FLAC dfLa box when receiving the caps too
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2208>
This commit is contained in:
parent
03e9a9f0fe
commit
3e5da7a783
2 changed files with 171 additions and 42 deletions
|
@ -1399,10 +1399,11 @@ fn write_audio_sample_entry(
|
||||||
let bitrate = s.get::<i32>("bitrate").context("no ADPCM bitrate field")?;
|
let bitrate = s.get::<i32>("bitrate").context("no ADPCM bitrate field")?;
|
||||||
(bitrate / 8000) as u16
|
(bitrate / 8000) as u16
|
||||||
}
|
}
|
||||||
"audio/x-flac" => with_flac_metadata(&stream.caps, |streaminfo, _| {
|
"audio/x-flac" => {
|
||||||
1 + ((u16::from_be_bytes([streaminfo[16], streaminfo[17]]) >> 4) & 0b11111)
|
let (streamheader, _headers) =
|
||||||
})
|
flac::parse_stream_header(&stream.caps).context("FLAC streamheader")?;
|
||||||
.context("FLAC metadata error")?,
|
streamheader.stream_info.bits_per_sample as u16
|
||||||
|
}
|
||||||
_ => 16u16,
|
_ => 16u16,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1446,7 +1447,9 @@ fn write_audio_sample_entry(
|
||||||
write_dops(v, &stream.caps)?;
|
write_dops(v, &stream.caps)?;
|
||||||
}
|
}
|
||||||
"audio/x-flac" => {
|
"audio/x-flac" => {
|
||||||
write_dfla(v, &stream.caps)?;
|
assert!(!stream.codec_specific_boxes.is_empty());
|
||||||
|
assert!(&stream.codec_specific_boxes[4..8] == b"dfLa");
|
||||||
|
v.extend_from_slice(&stream.codec_specific_boxes);
|
||||||
}
|
}
|
||||||
"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
|
||||||
|
@ -1673,35 +1676,6 @@ fn write_dops(v: &mut Vec<u8>, caps: &gst::Caps) -> Result<(), Error> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_flac_metadata<R>(
|
|
||||||
caps: &gst::Caps,
|
|
||||||
cb: impl FnOnce(&[u8], &[gst::glib::SendValue]) -> R,
|
|
||||||
) -> Result<R, Error> {
|
|
||||||
let caps = caps.structure(0).unwrap();
|
|
||||||
let header = caps.get::<gst::ArrayRef>("streamheader").unwrap();
|
|
||||||
let (streaminfo, remainder) = header.as_ref().split_first().unwrap();
|
|
||||||
let streaminfo = streaminfo.get::<&gst::BufferRef>().unwrap();
|
|
||||||
let streaminfo = streaminfo.map_readable().unwrap();
|
|
||||||
// 13 bytes for the Ogg/FLAC prefix and 38 for the streaminfo itself.
|
|
||||||
match <&[_; 13 + 38]>::try_from(streaminfo.as_slice()) {
|
|
||||||
Ok(i) if i.starts_with(b"\x7FFLAC\x01\x00") => Ok(cb(&i[13..], remainder)),
|
|
||||||
Ok(_) | Err(_) => bail!("Unknown streamheader format"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_dfla(v: &mut Vec<u8>, caps: &gst::Caps) -> Result<(), Error> {
|
|
||||||
write_full_box(v, b"dfLa", 0, 0, move |v| {
|
|
||||||
with_flac_metadata(caps, |streaminfo, remainder| {
|
|
||||||
v.extend(streaminfo);
|
|
||||||
for metadata in remainder {
|
|
||||||
let metadata = metadata.get::<&gst::BufferRef>().unwrap();
|
|
||||||
let metadata = metadata.map_readable().unwrap();
|
|
||||||
v.extend(&metadata[..]);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_xml_meta_data_sample_entry(
|
fn write_xml_meta_data_sample_entry(
|
||||||
v: &mut Vec<u8>,
|
v: &mut Vec<u8>,
|
||||||
_cfg: &super::HeaderConfiguration,
|
_cfg: &super::HeaderConfiguration,
|
||||||
|
@ -2408,6 +2382,158 @@ pub(crate) fn create_mfra(
|
||||||
Ok(gst::Buffer::from_mut_slice(v))
|
Ok(gst::Buffer::from_mut_slice(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create FLAC `dfLa` box.
|
||||||
|
pub(crate) fn write_dfla(caps: &gst::CapsRef) -> Result<Vec<u8>, Error> {
|
||||||
|
let mut dfla = Vec::new();
|
||||||
|
|
||||||
|
let (_streamheader, headers) = flac::parse_stream_header(caps).context("FLAC streamheader")?;
|
||||||
|
|
||||||
|
write_full_box(&mut dfla, b"dfLa", 0, 0, move |v| {
|
||||||
|
for header in headers {
|
||||||
|
let map = header.map_readable().unwrap();
|
||||||
|
v.extend(&map[..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(dfla)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod flac {
|
||||||
|
use anyhow::{bail, Context as _, Error};
|
||||||
|
use bitstream_io::FromBitStream;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct StreamHeader {
|
||||||
|
pub mapping_major_version: u8,
|
||||||
|
pub mapping_minor_version: u8,
|
||||||
|
pub num_headers: u16,
|
||||||
|
pub stream_info: StreamInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBitStream for StreamHeader {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_reader<R: bitstream_io::BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let packet_type = r.read_to::<u8>().context("packet_type")?;
|
||||||
|
if packet_type != 0x7f {
|
||||||
|
bail!("Invalid packet type");
|
||||||
|
}
|
||||||
|
let signature = r.read_to::<[u8; 4]>().context("signature")?;
|
||||||
|
if &signature != b"FLAC" {
|
||||||
|
bail!("Invalid FLAC signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mapping_major_version = r.read_to::<u8>().context("mapping_major_version")?;
|
||||||
|
let mapping_minor_version = r.read_to::<u8>().context("mapping_minor_version")?;
|
||||||
|
let num_headers = r.read_to::<u16>().context("num_headers")?;
|
||||||
|
let signature = r.read_to::<[u8; 4]>().context("signature")?;
|
||||||
|
if &signature != b"fLaC" {
|
||||||
|
bail!("Invalid fLaC signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
let stream_info = r.parse::<StreamInfo>().context("stream_info")?;
|
||||||
|
|
||||||
|
Ok(StreamHeader {
|
||||||
|
mapping_major_version,
|
||||||
|
mapping_minor_version,
|
||||||
|
num_headers,
|
||||||
|
stream_info,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct StreamInfo {
|
||||||
|
pub min_block_size: u16,
|
||||||
|
pub max_block_size: u16,
|
||||||
|
pub min_frame_size: u32,
|
||||||
|
pub max_frame_size: u32,
|
||||||
|
pub sample_rate: u32,
|
||||||
|
pub num_channels: u8,
|
||||||
|
pub bits_per_sample: u8,
|
||||||
|
pub num_samples: u64,
|
||||||
|
pub md5: [u8; 16],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBitStream for StreamInfo {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_reader<R: bitstream_io::BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let _is_last = r.read_bit().context("is_last")?;
|
||||||
|
let metadata_block_type = r.read::<7, u8>().context("metadata_block_type")?;
|
||||||
|
if metadata_block_type != 0 {
|
||||||
|
bail!("Invalid metadata block type {metadata_block_type}");
|
||||||
|
}
|
||||||
|
let _metadata_block_size = r.read::<24, u32>().context("metadata_block_size")?;
|
||||||
|
|
||||||
|
let min_block_size = r.read_to::<u16>().context("min_block_size")?;
|
||||||
|
let max_block_size = r.read_to::<u16>().context("max_block_size")?;
|
||||||
|
let min_frame_size = r.read::<24, u32>().context("min_frame_size")?;
|
||||||
|
let max_frame_size = r.read::<24, u32>().context("max_frame_size")?;
|
||||||
|
let sample_rate = r.read::<20, u32>().context("sample_rate")?;
|
||||||
|
let num_channels = r.read::<3, u8>().context("num_channels")? + 1;
|
||||||
|
let bits_per_sample = r.read::<5, u8>().context("bits_per_sample")? + 1;
|
||||||
|
let num_samples = r.read::<36, u64>().context("num_samples")?;
|
||||||
|
let md5 = r.read_to::<[u8; 16]>().context("md5")?;
|
||||||
|
|
||||||
|
Ok(StreamInfo {
|
||||||
|
min_block_size,
|
||||||
|
max_block_size,
|
||||||
|
min_frame_size,
|
||||||
|
max_frame_size,
|
||||||
|
sample_rate,
|
||||||
|
num_channels,
|
||||||
|
bits_per_sample,
|
||||||
|
num_samples,
|
||||||
|
md5,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_stream_header(
|
||||||
|
caps: &gst::CapsRef,
|
||||||
|
) -> Result<(StreamHeader, Vec<gst::Buffer>), Error> {
|
||||||
|
use bitstream_io::BitRead as _;
|
||||||
|
|
||||||
|
let s = caps.structure(0).unwrap();
|
||||||
|
let Ok(streamheader) = s.get::<gst::ArrayRef>("streamheader") else {
|
||||||
|
bail!("Need streamheader in caps for FLAC");
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some((streaminfo, remainder)) = streamheader.as_ref().split_first() else {
|
||||||
|
bail!("Empty FLAC streamheader");
|
||||||
|
};
|
||||||
|
let streaminfo = streaminfo.get::<&gst::Buffer>().unwrap();
|
||||||
|
let map = streaminfo.map_readable().unwrap();
|
||||||
|
|
||||||
|
let mut reader = bitstream_io::BitReader::endian(
|
||||||
|
std::io::Cursor::new(map.as_slice()),
|
||||||
|
bitstream_io::BigEndian,
|
||||||
|
);
|
||||||
|
|
||||||
|
let header = reader
|
||||||
|
.parse::<StreamHeader>()
|
||||||
|
.context("Parsing FLAC streamheader")?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
header,
|
||||||
|
std::iter::once(gst::Buffer::from_mut_slice(Vec::from(&map[13..])))
|
||||||
|
.chain(remainder.iter().map(|v| v.get::<gst::Buffer>().unwrap()))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create AC-3 `dac3` box.
|
/// Create AC-3 `dac3` box.
|
||||||
pub(crate) fn create_dac3(buffer: &gst::BufferRef) -> Result<Vec<u8>, Error> {
|
pub(crate) fn create_dac3(buffer: &gst::BufferRef) -> Result<Vec<u8>, Error> {
|
||||||
use bitstream_io::{BitRead as _, BitWrite as _};
|
use bitstream_io::{BitRead as _, BitWrite as _};
|
||||||
|
|
|
@ -3568,14 +3568,17 @@ impl FMP4Mux {
|
||||||
}
|
}
|
||||||
"audio/x-flac" => {
|
"audio/x-flac" => {
|
||||||
discard_header_buffers = true;
|
discard_header_buffers = true;
|
||||||
if let Err(e) = s.get::<gst::ArrayRef>("streamheader") {
|
|
||||||
gst::error!(
|
codec_specific_boxes = match boxes::write_dfla(&caps) {
|
||||||
CAT,
|
Ok(boxes) => boxes,
|
||||||
obj = pad,
|
Err(err) => {
|
||||||
"Muxing FLAC into MP4 needs streamheader: {}",
|
gst::error!(
|
||||||
e
|
CAT,
|
||||||
);
|
obj = pad,
|
||||||
return Err(gst::FlowError::NotNegotiated);
|
"Failed to create FLAC codec specific box: {err}"
|
||||||
|
);
|
||||||
|
return Err(gst::FlowError::NotNegotiated);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
"audio/x-ac3" | "audio/x-eac3" => {
|
"audio/x-ac3" | "audio/x-eac3" => {
|
||||||
|
|
Loading…
Reference in a new issue