diff --git a/generic/fmp4/src/fmp4mux/boxes.rs b/generic/fmp4/src/fmp4mux/boxes.rs index fbae0dfe..106f296a 100644 --- a/generic/fmp4/src/fmp4mux/boxes.rs +++ b/generic/fmp4/src/fmp4mux/boxes.rs @@ -644,10 +644,11 @@ fn write_hdlr( let s = caps.structure(0).unwrap(); 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" => { - (b"soun", b"SoundHandler\0") + (b"soun", b"SoundHandler\0".as_slice()) } + "application/x-onvif-metadata" => (b"meta", b"MetadataHandler\0".as_slice()), _ => unreachable!(), }; @@ -680,6 +681,11 @@ fn write_minf( 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!(), } @@ -785,6 +791,7 @@ fn write_stsd( "audio/mpeg" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { write_audio_sample_entry(v, cfg, caps)? } + "application/x-onvif-metadata" => write_xml_meta_data_sample_entry(v, cfg, caps)?, _ => unreachable!(), } @@ -1246,6 +1253,34 @@ fn write_esds_aac(v: &mut Vec, codec_data: &[u8]) -> Result<(), Error> { ) } +fn write_xml_meta_data_sample_entry( + v: &mut Vec, + _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, _cfg: &super::HeaderConfiguration) -> Result<(), Error> { // Entry count 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 intra_only = matches!( 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 diff --git a/generic/fmp4/src/fmp4mux/imp.rs b/generic/fmp4/src/fmp4mux/imp.rs index d5774cc5..399047be 100644 --- a/generic/fmp4/src/fmp4mux/imp.rs +++ b/generic/fmp4/src/fmp4mux/imp.rs @@ -860,6 +860,9 @@ impl FMP4Mux { "audio/x-adpcm" => { intra_only = true; } + "application/x-onvif-metadata" => { + intra_only = true; + } _ => unreachable!(), } @@ -879,32 +882,30 @@ impl FMP4Mux { 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| { - let stream_type_of_caps = |caps: &gst::CapsRef| { + let order_of_caps = |caps: &gst::CapsRef| { let s = caps.structure(0).unwrap(); if s.name().starts_with("video/") { - gst::StreamType::VIDEO + 0 } else if s.name().starts_with("audio/") { - gst::StreamType::AUDIO + 1 + } else if s.name().starts_with("application/x-onvif-metadata") { + 2 } else { unimplemented!(); } }; - let st_a = stream_type_of_caps(&a.caps); - let st_b = stream_type_of_caps(&b.caps); + let st_a = order_of_caps(&a.caps); + let st_b = order_of_caps(&b.caps); if st_a == st_b { return a.sinkpad.name().cmp(&b.sinkpad.name()); } - if st_a == gst::StreamType::VIDEO { - std::cmp::Ordering::Less - } else { - std::cmp::Ordering::Greater - } + st_a.cmp(&st_b) }); Ok(()) @@ -1963,6 +1964,9 @@ impl ElementImpl for ONVIFFMP4Mux { .field("rate", 8000i32) .field("bitrate", gst::List::new([16000i32, 24000, 32000, 40000])) .build(), + gst::Structure::builder("application/x-onvif-metadata") + .field("encoding", "utf8") + .build(), ] .into_iter() .collect::(),