transcriberbin: implement dynamic language update

Previously, transcriberbin only supported updating translation languages
while playing by resetting the state of the transcriber to NULL
beforehand, as for instance the speechmatics transcriber needs to
reestablish a connection to request new languages.

Now that translationbin exists, we can request new languages without
restarting the transcriber (this commit also implements support for this
in translationbin).

There is some code duplication as the old method still needs to be
supported, and not all code was trivially factorizable, but after some
refactoring most of the code for updating languages is shared
nevertheless.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2072>
This commit is contained in:
Mathieu Duponchelle 2025-02-12 19:01:53 +01:00
parent 2130c3bfbe
commit d0db66d61e
3 changed files with 934 additions and 193 deletions

View file

@ -9669,7 +9669,7 @@
"construct-only": false,
"controllable": false,
"default": "fr-FR",
"mutable": "ready",
"mutable": "null",
"readable": true,
"type": "gchararray",
"writable": true

File diff suppressed because it is too large Load diff

View file

@ -64,6 +64,74 @@ pub struct TranslationBin {
}
impl TranslationBin {
fn prepare_translation_srcpad(
&self,
elem: &super::TranslationBin,
srcpad: &super::TranslationSrcPad,
input_language_code: &str,
translate_latency_ms: u32,
tee: &gst::Element,
) -> Result<(), Error> {
let output_language_code = srcpad.imp().settings.lock().unwrap().language_code.clone();
let queue = gst::ElementFactory::make("queue").build()?;
let translator = gst::ElementFactory::make("awstranslate")
.property("input-language-code", input_language_code)
.property("output-language-code", output_language_code)
.build()?;
if translator.has_property_with_type("latency", u32::static_type()) {
translator.set_property("latency", translate_latency_ms);
}
elem.add_many([&queue, &translator])?;
queue.sync_state_with_parent()?;
translator.sync_state_with_parent()?;
tee.link(&queue)?;
queue.link(&translator)?;
srcpad.set_target(Some(
&translator
.static_pad("src")
.ok_or(anyhow!("No pad named src on translator"))?,
))?;
let mut pad_state = srcpad.imp().state.lock().unwrap();
pad_state.queue = Some(queue);
pad_state.translator = Some(translator);
Ok(())
}
fn unprepare_translation_srcpad(
&self,
elem: &super::TranslationBin,
tee: &gst::Element,
srcpad: &super::TranslationSrcPad,
) -> Result<(), Error> {
let (queue, translator) = {
let mut pad_state = srcpad.imp().state.lock().unwrap();
(
pad_state.queue.take().unwrap(),
pad_state.translator.take().unwrap(),
)
};
tee.unlink(&queue);
elem.remove_many([&queue, &translator])?;
srcpad.set_target(None::<&gst::Pad>)?;
let _ = queue.set_state(gst::State::Null);
let _ = translator.set_state(gst::State::Null);
Ok(())
}
fn prepare(&self) -> Result<(), Error> {
let (transcriber, srcpads) = {
let state = self.state.lock().unwrap();
@ -124,35 +192,13 @@ impl TranslationBin {
.set_target(Some(&queue.static_pad("src").unwrap()))?;
for srcpad in srcpads {
let output_language_code = srcpad.imp().settings.lock().unwrap().language_code.clone();
let queue = gst::ElementFactory::make("queue").build()?;
let translator = gst::ElementFactory::make("awstranslate")
.property("input-language-code", &language_code)
.property("output-language-code", output_language_code)
.build()?;
if translator.has_property_with_type("latency", u32::static_type()) {
translator.set_property("latency", translate_latency_ms);
}
obj.add_many([&queue, &translator])?;
queue.sync_state_with_parent()?;
translator.sync_state_with_parent()?;
tee.link(&queue)?;
queue.link(&translator)?;
srcpad.set_target(Some(
&translator
.static_pad("src")
.ok_or(anyhow!("No pad named src on translator"))?,
))?;
let mut pad_state = srcpad.imp().state.lock().unwrap();
pad_state.queue = Some(queue);
pad_state.translator = Some(translator);
self.prepare_translation_srcpad(
&obj,
&srcpad,
&language_code,
translate_latency_ms,
&tee,
)?;
}
let mut state = self.state.lock().unwrap();
@ -176,6 +222,12 @@ impl TranslationBin {
};
let obj = self.obj();
let srcpads = self.state.lock().unwrap().srcpads.clone();
for srcpad in srcpads {
self.unprepare_translation_srcpad(&obj, &tee, &srcpad)?;
}
transcriber.unlink(&tee);
obj.remove_many([&transcriber, &tee, &queue])?;
@ -183,26 +235,6 @@ impl TranslationBin {
self.audio_sinkpad.set_target(None::<&gst::Pad>)?;
self.transcript_srcpad.set_target(None::<&gst::Pad>)?;
let srcpads = self.state.lock().unwrap().srcpads.clone();
for srcpad in srcpads {
let (queue, translator) = {
let mut pad_state = srcpad.imp().state.lock().unwrap();
(
pad_state.queue.take().unwrap(),
pad_state.translator.take().unwrap(),
)
};
obj.remove_many([&queue, &translator])?;
srcpad.set_target(None::<&gst::Pad>)?;
let _ = queue.set_state(gst::State::Null);
let _ = translator.set_state(gst::State::Null);
}
let _ = transcriber.set_state(gst::State::Null);
let _ = tee.set_state(gst::State::Null);
let _ = queue.set_state(gst::State::Null);
@ -370,11 +402,40 @@ impl ElementImpl for TranslationBin {
self.state.lock().unwrap().srcpads.insert(pad.clone());
if let Some(tee) = self.state.lock().unwrap().tee.clone() {
let Settings {
translate_latency,
language_code,
..
} = self.settings.lock().unwrap().clone();
let translate_latency_ms = translate_latency.mseconds() as u32;
if let Err(err) = self.prepare_translation_srcpad(
&self.obj(),
&pad,
&language_code,
translate_latency_ms,
&tee,
) {
gst::error!(CAT, "Failed to prepare translation source pad: {err:?}");
return None;
}
}
Some(pad.upcast())
}
fn release_pad(&self, pad: &gst::Pad) {
let _ = self.state.lock().unwrap().srcpads.remove(pad);
let srcpad = self.state.lock().unwrap().srcpads.take(pad);
if let Some(srcpad) = srcpad {
if let Some(tee) = self.state.lock().unwrap().tee.clone() {
if let Err(err) = self.unprepare_translation_srcpad(&self.obj(), &tee, &srcpad) {
gst::warning!(CAT, "Failed to unprepare translation source pad: {err:?}");
}
}
}
let _ = self.obj().remove_pad(pad);
}
@ -511,7 +572,6 @@ impl ObjectImpl for TranslationSrcPad {
.nick("Language Code")
.blurb("The language of the output stream")
.default_value(Some(DEFAULT_OUTPUT_LANG_CODE))
.mutable_ready()
.build()]
});
@ -523,7 +583,11 @@ impl ObjectImpl for TranslationSrcPad {
"language-code" => {
let language_code: String = value.get().expect("type checked upstream");
let mut settings = self.settings.lock().unwrap();
settings.language_code = language_code;
settings.language_code = language_code.clone();
if let Some(translator) = self.state.lock().unwrap().translator.as_ref() {
translator.set_property("output-language-code", language_code);
}
}
_ => unimplemented!(),
}