Valmir Junior
Valmir Junior

Reputation: 137

In Rust, is there a way to subclass `gstreamer::Bin` such that I still have its original methods?

I am trying to subclass gstreamer::Bin to override its Bin::handle_message method, with the intention of intercepting end-of-stream messages, dropping them and signalling that they were dropped (using the default Bin::handle_message behaviour if they're not end-of-stream messages).

The thing is, I still need its original methods (like Bin::add, Bin::add_pad and such) because the only behaviour that I need stacked on top of the original gstreamer::Bin is the message interception like previously mentioned.

I mention that I still need its original methods because when I tried to follow this example, the resulting CustomBin that I wrote did not satisfy IsA<Bin>, and, thus, was not eligible for the blanket implementation of gstreamer::GstBinExt, which in turn made it not have the original gstreamer::Bin methods.

I also came across this example, but ultimately I do not want to reimplement the message interception behaviour for every set of elements that I want to dynamically add and remove from my pipeline. I also don't need to register it as an element proper, I just need to use it within my own crate, and the pipelines defined in it.

For reference, I am going to attach below my attempt of achieving this.

What am I missing?


pub struct CustomBin {
    eos_guard: Mutex<Option<Sender<()>>>,
}

impl Default for CustomBin {
    fn default() -> Self {
        Self {
            eos_guard: Mutex::new(None),
        }
    }
}

impl ObjectImpl for CustomBin {
    glib_object_impl!();
}

impl ElementImpl for CustomBin {}

impl BinImpl for CustomBin {
    fn handle_message(&self, bin: &Bin, message: Message) {
        if let MessageView::Eos(_) = message.view() {
            if let Some(sender) = self.eos_guard.lock().unwrap().take() {
                sender.send(()).unwrap_or(());
            } else {
                self.parent_handle_message(bin, message);
            }
        } else {
            self.parent_handle_message(bin, message);
        }
    }
}

impl ObjectSubclass for CustomBin {
    const NAME: &'static str = "GstCustomBin";
    type ParentType = Bin;
    type Instance = ElementInstanceStruct<Self>;
    type Class = ClassStruct<Self>;

    glib_object_subclass!();

    fn new() -> Self {
        Self::default()
    }
}

impl CustomBin {
    pub fn install_eos_guard(&self) -> Receiver<()> {
        let mut eos_guard = self.eos_guard.lock().unwrap();
        let (sender, receiver) = channel();

        if eos_guard.is_some() {
            panic!("End-of-stream guard was already installed");
        } else {
            eos_guard.replace(sender);
        }

        receiver
    }
}

Upvotes: 1

Views: 482

Answers (1)

Sebastian Dr&#246;ge
Sebastian Dr&#246;ge

Reputation: 2143

Your CustomBin struct is the internal implementation of the type and not a glib::Object (or gst::Bin) by itself. If you compare it with e.g. Java then this is all the private fields and protected virtual methods.

This is sufficient to create a new instance and also to use it as any of its parent classes

let obj: glib::Object = glib::Object::new(CustomBin::get_type(), &[("name", "bla")]).unwrap();
let bin: gst::Bin = obj.downcast::<gst::Bin>().unwrap();

Note that obj and bin above are not your CustomBin struct.

To create a public Rust API around your CustomBin you would use the glib_wrapper! macro. You can find an example of that here. Don't look only at those highlighted lines but also all the code above it.

Upvotes: 2

Related Questions