Open webrtc data channel from glib::closure

Hi,

I have a question related to the creation of data channels with webrtcbin. I have one channel called service, and depending on what I receive on it, I’d like to dynamically create additional channels.

A simplified example would be:

let channel = webrtcbin.lock().unwrap().emit_by_name::<WebRTCDataChannel>(
    "create-data-channel",
    &[
        &"service",
        &gst::Structure::builder("config")
            .field("ordered", true)
            .build(),
    ],
);

channel.connect_closure(
    "on-message-data",
    false,
    glib::closure!(move |_channel: &WebRTCDataChannel, msg: &glib::Bytes| {
        // Some processing of msg

        let channel2 = webrtcbin.lock().unwrap().emit_by_name::<WebRTCDataChannel>(
            "create-data-channel",
            &[
                &"dynamic_name",
                &gst::Structure::builder("config")
                    .field("ordered", true)
                    .build(),
            ],
        );
        info!("never called");
    }),
);

However, channel2 is never created (dynamic_name would change depending on what I receive in service). The thread seems to be blocked here. Can I create a channel from within a closure like this?

Creating the two channels like this works, but I miss the information to properly name channel2:

let channel = webrtcbin.lock().unwrap().emit_by_name::<WebRTCDataChannel>(
    "create-data-channel",
    &[
        &"service",
        &gst::Structure::builder("config")
            .field("ordered", true)
            .build(),
    ],
);

let channel2 = webrtcbin.lock().unwrap().emit_by_name::<WebRTCDataChannel>(
    "create-data-channel",
    &[
        &"dynamic_name",
        &gst::Structure::builder("config")
            .field("ordered", true)
            .build(),
    ],
);

Thanks for your help!

It will probably work if you create the second data channel from a helper thread.

I assume webrtcbin holds a mutex while emitting the signal, and that same mutex is also used for creating data channels. That’s not ideal and should ideally be fixed. Can you create an issue about that in gitlab?

Thanks for the quick answer.

Spawning a thread works indeed.

let channel = webrtcbin.lock().unwrap().emit_by_name::<WebRTCDataChannel>(
    "create-data-channel",
    &[
        &"service",
        &gst::Structure::builder("config")
            .field("ordered", true)
            .build(),
    ],
);

let (tx, rx) = std::sync::mpsc::channel();
thread::spawn(move || {
    let id = rx.recv().unwrap();

    let channel2 = webrtcbin.lock().unwrap().emit_by_name::<WebRTCDataChannel>(
        "create-data-channel",
        &[
            &format!("dynamic_name_{}", id),
            &gst::Structure::builder("config")
                .field("ordered", true)
                .build(),
        ],
    );

    debug!("exit create channel2");
});

channel.connect_closure(
    "on-message-data",
    false,
    glib::closure!(move |_channel: &WebRTCDataChannel, msg: &glib::Bytes| {
        // Some processing to get id from msg
        let _ = tx.send(id);
    }),
);

Here’s the issue: emit_by_name::<WebRTCDataChannel> hangs from whithin a channel.connect_closure("on-message-data", ..) (#535) · Issues · GStreamer / gstreamer-rs · GitLab

Hey @slomo ,

Quick additional question about the channels. I can subscribe to signals in three different ways:

channel.connect_closure(
    "on-open",
    false,
    glib::closure!(|_channel: &WebRTCDataChannel| {
        info!("Data channel opened");
    }),
);

channel.connect("on-open", false, |args| {
    info!("Data channel opened");
    None
});

channel.connect_on_open(|_| {
    info!("Data channel opened");
});

Are they all equivalent?

Secondly, when I do let channel = webrtcbin.emit_by_name::<WebRTCDataChannel>("create-data-channel", ..) before channel.connect("on-open" ...), the signal is not always triggered. I guess it happens just before I connect. Is it safe to consider that the channel is always created and can be used right away?

Thanks

More or less, yes. The main difference is that the first one checks the signal handler parameter types at connection time, the second once you actually make use of the args array, and the third statically enforces them so your code doesn’t compile if you get the parameter types wrong.

That looks like a race condition in the design. After you created the data channel, it is going to be opened in the background so it might already be opened when you connect to the on-open signal. That seems worth another issue.

1 Like