diff --git a/mux/fmp4/examples/hls_vod.rs b/mux/fmp4/examples/hls_vod.rs index c84102f4..55b06497 100644 --- a/mux/fmp4/examples/hls_vod.rs +++ b/mux/fmp4/examples/hls_vod.rs @@ -360,6 +360,10 @@ impl AudioStream { .property("samplesperbuffer", 4410) .property_from_str("wave", &self.wave) .build()?; + let taginject = gst::ElementFactory::make("taginject") + .property_from_str("tags", &format!("language-code={}", self.lang)) + .property_from_str("scope", "stream") + .build()?; let raw_capsfilter = gst::ElementFactory::make("capsfilter") .property( "caps", @@ -374,9 +378,23 @@ impl AudioStream { .build()?; let appsink = gst_app::AppSink::builder().buffer_list(true).build(); - pipeline.add_many([&src, &raw_capsfilter, &enc, &mux, appsink.upcast_ref()])?; + pipeline.add_many([ + &src, + &taginject, + &raw_capsfilter, + &enc, + &mux, + appsink.upcast_ref(), + ])?; - gst::Element::link_many([&src, &raw_capsfilter, &enc, &mux, appsink.upcast_ref()])?; + gst::Element::link_many([ + &src, + &taginject, + &raw_capsfilter, + &enc, + &mux, + appsink.upcast_ref(), + ])?; probe_encoder(state, enc); @@ -416,7 +434,7 @@ fn main() -> Result<(), Error> { }, AudioStream { name: "audio_1".to_string(), - lang: "fre".to_string(), + lang: "fra".to_string(), default: false, wave: "white-noise".to_string(), }, diff --git a/mux/fmp4/src/fmp4mux/boxes.rs b/mux/fmp4/src/fmp4mux/boxes.rs index e2fdeae7..2b9f8dd6 100644 --- a/mux/fmp4/src/fmp4mux/boxes.rs +++ b/mux/fmp4/src/fmp4mux/boxes.rs @@ -699,10 +699,6 @@ fn write_tref( fn language_code(lang: impl std::borrow::Borrow<[u8; 3]>) -> u16 { let lang = lang.borrow(); - - // TODO: Need to relax this once we get the language code from tags - assert!(lang.iter().all(u8::is_ascii_lowercase)); - (((lang[0] as u16 - 0x60) & 0x1F) << 10) + (((lang[1] as u16 - 0x60) & 0x1F) << 5) + ((lang[2] as u16 - 0x60) & 0x1F) @@ -710,7 +706,7 @@ fn language_code(lang: impl std::borrow::Borrow<[u8; 3]>) -> u16 { fn write_mdhd( v: &mut Vec, - _cfg: &super::HeaderConfiguration, + cfg: &super::HeaderConfiguration, stream: &super::HeaderStream, creation_time: u64, ) -> Result<(), Error> { @@ -724,8 +720,11 @@ fn write_mdhd( v.extend(0u64.to_be_bytes()); // Language as ISO-639-2/T - // TODO: get actual language from the tags - v.extend(language_code(b"und").to_be_bytes()); + if let Some(lang) = cfg.language_code { + v.extend(language_code(lang).to_be_bytes()); + } else { + v.extend(language_code(b"und").to_be_bytes()); + } // Pre-defined v.extend([0u8; 2]); diff --git a/mux/fmp4/src/fmp4mux/imp.rs b/mux/fmp4/src/fmp4mux/imp.rs index 501a482e..026771a5 100644 --- a/mux/fmp4/src/fmp4mux/imp.rs +++ b/mux/fmp4/src/fmp4mux/imp.rs @@ -250,6 +250,8 @@ struct State { end_pts: Option, /// Start DTS of the whole stream start_dts: Option, + /// Language code from tags + language_code: Option<[u8; 3]>, /// Start PTS of the current fragment fragment_start_pts: Option, @@ -2707,6 +2709,7 @@ impl FMP4Mux { streams, write_mehd: settings.write_mehd, duration: if at_eos { duration } else { None }, + language_code: state.language_code.clone(), start_utc_time: if variant == super::Variant::ONVIF { state .earliest_pts @@ -3140,8 +3143,22 @@ impl AggregatorImpl for FMP4Mux { self.parent_sink_event(aggregator_pad, event) } - EventView::Tag(_ev) => { - // TODO: Maybe store for putting into the headers of the next fragment? + EventView::Tag(ev) => { + if let Some(tag_value) = ev.tag().get::() { + let lang = tag_value.get(); + gst::trace!(CAT, imp: self, "Received language code from tags: {:?}", lang); + + // Language as ISO-639-2/T + if lang.len() == 3 && lang.chars().all(|c| c.is_ascii_lowercase()) { + let mut state = self.state.lock().unwrap(); + + let mut language_code: [u8; 3] = [0; 3]; + for (i, c) in lang.chars().enumerate() { + language_code[i] = c as u8; + } + state.language_code = Some(language_code); + } + } self.parent_sink_event(aggregator_pad, event) } diff --git a/mux/fmp4/src/fmp4mux/mod.rs b/mux/fmp4/src/fmp4mux/mod.rs index bb50c289..f6049b2d 100644 --- a/mux/fmp4/src/fmp4mux/mod.rs +++ b/mux/fmp4/src/fmp4mux/mod.rs @@ -85,6 +85,7 @@ pub(crate) struct HeaderConfiguration { write_mehd: bool, duration: Option, + language_code: Option<[u8; 3]>, /// Start UTC time in ONVIF mode. /// Since Jan 1 1601 in 100ns units.