fmp4mux: Add support for ONVIF timed XML metadata

This commit is contained in:
Sebastian Dröge 2022-05-13 12:13:54 +03:00
parent e4634ca2fe
commit ffea0e2d2d
2 changed files with 58 additions and 14 deletions

View file

@ -644,10 +644,11 @@ fn write_hdlr(
let s = caps.structure(0).unwrap(); let s = caps.structure(0).unwrap();
let (handler_type, name) = match s.name() { let (handler_type, name) = match s.name() {
"video/x-h264" | "video/x-h265" | "image/jpeg" => (b"vide", b"VideoHandler\0"), "video/x-h264" | "video/x-h265" | "image/jpeg" => (b"vide", b"VideoHandler\0".as_slice()),
"audio/mpeg" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { "audio/mpeg" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => {
(b"soun", b"SoundHandler\0") (b"soun", b"SoundHandler\0".as_slice())
} }
"application/x-onvif-metadata" => (b"meta", b"MetadataHandler\0".as_slice()),
_ => unreachable!(), _ => unreachable!(),
}; };
@ -680,6 +681,11 @@ fn write_minf(
write_smhd(v, cfg) write_smhd(v, cfg)
})? })?
} }
"application/x-onvif-metadata" => {
write_full_box(v, b"nmhd", FULL_BOX_VERSION_0, FULL_BOX_FLAGS_NONE, |_v| {
Ok(())
})?
}
_ => unreachable!(), _ => unreachable!(),
} }
@ -785,6 +791,7 @@ fn write_stsd(
"audio/mpeg" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { "audio/mpeg" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => {
write_audio_sample_entry(v, cfg, caps)? write_audio_sample_entry(v, cfg, caps)?
} }
"application/x-onvif-metadata" => write_xml_meta_data_sample_entry(v, cfg, caps)?,
_ => unreachable!(), _ => unreachable!(),
} }
@ -1246,6 +1253,34 @@ fn write_esds_aac(v: &mut Vec<u8>, codec_data: &[u8]) -> Result<(), Error> {
) )
} }
fn write_xml_meta_data_sample_entry(
v: &mut Vec<u8>,
_cfg: &super::HeaderConfiguration,
caps: &gst::CapsRef,
) -> Result<(), Error> {
let s = caps.structure(0).unwrap();
let namespace = match s.name() {
"application/x-onvif-metadata" => b"http://www.onvif.org/ver10/schema",
_ => unreachable!(),
};
write_sample_entry_box(v, b"metx", move |v| {
// content_encoding, empty string
v.push(0);
// namespace
v.extend_from_slice(namespace);
v.push(0);
// schema_location, empty string list
v.push(0);
Ok(())
})?;
Ok(())
}
fn write_stts(v: &mut Vec<u8>, _cfg: &super::HeaderConfiguration) -> Result<(), Error> { fn write_stts(v: &mut Vec<u8>, _cfg: &super::HeaderConfiguration) -> Result<(), Error> {
// Entry count // Entry count
v.extend(0u32.to_be_bytes()); v.extend(0u32.to_be_bytes());
@ -1681,7 +1716,12 @@ fn write_traf(
let check_dts = matches!(s.name(), "video/x-h264" | "video/x-h265"); let check_dts = matches!(s.name(), "video/x-h264" | "video/x-h265");
let intra_only = matches!( let intra_only = matches!(
s.name(), s.name(),
"audio/mpeg" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" | "image/jpeg" "audio/mpeg"
| "audio/x-alaw"
| "audio/x-mulaw"
| "audio/x-adpcm"
| "image/jpeg"
| "application/x-onvif-metadata"
); );
// Analyze all buffers to know what values can be put into the tfhd for all samples and what // Analyze all buffers to know what values can be put into the tfhd for all samples and what

View file

@ -860,6 +860,9 @@ impl FMP4Mux {
"audio/x-adpcm" => { "audio/x-adpcm" => {
intra_only = true; intra_only = true;
} }
"application/x-onvif-metadata" => {
intra_only = true;
}
_ => unreachable!(), _ => unreachable!(),
} }
@ -879,32 +882,30 @@ impl FMP4Mux {
return Err(gst::FlowError::Error); return Err(gst::FlowError::Error);
} }
// Sort video streams first and then audio streams, and each group by pad name. // Sort video streams first and then audio streams and then metadata streams, and each group by pad name.
state.streams.sort_by(|a, b| { state.streams.sort_by(|a, b| {
let stream_type_of_caps = |caps: &gst::CapsRef| { let order_of_caps = |caps: &gst::CapsRef| {
let s = caps.structure(0).unwrap(); let s = caps.structure(0).unwrap();
if s.name().starts_with("video/") { if s.name().starts_with("video/") {
gst::StreamType::VIDEO 0
} else if s.name().starts_with("audio/") { } else if s.name().starts_with("audio/") {
gst::StreamType::AUDIO 1
} else if s.name().starts_with("application/x-onvif-metadata") {
2
} else { } else {
unimplemented!(); unimplemented!();
} }
}; };
let st_a = stream_type_of_caps(&a.caps); let st_a = order_of_caps(&a.caps);
let st_b = stream_type_of_caps(&b.caps); let st_b = order_of_caps(&b.caps);
if st_a == st_b { if st_a == st_b {
return a.sinkpad.name().cmp(&b.sinkpad.name()); return a.sinkpad.name().cmp(&b.sinkpad.name());
} }
if st_a == gst::StreamType::VIDEO { st_a.cmp(&st_b)
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Greater
}
}); });
Ok(()) Ok(())
@ -1963,6 +1964,9 @@ impl ElementImpl for ONVIFFMP4Mux {
.field("rate", 8000i32) .field("rate", 8000i32)
.field("bitrate", gst::List::new([16000i32, 24000, 32000, 40000])) .field("bitrate", gst::List::new([16000i32, 24000, 32000, 40000]))
.build(), .build(),
gst::Structure::builder("application/x-onvif-metadata")
.field("encoding", "utf8")
.build(),
] ]
.into_iter() .into_iter()
.collect::<gst::Caps>(), .collect::<gst::Caps>(),