mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-09-03 18:23:49 +00:00
mp4mux: Write btrt box from the bitrate tags if existing
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2193>
This commit is contained in:
parent
8b92f8c5c0
commit
06f1a7e818
3 changed files with 150 additions and 11 deletions
|
@ -1082,7 +1082,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(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
@ -1616,7 +1632,7 @@ fn av1_tier(tier: Option<&str>) -> u8 {
|
||||||
|
|
||||||
fn write_audio_sample_entry(
|
fn write_audio_sample_entry(
|
||||||
v: &mut Vec<u8>,
|
v: &mut Vec<u8>,
|
||||||
_header: &super::Header,
|
header: &super::Header,
|
||||||
stream: &super::Stream,
|
stream: &super::Stream,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let s = stream.caps.structure(0).unwrap();
|
let s = stream.caps.structure(0).unwrap();
|
||||||
|
@ -1683,7 +1699,7 @@ fn write_audio_sample_entry(
|
||||||
if map.len() < 2 {
|
if map.len() < 2 {
|
||||||
bail!("too small codec_data");
|
bail!("too small codec_data");
|
||||||
}
|
}
|
||||||
write_esds_aac(v, &map)?;
|
write_esds_aac(v, header, stream, &map)?;
|
||||||
}
|
}
|
||||||
"audio/x-opus" => {
|
"audio/x-opus" => {
|
||||||
write_dops(v, &stream.caps)?;
|
write_dops(v, &stream.caps)?;
|
||||||
|
@ -1713,7 +1729,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
|
// TODO: chnl box for channel ordering? probably not needed for AAC
|
||||||
|
|
||||||
|
@ -1723,7 +1755,12 @@ fn write_audio_sample_entry(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_esds_aac(v: &mut Vec<u8>, codec_data: &[u8]) -> Result<(), Error> {
|
fn write_esds_aac(
|
||||||
|
v: &mut Vec<u8>,
|
||||||
|
_header: &super::Header,
|
||||||
|
stream: &super::Stream,
|
||||||
|
codec_data: &[u8],
|
||||||
|
) -> Result<(), Error> {
|
||||||
let calculate_len = |mut len| {
|
let calculate_len = |mut len| {
|
||||||
if len > 260144641 {
|
if len > 260144641 {
|
||||||
bail!("too big descriptor length");
|
bail!("too big descriptor length");
|
||||||
|
@ -1801,10 +1838,10 @@ fn write_esds_aac(v: &mut Vec<u8>, codec_data: &[u8]) -> Result<(), Error> {
|
||||||
v.extend([0u8; 3]);
|
v.extend([0u8; 3]);
|
||||||
|
|
||||||
// Max bitrate
|
// Max bitrate
|
||||||
v.extend(0u32.to_be_bytes());
|
v.extend(stream.max_bitrate.unwrap_or(0u32).to_be_bytes());
|
||||||
|
|
||||||
// Avg bitrate
|
// Avg bitrate
|
||||||
v.extend(0u32.to_be_bytes());
|
v.extend(stream.avg_bitrate.unwrap_or(0u32).to_be_bytes());
|
||||||
|
|
||||||
// Decoder specific info
|
// Decoder specific info
|
||||||
v.push(0x05);
|
v.push(0x05);
|
||||||
|
|
|
@ -155,6 +155,9 @@ struct Stream {
|
||||||
/// Orientation from tags, stream orientation takes precedence over global orientation
|
/// Orientation from tags, stream orientation takes precedence over global orientation
|
||||||
global_orientation: &'static TransformMatrix,
|
global_orientation: &'static TransformMatrix,
|
||||||
stream_orientation: Option<&'static TransformMatrix>,
|
stream_orientation: Option<&'static TransformMatrix>,
|
||||||
|
|
||||||
|
avg_bitrate: Option<u32>,
|
||||||
|
max_bitrate: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream {
|
impl Stream {
|
||||||
|
@ -1152,6 +1155,8 @@ impl MP4Mux {
|
||||||
let mut stream_orientation = Default::default();
|
let mut stream_orientation = Default::default();
|
||||||
let mut global_orientation = Default::default();
|
let mut global_orientation = Default::default();
|
||||||
let mut language_code = None;
|
let mut language_code = None;
|
||||||
|
let mut avg_bitrate = None;
|
||||||
|
let mut max_bitrate = None;
|
||||||
pad.sticky_events_foreach(|ev| {
|
pad.sticky_events_foreach(|ev| {
|
||||||
if let gst::EventView::Tag(ev) = ev.view() {
|
if let gst::EventView::Tag(ev) = ev.view() {
|
||||||
let tag = ev.tag();
|
let tag = ev.tag();
|
||||||
|
@ -1159,7 +1164,7 @@ impl MP4Mux {
|
||||||
let lang = lang.get();
|
let lang = lang.get();
|
||||||
gst::trace!(
|
gst::trace!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
obj = pad,
|
||||||
"Received language code from tags: {:?}",
|
"Received language code from tags: {:?}",
|
||||||
lang
|
lang
|
||||||
);
|
);
|
||||||
|
@ -1179,7 +1184,7 @@ impl MP4Mux {
|
||||||
} else if let Some(orientation) = tag.get::<gst::tags::ImageOrientation>() {
|
} else if let Some(orientation) = tag.get::<gst::tags::ImageOrientation>() {
|
||||||
gst::trace!(
|
gst::trace!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
obj = pad,
|
||||||
"Received image orientation from tags: {:?}",
|
"Received image orientation from tags: {:?}",
|
||||||
orientation.get(),
|
orientation.get(),
|
||||||
);
|
);
|
||||||
|
@ -1189,6 +1194,41 @@ impl MP4Mux {
|
||||||
} else {
|
} else {
|
||||||
stream_orientation = Some(TransformMatrix::from_tag(self, ev));
|
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,
|
||||||
|
obj = pad,
|
||||||
|
"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, obj = pad, "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)
|
std::ops::ControlFlow::Continue(gst::EventForeachAction::Keep)
|
||||||
|
@ -1289,6 +1329,8 @@ impl MP4Mux {
|
||||||
language_code,
|
language_code,
|
||||||
global_orientation,
|
global_orientation,
|
||||||
stream_orientation,
|
stream_orientation,
|
||||||
|
max_bitrate,
|
||||||
|
avg_bitrate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1518,7 +1560,7 @@ impl AggregatorImpl for MP4Mux {
|
||||||
let lang = tag_value.get();
|
let lang = tag_value.get();
|
||||||
gst::trace!(
|
gst::trace!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
obj = aggregator_pad,
|
||||||
"Received language code from tags: {:?}",
|
"Received language code from tags: {:?}",
|
||||||
lang
|
lang
|
||||||
);
|
);
|
||||||
|
@ -1530,7 +1572,7 @@ impl AggregatorImpl for MP4Mux {
|
||||||
if tag.scope() == gst::TagScope::Global {
|
if tag.scope() == gst::TagScope::Global {
|
||||||
gst::info!(
|
gst::info!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
obj = aggregator_pad,
|
||||||
"Language tags scoped 'global' are considered stream tags",
|
"Language tags scoped 'global' are considered stream tags",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1563,6 +1605,60 @@ impl AggregatorImpl for MP4Mux {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} 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,
|
||||||
|
obj = aggregator_pad,
|
||||||
|
"Received maximum bitrate from tags: {:?}",
|
||||||
|
bitrate
|
||||||
|
);
|
||||||
|
|
||||||
|
if tag.scope() == gst::TagScope::Global {
|
||||||
|
gst::info!(
|
||||||
|
CAT,
|
||||||
|
obj = aggregator_pad,
|
||||||
|
"Bitrate tags scoped 'global' are considered stream tags",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut state = self.state.lock().unwrap();
|
||||||
|
for stream in &mut state.streams {
|
||||||
|
if &stream.sinkpad == aggregator_pad {
|
||||||
|
stream.max_bitrate = Some(bitrate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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,
|
||||||
|
obj = aggregator_pad,
|
||||||
|
"Received bitrate from tags: {:?}",
|
||||||
|
bitrate
|
||||||
|
);
|
||||||
|
|
||||||
|
if tag.scope() == gst::TagScope::Global {
|
||||||
|
gst::info!(
|
||||||
|
CAT,
|
||||||
|
obj = aggregator_pad,
|
||||||
|
"Bitrate tags scoped 'global' are considered stream tags",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut state = self.state.lock().unwrap();
|
||||||
|
for stream in &mut state.streams {
|
||||||
|
if &stream.sinkpad == aggregator_pad {
|
||||||
|
stream.avg_bitrate = Some(bitrate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.parent_sink_event(aggregator_pad, event)
|
self.parent_sink_event(aggregator_pad, event)
|
||||||
|
@ -1805,6 +1901,8 @@ impl AggregatorImpl for MP4Mux {
|
||||||
extra_header_data: stream.extra_header_data.clone(),
|
extra_header_data: stream.extra_header_data.clone(),
|
||||||
language_code: stream.language_code,
|
language_code: stream.language_code,
|
||||||
orientation: stream.orientation(),
|
orientation: stream.orientation(),
|
||||||
|
max_bitrate: stream.max_bitrate,
|
||||||
|
avg_bitrate: stream.avg_bitrate,
|
||||||
chunks: stream.chunks,
|
chunks: stream.chunks,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,6 +231,10 @@ pub(crate) struct Stream {
|
||||||
// Language code from tags
|
// Language code from tags
|
||||||
language_code: Option<[u8; 3]>,
|
language_code: Option<[u8; 3]>,
|
||||||
|
|
||||||
|
/// Bitrate tags
|
||||||
|
avg_bitrate: Option<u32>,
|
||||||
|
max_bitrate: Option<u32>,
|
||||||
|
|
||||||
/// Orientation from tags
|
/// Orientation from tags
|
||||||
orientation: &'static TransformMatrix,
|
orientation: &'static TransformMatrix,
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue