mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-09-02 09:43:48 +00:00
fmp4mux: Write btrt box from the bitrate tags if existing
Only take the tags into account that arrive before the muxer created its streams, otherwise we would have to re-create the header every time the bitrate changes. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2193>
This commit is contained in:
parent
51987b6f1f
commit
f86e7e6c33
3 changed files with 108 additions and 10 deletions
|
@ -1300,7 +1300,23 @@ fn write_visual_sample_entry(
|
|||
})?;
|
||||
}
|
||||
|
||||
// TODO: write btrt bitrate box based on tags
|
||||
if stream.avg_bitrate.is_some() || stream.max_bitrate.is_some() {
|
||||
write_box(v, b"btrt", |v| {
|
||||
// Buffer size DB
|
||||
// TODO
|
||||
v.extend(0u32.to_be_bytes());
|
||||
|
||||
// Maximum bitrate
|
||||
let max_bitrate = stream.max_bitrate.or(stream.avg_bitrate).unwrap();
|
||||
v.extend(max_bitrate.to_be_bytes());
|
||||
|
||||
// Average bitrate
|
||||
let avg_bitrate = stream.avg_bitrate.or(stream.max_bitrate).unwrap();
|
||||
v.extend(avg_bitrate.to_be_bytes());
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -1348,7 +1364,7 @@ fn av1_tier(tier: Option<&str>) -> u8 {
|
|||
|
||||
fn write_audio_sample_entry(
|
||||
v: &mut Vec<u8>,
|
||||
_cfg: &super::HeaderConfiguration,
|
||||
cfg: &super::HeaderConfiguration,
|
||||
stream: &super::HeaderStream,
|
||||
) -> Result<(), Error> {
|
||||
let s = stream.caps.structure(0).unwrap();
|
||||
|
@ -1415,7 +1431,7 @@ fn write_audio_sample_entry(
|
|||
if map.len() < 2 {
|
||||
bail!("too small codec_data");
|
||||
}
|
||||
write_esds_aac(v, &map)?;
|
||||
write_esds_aac(v, cfg, stream, &map)?;
|
||||
}
|
||||
"audio/x-opus" => {
|
||||
write_dops(v, &stream.caps)?;
|
||||
|
@ -1445,7 +1461,23 @@ fn write_audio_sample_entry(
|
|||
)?;
|
||||
}
|
||||
|
||||
// TODO: write btrt bitrate box based on tags
|
||||
if stream.avg_bitrate.is_some() || stream.max_bitrate.is_some() {
|
||||
write_box(v, b"btrt", |v| {
|
||||
// Buffer size DB
|
||||
// TODO
|
||||
v.extend(0u32.to_be_bytes());
|
||||
|
||||
// Maximum bitrate
|
||||
let max_bitrate = stream.max_bitrate.or(stream.avg_bitrate).unwrap();
|
||||
v.extend(max_bitrate.to_be_bytes());
|
||||
|
||||
// Average bitrate
|
||||
let avg_bitrate = stream.avg_bitrate.or(stream.max_bitrate).unwrap();
|
||||
v.extend(avg_bitrate.to_be_bytes());
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
// TODO: chnl box for channel ordering? probably not needed for AAC
|
||||
|
||||
|
@ -1455,7 +1487,12 @@ fn write_audio_sample_entry(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_esds_aac(v: &mut Vec<u8>, codec_data: &[u8]) -> Result<(), Error> {
|
||||
fn write_esds_aac(
|
||||
v: &mut Vec<u8>,
|
||||
_cfg: &super::HeaderConfiguration,
|
||||
stream: &super::HeaderStream,
|
||||
codec_data: &[u8],
|
||||
) -> Result<(), Error> {
|
||||
let calculate_len = |mut len| {
|
||||
if len > 260144641 {
|
||||
bail!("too big descriptor length");
|
||||
|
@ -1533,10 +1570,10 @@ fn write_esds_aac(v: &mut Vec<u8>, codec_data: &[u8]) -> Result<(), Error> {
|
|||
v.extend([0u8; 3]);
|
||||
|
||||
// Max bitrate
|
||||
v.extend(0u32.to_be_bytes());
|
||||
v.extend(stream.max_bitrate.unwrap_or(0u32).to_be_bytes());
|
||||
|
||||
// Avg bitrate
|
||||
v.extend(0u32.to_be_bytes());
|
||||
v.extend(stream.avg_bitrate.unwrap_or(0u32).to_be_bytes());
|
||||
|
||||
// Decoder specific info
|
||||
v.push(0x05);
|
||||
|
|
|
@ -285,6 +285,9 @@ struct Stream {
|
|||
/// Orientation from tags, stream orientation takes precedence over global orientation
|
||||
global_orientation: &'static TransformMatrix,
|
||||
stream_orientation: Option<&'static TransformMatrix>,
|
||||
/// Bitrate tags
|
||||
avg_bitrate: Option<u32>,
|
||||
max_bitrate: Option<u32>,
|
||||
|
||||
/// Edit list entries for this stream.
|
||||
elst_infos: Vec<super::ElstInfo>,
|
||||
|
@ -3421,10 +3424,20 @@ impl FMP4Mux {
|
|||
let mut stream_orientation = Default::default();
|
||||
let mut global_orientation = Default::default();
|
||||
let mut language_code = None;
|
||||
let mut avg_bitrate = None;
|
||||
let mut max_bitrate = None;
|
||||
pad.sticky_events_foreach(|ev| {
|
||||
if let gst::EventView::Tag(ev) = ev.view() {
|
||||
let tag = ev.tag();
|
||||
if let Some(l) = tag.get::<gst::tags::LanguageCode>() {
|
||||
if let Some(lang) = tag.get::<gst::tags::LanguageCode>() {
|
||||
let lang = lang.get();
|
||||
gst::trace!(
|
||||
CAT,
|
||||
imp = self,
|
||||
"Received language code from tags: {:?}",
|
||||
lang
|
||||
);
|
||||
|
||||
// There is no header field for global
|
||||
// language code, maybe because it does not
|
||||
// really make sense, global language tags are
|
||||
|
@ -3436,13 +3449,55 @@ impl FMP4Mux {
|
|||
"Language tags scoped 'global' are considered stream tags",
|
||||
);
|
||||
}
|
||||
language_code = Stream::parse_language_code(l.get());
|
||||
} else if tag.get::<gst::tags::ImageOrientation>().is_some() {
|
||||
language_code = Stream::parse_language_code(lang);
|
||||
} else if let Some(orientation) = tag.get::<gst::tags::ImageOrientation>() {
|
||||
gst::trace!(
|
||||
CAT,
|
||||
imp = self,
|
||||
"Received image orientation from tags: {:?}",
|
||||
orientation.get(),
|
||||
);
|
||||
|
||||
if tag.scope() == gst::TagScope::Global {
|
||||
global_orientation = TransformMatrix::from_tag(self, ev);
|
||||
} else {
|
||||
stream_orientation = Some(TransformMatrix::from_tag(self, ev));
|
||||
}
|
||||
} else if let Some(bitrate) = tag
|
||||
.get::<gst::tags::MaximumBitrate>()
|
||||
.filter(|br| br.get() > 0 && br.get() < u32::MAX)
|
||||
{
|
||||
let bitrate = bitrate.get();
|
||||
gst::trace!(
|
||||
CAT,
|
||||
imp = self,
|
||||
"Received maximum bitrate from tags: {:?}",
|
||||
bitrate
|
||||
);
|
||||
|
||||
if tag.scope() == gst::TagScope::Global {
|
||||
gst::info!(
|
||||
CAT,
|
||||
obj = pad,
|
||||
"Bitrate tags scoped 'global' are considered stream tags",
|
||||
);
|
||||
}
|
||||
max_bitrate = Some(bitrate);
|
||||
} else if let Some(bitrate) = tag
|
||||
.get::<gst::tags::Bitrate>()
|
||||
.filter(|br| br.get() > 0 && br.get() < u32::MAX)
|
||||
{
|
||||
let bitrate = bitrate.get();
|
||||
gst::trace!(CAT, imp = self, "Received bitrate from tags: {:?}", bitrate);
|
||||
|
||||
if tag.scope() == gst::TagScope::Global {
|
||||
gst::info!(
|
||||
CAT,
|
||||
obj = pad,
|
||||
"Bitrate tags scoped 'global' are considered stream tags",
|
||||
);
|
||||
}
|
||||
avg_bitrate = Some(bitrate);
|
||||
}
|
||||
}
|
||||
std::ops::ControlFlow::Continue(gst::EventForeachAction::Keep)
|
||||
|
@ -3536,6 +3591,8 @@ impl FMP4Mux {
|
|||
language_code,
|
||||
global_orientation,
|
||||
stream_orientation,
|
||||
avg_bitrate,
|
||||
max_bitrate,
|
||||
elst_infos: Vec::new(),
|
||||
pending_split_now: Vec::new(),
|
||||
});
|
||||
|
@ -3610,6 +3667,8 @@ impl FMP4Mux {
|
|||
extra_header_data: s.extra_header_data.clone(),
|
||||
language_code: s.language_code,
|
||||
orientation: s.orientation(),
|
||||
max_bitrate: s.max_bitrate,
|
||||
avg_bitrate: s.avg_bitrate,
|
||||
elst_infos: s.get_elst_infos().unwrap_or_else(|e| {
|
||||
gst::error!(CAT, "Could not prepare edit lists: {e:?}");
|
||||
|
||||
|
|
|
@ -218,6 +218,8 @@ pub(crate) struct HeaderStream {
|
|||
// Tags meta for audio language and video orientation
|
||||
language_code: Option<[u8; 3]>,
|
||||
orientation: &'static TransformMatrix,
|
||||
avg_bitrate: Option<u32>,
|
||||
max_bitrate: Option<u32>,
|
||||
|
||||
/// Edit list clipping information
|
||||
elst_infos: Vec<ElstInfo>,
|
||||
|
|
Loading…
Reference in a new issue