mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-01-05 16:58:40 +00:00
transcriberbin: Add support for runtime translation-languages update
Allows updating translation-languages at runtime Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1162>
This commit is contained in:
parent
65c6117962
commit
538e2e0c9e
2 changed files with 161 additions and 13 deletions
|
@ -5020,9 +5020,9 @@
|
||||||
"blurb": "A map of CEA 608 channels to language codes, eg translation-languages=\"languages, CC1=fr, CC3=transcript\" will map the French translation to CC1 and the original transcript to CC3",
|
"blurb": "A map of CEA 608 channels to language codes, eg translation-languages=\"languages, CC1=fr, CC3=transcript\" will map the French translation to CC1 and the original transcript to CC3",
|
||||||
"conditionally-available": false,
|
"conditionally-available": false,
|
||||||
"construct": false,
|
"construct": false,
|
||||||
"construct-only": true,
|
"construct-only": false,
|
||||||
"controllable": false,
|
"controllable": false,
|
||||||
"mutable": "null",
|
"mutable": "playing",
|
||||||
"readable": true,
|
"readable": true,
|
||||||
"type": "GstStructure",
|
"type": "GstStructure",
|
||||||
"writable": true
|
"writable": true
|
||||||
|
|
|
@ -73,6 +73,7 @@ struct State {
|
||||||
audio_tee: gst::Element,
|
audio_tee: gst::Element,
|
||||||
transcriber_aconv: gst::Element,
|
transcriber_aconv: gst::Element,
|
||||||
transcriber: gst::Element,
|
transcriber: gst::Element,
|
||||||
|
ccmux: gst::Element,
|
||||||
cccombiner: gst::Element,
|
cccombiner: gst::Element,
|
||||||
transcription_bin: gst::Bin,
|
transcription_bin: gst::Bin,
|
||||||
transcription_channels: HashMap<String, TranscriptionChannel>,
|
transcription_channels: HashMap<String, TranscriptionChannel>,
|
||||||
|
@ -168,16 +169,13 @@ impl TranscriberBin {
|
||||||
.property("max-size-time", 5_000_000_000u64)
|
.property("max-size-time", 5_000_000_000u64)
|
||||||
.property_from_str("leaky", "downstream")
|
.property_from_str("leaky", "downstream")
|
||||||
.build()?;
|
.build()?;
|
||||||
let ccmux = gst::ElementFactory::make("cea608mux")
|
|
||||||
.property_from_str("start-time-selection", "first")
|
|
||||||
.build()?;
|
|
||||||
let ccconverter = gst::ElementFactory::make("ccconverter").build()?;
|
let ccconverter = gst::ElementFactory::make("ccconverter").build()?;
|
||||||
|
|
||||||
state.transcription_bin.add_many([
|
state.transcription_bin.add_many([
|
||||||
&aqueue_transcription,
|
&aqueue_transcription,
|
||||||
&state.transcriber_aconv,
|
&state.transcriber_aconv,
|
||||||
&state.transcriber,
|
&state.transcriber,
|
||||||
&ccmux,
|
&state.ccmux,
|
||||||
&ccconverter,
|
&ccconverter,
|
||||||
&state.cccapsfilter,
|
&state.cccapsfilter,
|
||||||
&state.transcription_valve,
|
&state.transcription_valve,
|
||||||
|
@ -190,7 +188,7 @@ impl TranscriberBin {
|
||||||
])?;
|
])?;
|
||||||
|
|
||||||
gst::Element::link_many([
|
gst::Element::link_many([
|
||||||
&ccmux,
|
&state.ccmux,
|
||||||
&ccconverter,
|
&ccconverter,
|
||||||
&state.cccapsfilter,
|
&state.cccapsfilter,
|
||||||
&state.transcription_valve,
|
&state.transcription_valve,
|
||||||
|
@ -201,13 +199,14 @@ impl TranscriberBin {
|
||||||
|
|
||||||
channel.link_transcriber(&state.transcriber)?;
|
channel.link_transcriber(&state.transcriber)?;
|
||||||
|
|
||||||
let ccmux_pad = ccmux
|
let ccmux_pad = state
|
||||||
|
.ccmux
|
||||||
.request_pad_simple(padname)
|
.request_pad_simple(padname)
|
||||||
.ok_or(anyhow!("Failed to request ccmux sink pad"))?;
|
.ok_or(anyhow!("Failed to request ccmux sink pad"))?;
|
||||||
channel.bin.static_pad("src").unwrap().link(&ccmux_pad)?;
|
channel.bin.static_pad("src").unwrap().link(&ccmux_pad)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
ccmux.set_property("latency", CEA608MUX_LATENCY);
|
state.ccmux.set_property("latency", CEA608MUX_LATENCY);
|
||||||
|
|
||||||
let transcription_audio_sinkpad = gst::GhostPad::with_target(
|
let transcription_audio_sinkpad = gst::GhostPad::with_target(
|
||||||
Some("sink"),
|
Some("sink"),
|
||||||
|
@ -358,9 +357,12 @@ impl TranscriberBin {
|
||||||
state.transcription_bin.set_locked_state(false);
|
state.transcription_bin.set_locked_state(false);
|
||||||
state.transcription_bin.sync_state_with_parent().unwrap();
|
state.transcription_bin.sync_state_with_parent().unwrap();
|
||||||
|
|
||||||
let audio_tee_pad = state.audio_tee.request_pad_simple("src_%u").unwrap();
|
|
||||||
let transcription_sink_pad = state.transcription_bin.static_pad("sink").unwrap();
|
let transcription_sink_pad = state.transcription_bin.static_pad("sink").unwrap();
|
||||||
audio_tee_pad.link(&transcription_sink_pad).unwrap();
|
// Might be linked already if "translation-languages" is set
|
||||||
|
if transcription_sink_pad.peer().is_none() {
|
||||||
|
let audio_tee_pad = state.audio_tee.request_pad_simple("src_%u").unwrap();
|
||||||
|
audio_tee_pad.link(&transcription_sink_pad).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(settings);
|
drop(settings);
|
||||||
|
@ -495,6 +497,139 @@ impl TranscriberBin {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reconfigure_channels(&self) -> Result<(), Error> {
|
||||||
|
let mut state = self.state.lock().unwrap();
|
||||||
|
|
||||||
|
if let Some(ref mut state) = state.as_mut() {
|
||||||
|
let settings = self.settings.lock().unwrap();
|
||||||
|
|
||||||
|
gst::debug!(
|
||||||
|
CAT,
|
||||||
|
imp: self,
|
||||||
|
"Updating transcription/translation channel configuration"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Unlink sinkpad temporarily
|
||||||
|
let sinkpad = state.transcription_bin.static_pad("sink").unwrap();
|
||||||
|
let peer = sinkpad.peer();
|
||||||
|
if let Some(peer) = &peer {
|
||||||
|
gst::debug!(CAT, imp: self, "Unlinking {:?}", peer);
|
||||||
|
peer.unlink(&sinkpad)?;
|
||||||
|
state.audio_tee.release_request_pad(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.transcription_bin.set_locked_state(true);
|
||||||
|
state.transcription_bin.set_state(gst::State::Null).unwrap();
|
||||||
|
|
||||||
|
for channel in state.transcription_channels.values() {
|
||||||
|
let sinkpad = channel.bin.static_pad("sink").unwrap();
|
||||||
|
if let Some(peer) = sinkpad.peer() {
|
||||||
|
peer.unlink(&sinkpad)?;
|
||||||
|
if channel.language != "transcript" {
|
||||||
|
state.transcriber.release_request_pad(&peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let srcpad = channel.bin.static_pad("src").unwrap();
|
||||||
|
if let Some(peer) = srcpad.peer() {
|
||||||
|
srcpad.unlink(&peer)?;
|
||||||
|
state.ccmux.release_request_pad(&peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.transcription_bin.remove(&channel.bin)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.transcription_channels.clear();
|
||||||
|
|
||||||
|
if let Some(ref map) = settings.translation_languages {
|
||||||
|
for (key, value) in map.iter() {
|
||||||
|
let channel = key.to_lowercase();
|
||||||
|
if !["cc1", "cc3"].contains(&channel.as_str()) {
|
||||||
|
anyhow::bail!("Unknown 608 channel {}, valid values are cc1, cc3", channel);
|
||||||
|
}
|
||||||
|
let language_code = value.get::<String>()?;
|
||||||
|
|
||||||
|
state.transcription_channels.insert(
|
||||||
|
channel.to_owned(),
|
||||||
|
self.construct_channel_bin(&language_code).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.transcription_channels.insert(
|
||||||
|
"cc1".to_string(),
|
||||||
|
self.construct_channel_bin("transcript").unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (padname, channel) in &state.transcription_channels {
|
||||||
|
state.transcription_bin.add(&channel.bin)?;
|
||||||
|
|
||||||
|
channel.link_transcriber(&state.transcriber)?;
|
||||||
|
|
||||||
|
let ccmux_pad = state
|
||||||
|
.ccmux
|
||||||
|
.request_pad_simple(padname)
|
||||||
|
.ok_or(anyhow!("Failed to request ccmux sink pad"))?;
|
||||||
|
channel.bin.static_pad("src").unwrap().link(&ccmux_pad)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(settings);
|
||||||
|
self.setup_cc_mode(state);
|
||||||
|
|
||||||
|
if !self.settings.lock().unwrap().passthrough {
|
||||||
|
gst::debug!(CAT, imp: self, "Syncing state with parent");
|
||||||
|
|
||||||
|
state.transcription_bin.set_locked_state(false);
|
||||||
|
state.transcription_bin.sync_state_with_parent()?;
|
||||||
|
|
||||||
|
let audio_tee_pad = state.audio_tee.request_pad_simple("src_%u").unwrap();
|
||||||
|
audio_tee_pad.link(&sinkpad)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_channels(&self) {
|
||||||
|
let s = self.state.lock().unwrap();
|
||||||
|
|
||||||
|
if let Some(state) = s.as_ref() {
|
||||||
|
gst::debug!(
|
||||||
|
CAT,
|
||||||
|
imp: self,
|
||||||
|
"Schedule transcription/translation channel update"
|
||||||
|
);
|
||||||
|
|
||||||
|
let sinkpad = state.transcription_bin.static_pad("sink").unwrap();
|
||||||
|
let imp_weak = self.downgrade();
|
||||||
|
drop(s);
|
||||||
|
|
||||||
|
let _ = sinkpad.add_probe(
|
||||||
|
gst::PadProbeType::IDLE
|
||||||
|
| gst::PadProbeType::BUFFER
|
||||||
|
| gst::PadProbeType::EVENT_DOWNSTREAM,
|
||||||
|
move |_pad, _info| {
|
||||||
|
let imp = match imp_weak.upgrade() {
|
||||||
|
None => return gst::PadProbeReturn::Remove,
|
||||||
|
Some(imp) => imp,
|
||||||
|
};
|
||||||
|
|
||||||
|
if imp.reconfigure_channels().is_err() {
|
||||||
|
gst::element_imp_error!(
|
||||||
|
imp,
|
||||||
|
gst::StreamError::Failed,
|
||||||
|
["Couldn't reconfigure channels"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst::PadProbeReturn::Remove
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
gst::debug!(CAT, imp: self, "Transcriber is not configured yet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
fn src_query(&self, pad: &gst::Pad, query: &mut gst::QueryRef) -> bool {
|
fn src_query(&self, pad: &gst::Pad, query: &mut gst::QueryRef) -> bool {
|
||||||
use gst::QueryViewMut;
|
use gst::QueryViewMut;
|
||||||
|
@ -572,6 +707,9 @@ impl TranscriberBin {
|
||||||
let transcription_valve = gst::ElementFactory::make("valve")
|
let transcription_valve = gst::ElementFactory::make("valve")
|
||||||
.property_from_str("drop-mode", "transform-to-gap")
|
.property_from_str("drop-mode", "transform-to-gap")
|
||||||
.build()?;
|
.build()?;
|
||||||
|
let ccmux = gst::ElementFactory::make("cea608mux")
|
||||||
|
.property_from_str("start-time-selection", "first")
|
||||||
|
.build()?;
|
||||||
|
|
||||||
let mut transcription_channels = HashMap::new();
|
let mut transcription_channels = HashMap::new();
|
||||||
|
|
||||||
|
@ -602,6 +740,7 @@ impl TranscriberBin {
|
||||||
video_queue,
|
video_queue,
|
||||||
transcriber_aconv,
|
transcriber_aconv,
|
||||||
transcriber,
|
transcriber,
|
||||||
|
ccmux,
|
||||||
audio_tee,
|
audio_tee,
|
||||||
cccombiner,
|
cccombiner,
|
||||||
transcription_bin,
|
transcription_bin,
|
||||||
|
@ -750,7 +889,7 @@ impl ObjectImpl for TranscriberBin {
|
||||||
glib::ParamSpecBoxed::builder::<gst::Structure>("translation-languages")
|
glib::ParamSpecBoxed::builder::<gst::Structure>("translation-languages")
|
||||||
.nick("Translation languages")
|
.nick("Translation languages")
|
||||||
.blurb("A map of CEA 608 channels to language codes, eg translation-languages=\"languages, CC1=fr, CC3=transcript\" will map the French translation to CC1 and the original transcript to CC3")
|
.blurb("A map of CEA 608 channels to language codes, eg translation-languages=\"languages, CC1=fr, CC3=transcript\" will map the French translation to CC1 and the original transcript to CC3")
|
||||||
.construct_only()
|
.mutable_playing()
|
||||||
.build(),
|
.build(),
|
||||||
glib::ParamSpecUInt::builder("translate-latency")
|
glib::ParamSpecUInt::builder("translate-latency")
|
||||||
.nick("Translation Latency")
|
.nick("Translation Latency")
|
||||||
|
@ -842,7 +981,16 @@ impl ObjectImpl for TranscriberBin {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.translation_languages = value
|
settings.translation_languages = value
|
||||||
.get::<Option<gst::Structure>>()
|
.get::<Option<gst::Structure>>()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream");
|
||||||
|
gst::debug!(
|
||||||
|
CAT,
|
||||||
|
imp: self,
|
||||||
|
"Updated translation-languages {:?}",
|
||||||
|
settings.translation_languages
|
||||||
|
);
|
||||||
|
drop(settings);
|
||||||
|
|
||||||
|
self.update_channels();
|
||||||
}
|
}
|
||||||
"translate-latency" => {
|
"translate-latency" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
|
|
Loading…
Reference in a new issue