mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-09-02 01:33: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")?;
|
||||
(bitrate / 8000) as u16
|
||||
}
|
||||
"audio/x-flac" => with_flac_metadata(&stream.caps, |streaminfo, _| {
|
||||
1 + ((u16::from_be_bytes([streaminfo[16], streaminfo[17]]) >> 4) & 0b11111)
|
||||
})
|
||||
.context("FLAC metadata error")?,
|
||||
"audio/x-flac" => {
|
||||
let (streamheader, _headers) =
|
||||
flac::parse_stream_header(&stream.caps).context("FLAC streamheader")?;
|
||||
streamheader.stream_info.bits_per_sample as u16
|
||||
}
|
||||
_ => 16u16,
|
||||
};
|
||||
|
||||
|
@ -1446,7 +1447,9 @@ fn write_audio_sample_entry(
|
|||
write_dops(v, &stream.caps)?;
|
||||
}
|
||||
"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" => {
|
||||
// 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(
|
||||
v: &mut Vec<u8>,
|
||||
_cfg: &super::HeaderConfiguration,
|
||||
|
@ -2408,6 +2382,158 @@ pub(crate) fn create_mfra(
|
|||
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.
|
||||
pub(crate) fn create_dac3(buffer: &gst::BufferRef) -> Result<Vec<u8>, Error> {
|
||||
use bitstream_io::{BitRead as _, BitWrite as _};
|
||||
|
|
|
@ -3568,14 +3568,17 @@ impl FMP4Mux {
|
|||
}
|
||||
"audio/x-flac" => {
|
||||
discard_header_buffers = true;
|
||||
if let Err(e) = s.get::<gst::ArrayRef>("streamheader") {
|
||||
gst::error!(
|
||||
CAT,
|
||||
obj = pad,
|
||||
"Muxing FLAC into MP4 needs streamheader: {}",
|
||||
e
|
||||
);
|
||||
return Err(gst::FlowError::NotNegotiated);
|
||||
|
||||
codec_specific_boxes = match boxes::write_dfla(&caps) {
|
||||
Ok(boxes) => boxes,
|
||||
Err(err) => {
|
||||
gst::error!(
|
||||
CAT,
|
||||
obj = pad,
|
||||
"Failed to create FLAC codec specific box: {err}"
|
||||
);
|
||||
return Err(gst::FlowError::NotNegotiated);
|
||||
}
|
||||
};
|
||||
}
|
||||
"audio/x-ac3" | "audio/x-eac3" => {
|
||||
|
|
Loading…
Reference in a new issue