From 5d85a687c1add9517c2343fa3b4f4fd792c3f1c9 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sun, 26 Mar 2023 22:26:46 +0200 Subject: [PATCH] Send DTMF events typed --- src/main.rs | 153 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 98 insertions(+), 55 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8661d3d..dd7cf3d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,41 +4,51 @@ use anyhow::Result; use gst::glib; use gst::glib::once_cell::sync::Lazy; use gst::prelude::*; +use rand::prelude::*; use std::thread; use std::thread::sleep; -use rand::prelude::*; use tokio::runtime::Builder; static CAT: Lazy = Lazy::new(|| { gst::DebugCategory::new("main", gst::DebugColorFlags::empty(), Some("Main function")) }); -#[derive(Debug, Clone)] -enum DtmfEvent { - Start(DtmfStart), - End(i32), +#[derive(Debug, Clone, Copy)] +struct DtmfEvent(i32); + +impl TryFrom<&gst::StructureRef> for DtmfEvent { + type Error = anyhow::Error; + + fn try_from(structure: &gst::StructureRef) -> anyhow::Result { + let name = structure.name().to_string(); + if name != "dtmf-event" { + anyhow::bail!("Not a dtmf-event structure: {name}"); + } + let number = structure.get::("number")?; + Ok(Self(number)) + } } -#[derive(Debug, Clone)] -struct DtmfStart { - number: i32, - volume: i32, +#[derive(Debug, Clone, Copy)] +enum DtmfCommand { + Start(i32), + End(Option), } -impl DtmfEvent { +impl DtmfCommand { fn start(number: i32) -> Self { - Self::Start(DtmfStart { number, volume: 36 }) + Self::Start(number) } fn end(self) -> Self { match self { - Self::Start(start) => Self::End(start.number), + Self::Start(number) => Self::End(Some(number)), Self::End(_) => self, } } } -impl TryFrom<&gst::StructureRef> for DtmfEvent { +impl TryFrom<&gst::StructureRef> for DtmfCommand { type Error = anyhow::Error; fn try_from(structure: &gst::StructureRef) -> anyhow::Result { @@ -46,59 +56,60 @@ impl TryFrom<&gst::StructureRef> for DtmfEvent { if !name.starts_with("dtmf-event") { anyhow::bail!("Not a dtmf-event structure: {name}"); } - let number = structure.get::("number")?; - return if structure.get::("start")? { - let volume = structure.get::("volume")?; - Ok(Self::Start(DtmfStart { number, volume })) + let number = structure.get_optional::("number")?; + if structure.get::("start")? { + Ok(Self::Start(number.ok_or_else(|| { + anyhow::anyhow!("No number specified for start DTMF command") + })?)) } else { Ok(Self::End(number)) } } } -impl From for gst::Structure { - fn from(event: DtmfEvent) -> Self { - match event { - DtmfEvent::Start(start) => { - gst::Structure::builder("dtmf-event") - .field("type", 1) - .field("start", true) - .field("number", start.number) - .field("volume", start.volume) - .build() - } - DtmfEvent::End(number) => { +impl TryFrom for gst::Structure { + type Error = anyhow::Error; + + fn try_from(event: DtmfCommand) -> anyhow::Result { + let structure = match event { + DtmfCommand::Start(number) => gst::Structure::builder("dtmf-event") + .field("type", 1) + .field("start", true) + .field("number", number) + .field("volume", 36) + .build(), + DtmfCommand::End(number) => { + let Some(number) = number else { + anyhow::bail!("Cannot send end DTMF command without a specified number"); + }; gst::Structure::builder("dtmf-event") .field("type", 1) .field("start", false) .field("number", number) .build() } - } + }; + Ok(structure) } } fn main() -> Result<()> { gst::init()?; - //videotestsrc ! videoconvert ! timeoverlay shaded-background=true ! gtksink let pipeline = gst::parse_launch( r#" dtmfsrc name=src ! mix. - audiotestsrc freq=20 ! audiomixer name=mix ! tee name=t ! queue ! audioconvert ! autoaudiosink name=audiosink + audiotestsrc freq=0 ! audiomixer name=mix ! dtmfdetect ! audioconvert ! autoaudiosink name=audiosink "#, )? .downcast::() .unwrap(); - // t.! queue ! audioconvert ! dtmfdetect let context = glib::MainContext::default(); let main_loop = glib::MainLoop::new(Some(&context), false); - pipeline.set_state(gst::State::Playing)?; - let bus = pipeline.bus().unwrap(); bus.add_watch({ let main_loop = main_loop.clone(); @@ -108,38 +119,50 @@ fn main() -> Result<()> { match msg.view() { MessageView::Eos(..) => main_loop.quit(), MessageView::Error(err) => { - gst::error!(CAT, obj: &err.src().unwrap(), + gst::error!(CAT, obj: err.src().unwrap(), "Error from {:?}: {} ({:?})", err.src().map(|s| s.path_string()), err.error(), err.debug() ); main_loop.quit(); - }, + } MessageView::Element(element) => { - let dtmf_event = match DtmfEvent::try_from(element.structure().unwrap()) { - Ok(ev) => ev, - Err(err) => { - gst::info!(CAT, "Failed to parse DTMF event {:?} with error: {:?}", element.structure().unwrap(), err); - return glib::Continue(true); - } - }; match element.structure().unwrap().name().as_str() { "dtmf-event" => { - gst::info!(CAT, "Detected DTMF event"); + let dtmf_event = DtmfEvent::try_from(element.structure().unwrap()) + .expect("Failed to parse DTMF event"); + gst::info!(CAT, "Detected DTMF event: {:?}", dtmf_event); } "dtmf-event-processed" => { - match dtmf_event { - DtmfEvent::Start(start) => { - gst::info!(CAT, "DTMF event {} processed", start.number); + let dtmf_cmd = match DtmfCommand::try_from(element.structure().unwrap()) + { + Ok(ev) => ev, + Err(err) => { + gst::error!( + CAT, + "Failed to parse DTMF event {:?} with error: {:?}", + element.structure().unwrap(), + err + ); + return glib::Continue(true); } - DtmfEvent::End(number) => { - gst::info!(CAT, "Ending DTMF event {number} processed"); + }; + match dtmf_cmd { + DtmfCommand::Start(number) => { + gst::info!(CAT, "Processed DTMF event {}", number); + } + DtmfCommand::End(_) => { + gst::info!(CAT, "Processed ending DTMF event"); } } } _ => { - gst::info!(CAT, "Received unknown event: {:?}", element.structure().unwrap().name()); + gst::error!( + CAT, + "Received unknown event: {:?}", + element.structure().unwrap().name() + ); } } } @@ -160,16 +183,34 @@ fn main() -> Result<()> { let source = pipeline.by_name("src").unwrap(); + // wait pipeline to be running + let bus = pipeline.bus().unwrap(); + while let Some(msg) = bus.timed_pop(None) { + use gst::MessageView; + if let MessageView::StateChanged(state_changed) = msg.view() { + if state_changed.src().unwrap() == &source + && state_changed.current() == gst::State::Playing + { + break; + } + } + } + gst::info!(CAT, "Pipeline is running"); + let mut rng = rand::thread_rng(); loop { - let dtmf_event = DtmfEvent::start(rng.gen_range(0..15)); + let dtmf_cmd = DtmfCommand::start(rng.gen_range(0..15)); - source.send_event(gst::event::CustomUpstream::new(dtmf_event.clone().into())); - gst::info!(CAT, "Sent DTMF event {:?}", dtmf_event); + source.send_event(gst::event::CustomUpstream::new( + dtmf_cmd.try_into().unwrap(), + )); + gst::info!(CAT, "Sent DTMF event {:?}", dtmf_cmd); sleep(std::time::Duration::from_millis(10)); - source.send_event(gst::event::CustomUpstream::new(dtmf_event.end().into())); + source.send_event(gst::event::CustomUpstream::new( + dtmf_cmd.end().try_into().unwrap(), + )); sleep(std::time::Duration::from_millis(1000)); } @@ -189,6 +230,8 @@ fn main() -> Result<()> { } }); + pipeline.set_state(gst::State::Playing)?; + ctrlc::set_handler({ let main_loop = main_loop.clone(); move || {