Livekitwebrtcsrc pad-added random untriggered

Hello everyone,

I’m randomly experiencing untriggered pad-added with livekitwebrtcsrc (GStreamer/Node.js).

For example: when two participants with camera and microphone join the LiveKit room, the “pad-added” should trigger four times (once per audio/video stream per participant), but it randomly misses some streams.

No obvious errors are logged, and credentials/room configuration is valid.

Min code snippet:

import * as gi from 'node-gtk';

(async () => {
  const roomName = 'room-name';
  const livekitUrl = 'livekit-url';
  const livekitApiKey = 'livekit-api-key';
  const livekitApiSecret = 'livekit-api-secret';

  const Gst = gi.require('Gst', '1.0');
  const GObject = gi.require('GObject', '2.0');
  gi.startLoop();
  Gst.init(null);

  const gString = (value) => {
    const gValue = new GObject.Value();
    gValue.init(GObject.TYPE_STRING);
    gValue.setString(value);
    return gValue;
  };

  const gBoolean = (value) => {
    const gValue = new GObject.Value();
    gValue.init(GObject.TYPE_BOOLEAN);
    gValue.setBoolean(value);
    return gValue;
  };

  const pipeline = Gst.Pipeline.new(`PI_${roomName}`);

  const webrtcSrc = Gst.ElementFactory.make('livekitwebrtcsrc');
  webrtcSrc.setProperty('signaller::ws-url', gString(livekitUrl));
  webrtcSrc.setProperty('signaller::api-key', gString(livekitApiKey));
  webrtcSrc.setProperty('signaller::secret-key', gString(livekitApiSecret));
  webrtcSrc.setProperty('signaller::room-name', gString(roomName));
  webrtcSrc.setProperty('signaller::identity', gString(`GS_${roomName}`));

  pipeline.add(webrtcSrc);

  /** @see {@link https://gstreamer.freedesktop.org/documentation/rswebrtc/livekitwebrtcsrc.html#pad-templates} */
  webrtcSrc.connect('pad-added', (pad) => {
    console.log('🎤📷 New pad added:', pad.getName());

    if (pad.getName().startsWith('video_')) {
      console.log('🔗 Linking video pad');
      const videoQueue = Gst.ElementFactory.make('queue');
      const videoConvert = Gst.ElementFactory.make('videoconvert');
      const videoSink = Gst.ElementFactory.make('fakevideosink');
      pipeline.add(videoQueue);
      pipeline.add(videoConvert);
      pipeline.add(videoSink);
      videoQueue.link(videoConvert);
      videoConvert.link(videoSink);
      pad.link(videoQueue.getStaticPad('sink'));
    } else if (pad.getName().startsWith('audio_')) {
      console.log('🔗 Linking audio pad');
      const audioQueue = Gst.ElementFactory.make('queue');
      const audioConvert = Gst.ElementFactory.make('audioconvert');
      const audioSink = Gst.ElementFactory.make('fakeaudiosink');
      audioSink.setProperty('async', gBoolean(false));
      pipeline.add(audioQueue);
      pipeline.add(audioConvert);
      pipeline.add(audioSink);
      audioQueue.link(audioConvert);
      audioConvert.link(audioSink);
      pad.link(audioQueue.getStaticPad('sink'));
    }
  });

  console.log('🚀 Setting pipeline state to PLAYING');
  pipeline.setState(Gst.State.PLAYING);

  await new Promise((resolve) => setTimeout(resolve, 20000));

  console.log('🛑 Setting pipeline state to NULL');
  pipeline.setState(Gst.State.NULL);
})();

2 participant meeting, the log is missing audio_unique_0

🚀 Setting pipeline state to PLAYING
0:00:00.590252091 116480 0x721bcc000b70 FIXME                default gstutils.c:4088:gst_element_decorate_stream_id_internal:<nicesrc0> Creating random stream-id, consider implementing a deterministic way of creating a stream-id
0:00:00.590285866 116480 0x721bcc000b70 WARN                GST_PADS gstpad.c:4392:gst_pad_peer_query:<nicesrc0:src> could not send sticky events
0:00:00.590664184 116480     0x420612e0 ERROR              webrtcbin gstwebrtcbin.c:7145:gst_webrtc_bin_get_transceiver:<webrtcbin0> No transceiver for idx 0
0:00:00.590677260 116480     0x420612e0 WARN               webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:854:gstrswebrtc::webrtcsrc::imp::Session::remote_description_set::{{closure}}: Transceiver for idx 0 does not exist, GStreamer <= 1.24, adding it ourself
0:00:00.591017270 116480     0x420612e0 ERROR              webrtcbin gstwebrtcbin.c:7145:gst_webrtc_bin_get_transceiver:<webrtcbin0> No transceiver for idx 1
0:00:00.591024959 116480     0x420612e0 WARN               webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:854:gstrswebrtc::webrtcsrc::imp::Session::remote_description_set::{{closure}}: Transceiver for idx 1 does not exist, GStreamer <= 1.24, adding it ourself
0:00:00.591089334 116480     0x420612e0 ERROR              webrtcbin gstwebrtcbin.c:7145:gst_webrtc_bin_get_transceiver:<webrtcbin0> No transceiver for idx 2
0:00:00.591095358 116480     0x420612e0 WARN               webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:854:gstrswebrtc::webrtcsrc::imp::Session::remote_description_set::{{closure}}: Transceiver for idx 2 does not exist, GStreamer <= 1.24, adding it ourself
0:00:00.591355090 116480     0x420612e0 ERROR              webrtcbin gstwebrtcbin.c:7145:gst_webrtc_bin_get_transceiver:<webrtcbin0> No transceiver for idx 3
0:00:00.591362273 116480     0x420612e0 WARN               webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:854:gstrswebrtc::webrtcsrc::imp::Session::remote_description_set::{{closure}}: Transceiver for idx 3 does not exist, GStreamer <= 1.24, adding it ourself
0:00:00.802931353 116480 0x721bcc001470 FIXME                default gstutils.c:4088:gst_element_decorate_stream_id_internal:<dtlsenc0> Creating random stream-id, consider implementing a deterministic way of creating a stream-id
0:00:01.054735544 116480 0x721bcc001b30 FIXME                default gstutils.c:4088:gst_element_decorate_stream_id_internal:<appsrc0> Creating random stream-id, consider implementing a deterministic way of creating a stream-id
0:00:01.055531250 116480 0x721bcc001d70 FIXME               basesink gstbasesink.c:3399:gst_base_sink_default_event:<appsink0> stream-start event without group-id. Consider implementing group-id handling in the upstream elements
0:00:01.056292386 116480 0x721bcc001fb0 FIXME                default gstutils.c:4088:gst_element_decorate_stream_id_internal:<appsrc1> Creating random stream-id, consider implementing a deterministic way of creating a stream-id
0:00:01.056829293 116480 0x721bcc0021f0 FIXME               basesink gstbasesink.c:3399:gst_base_sink_default_event:<appsink1> stream-start event without group-id. Consider implementing group-id handling in the upstream elements
0:00:01.166966280 116480 0x721bcc0018f0 WARN               webrtcbin gstwebrtcbin.c:5096:try_match_transceiver_with_fec_decoder:<webrtctransceiver1> failed to match fec decoder with transceiver
0:00:01.167012581 116480 0x721bcc0018f0 WARN               webrtcbin gstwebrtcbin.c:5096:try_match_transceiver_with_fec_decoder:<webrtctransceiver3> failed to match fec decoder with transceiver
🎤📷 New pad added: audio_unique_1
🔗 Linking audio pad
0:00:01.176902155 116480 0x721bcc0018f0 FIXME             decodebin3 gstdecodebin3.c:1690:update_requested_selection:<decodebin3-0> Implement EXPOSE_ALL_MODE
0:00:01.176933838 116480 0x721bcc0018f0 FIXME             decodebin3 gstdecodebin3-parse.c:454:unblock_pending_input:<decodebin3-0> Re-use existing input streams if/when possible
0:00:01.177194303 116480 0x721bcc002670 FIXME             decodebin3 gstdecodebin3.c:2273:get_output_for_slot:<decodebin3-0> emit autoplug-continue
0:00:01.177203374 116480 0x721bcc002670 FIXME             decodebin3 gstdecodebin3.c:2276:get_output_for_slot:<decodebin3-0> Handle EXPOSE_ALL_MODE
0:00:01.178989720 116480 0x721bcc002670 WARN                GST_PADS gstpad.c:4392:gst_pad_peer_query:<livekitwebrtcsrc0:audio_unique_1> could not send sticky events
0:00:01.208328151 116480 0x721bcc002430 WARN               webrtcbin gstwebrtcbin.c:5096:try_match_transceiver_with_fec_decoder:<webrtctransceiver0> failed to match fec decoder with transceiver
0:00:01.208350637 116480 0x721bcc002430 FIXME              webrtcbin gstwebrtcbin.c:5086:try_match_transceiver_with_fec_decoder:<webrtctransceiver0> cannot
0:00:01.208359956 116480 0x721bcc002430 WARN               webrtcbin gstwebrtcbin.c:5096:try_match_transceiver_with_fec_decoder:<webrtctransceiver1> failed to match fec decoder with transceiver
0:00:01.208366964 116480 0x721bcc002430 WARN               webrtcbin gstwebrtcbin.c:5096:try_match_transceiver_with_fec_decoder:<webrtctransceiver2> failed to match fec decoder with transceiver
0:00:01.208372742 116480 0x721bcc002430 FIXME              webrtcbin gstwebrtcbin.c:5086:try_match_transceiver_with_fec_decoder:<webrtctransceiver2> cannot
0:00:01.208379634 116480 0x721bcc002430 WARN               webrtcbin gstwebrtcbin.c:5096:try_match_transceiver_with_fec_decoder:<webrtctransceiver3> failed to match fec decoder with transceiver
🎤📷 New pad added: video_unique_0
🔗 Linking video pad
0:00:01.221207484 116480 0x721bcc002430 FIXME             decodebin3 gstdecodebin3.c:1690:update_requested_selection:<decodebin3-1> Implement EXPOSE_ALL_MODE
0:00:01.221247348 116480 0x721bcc002430 FIXME             decodebin3 gstdecodebin3-parse.c:454:unblock_pending_input:<decodebin3-1> Re-use existing input streams if/when possible
0:00:01.221563949 116480 0x721bcc0028b0 FIXME             decodebin3 gstdecodebin3.c:2273:get_output_for_slot:<decodebin3-1> emit autoplug-continue
0:00:01.221576491 116480 0x721bcc0028b0 FIXME             decodebin3 gstdecodebin3.c:2276:get_output_for_slot:<decodebin3-1> Handle EXPOSE_ALL_MODE
0:00:01.230326108 116480 0x721bcc0028b0 WARN               vadisplay gstvadisplay.c:401:gst_va_display_initialize:<vadisplaydrm1> vaInitialize: unknown libva error
0:00:01.292402194 116480 0x721bcc0028b0 WARN                GST_PADS gstpad.c:4392:gst_pad_peer_query:<livekitwebrtcsrc0:video_unique_0> could not send sticky events
0:00:01.328293490 116480 0x721bcc001230 WARN               webrtcbin gstwebrtcbin.c:5096:try_match_transceiver_with_fec_decoder:<webrtctransceiver0> failed to match fec decoder with transceiver
0:00:01.328310436 116480 0x721bcc001230 WARN               webrtcbin gstwebrtcbin.c:5096:try_match_transceiver_with_fec_decoder:<webrtctransceiver0> failed to match fec decoder with transceiver
0:00:01.328315711 116480 0x721bcc001230 FIXME              webrtcbin gstwebrtcbin.c:5086:try_match_transceiver_with_fec_decoder:<webrtctransceiver0> cannot
0:00:01.328324388 116480 0x721bcc001230 FIXME              webrtcbin gstwebrtcbin.c:5086:try_match_transceiver_with_fec_decoder:<webrtctransceiver1> cannot
0:00:01.328328871 116480 0x721bcc001230 FIXME              webrtcbin gstwebrtcbin.c:5086:try_match_transceiver_with_fec_decoder:<webrtctransceiver1> cannot
0:00:01.328332824 116480 0x721bcc001230 WARN               webrtcbin gstwebrtcbin.c:5096:try_match_transceiver_with_fec_decoder:<webrtctransceiver1> failed to match fec decoder with transceiver
0:00:01.328342928 116480 0x721bcc001230 WARN               webrtcbin gstwebrtcbin.c:5096:try_match_transceiver_with_fec_decoder:<webrtctransceiver2> failed to match fec decoder with transceiver
0:00:01.328347595 116480 0x721bcc001230 WARN               webrtcbin gstwebrtcbin.c:5096:try_match_transceiver_with_fec_decoder:<webrtctransceiver2> failed to match fec decoder with transceiver
0:00:01.328351478 116480 0x721bcc001230 FIXME              webrtcbin gstwebrtcbin.c:5086:try_match_transceiver_with_fec_decoder:<webrtctransceiver2> cannot
0:00:01.328358108 116480 0x721bcc001230 FIXME              webrtcbin gstwebrtcbin.c:5086:try_match_transceiver_with_fec_decoder:<webrtctransceiver3> cannot
0:00:01.328362428 116480 0x721bcc001230 FIXME              webrtcbin gstwebrtcbin.c:5086:try_match_transceiver_with_fec_decoder:<webrtctransceiver3> cannot
0:00:01.328366202 116480 0x721bcc001230 WARN               webrtcbin gstwebrtcbin.c:5096:try_match_transceiver_with_fec_decoder:<webrtctransceiver3> failed to match fec decoder with transceiver
🎤📷 New pad added: video_unique_1
🔗 Linking video pad
0:00:01.336580905 116480 0x721bcc001230 FIXME             decodebin3 gstdecodebin3.c:1690:update_requested_selection:<decodebin3-2> Implement EXPOSE_ALL_MODE
0:00:01.336608871 116480 0x721bcc001230 FIXME             decodebin3 gstdecodebin3-parse.c:454:unblock_pending_input:<decodebin3-2> Re-use existing input streams if/when possible
0:00:01.336898114 116480 0x721bcc002af0 FIXME             decodebin3 gstdecodebin3.c:2273:get_output_for_slot:<decodebin3-2> emit autoplug-continue
0:00:01.336911329 116480 0x721bcc002af0 FIXME             decodebin3 gstdecodebin3.c:2276:get_output_for_slot:<decodebin3-2> Handle EXPOSE_ALL_MODE
0:00:01.340658929 116480 0x721bcc002af0 WARN                GST_PADS gstpad.c:4392:gst_pad_peer_query:<livekitwebrtcsrc0:video_unique_1> could not send sticky events
0:00:06.242689174 116480 0x721bd00629d0 WARN              rtpsession rtpsession.c:4067:session_nack: Removing 1 expired NACKS
0:00:06.242784468 116480 0x721bd00629d0 WARN              rtpsession rtpsession.c:4067:session_nack: Removing 1 expired NACKS
0:00:06.242810337 116480 0x721bd00629d0 WARN              rtpsession rtpsession.c:4067:session_nack: Removing 1 expired NACKS
🛑 Setting pipeline state to NULL
0:00:20.047714793 116480 0x721b4810a230 WARN               webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:1483:gstrswebrtc::webrtcsrc::imp::BaseWebRTCSrc::end_session:<livekitwebrtcsrc0> failed to send EOS on audio_unique_1
0:00:20.047770249 116480 0x721b4810a230 WARN               webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:1483:gstrswebrtc::webrtcsrc::imp::BaseWebRTCSrc::end_session:<livekitwebrtcsrc0> failed to send EOS on video_unique_0
0:00:20.047797181 116480 0x721b4810a230 WARN               webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:1483:gstrswebrtc::webrtcsrc::imp::BaseWebRTCSrc::end_session:<livekitwebrtcsrc0> failed to send EOS on video_unique_1
0:00:20.100268727 116480 0x721b4810a230 WARN               webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:1362:gstrswebrtc::webrtcsrc::imp::BaseWebRTCSrc::start_session::{{closure}}:<livekitwebrtcsrc0> session "unique" not found
0:00:20.100291422 116480 0x721b4810a230 WARN               webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:1362:gstrswebrtc::webrtcsrc::imp::BaseWebRTCSrc::start_session::{{closure}}:<livekitwebrtcsrc0> session "unique" not found
0:00:20.100300224 116480 0x721b4810a230 WARN               webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:1362:gstrswebrtc::webrtcsrc::imp::BaseWebRTCSrc::start_session::{{closure}}:<livekitwebrtcsrc0> session "unique" not found
0:00:20.100678706 116480 0x721b4810a230 ERROR              webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:1414:gstrswebrtc::webrtcsrc::imp::BaseWebRTCSrc::start_session::{{closure}}:<livekitwebrtcsrc0> session "unique" not found
0:00:20.100696197 116480 0x721b4810a230 ERROR              webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:1414:gstrswebrtc::webrtcsrc::imp::BaseWebRTCSrc::start_session::{{closure}}:<livekitwebrtcsrc0> session "unique" not found
0:00:20.100705429 116480 0x721b4810a230 ERROR              webrtcsrc net/webrtc/src/webrtcsrc/imp.rs:1414:gstrswebrtc::webrtcsrc::imp::BaseWebRTCSrc::start_session::{{closure}}:<livekitwebrtcsrc0> session "unique" not found

What are causes for random pad-added not being triggered?

Any insights or suggestions would be greatly appreciated! Thank you in advance. :pray:

When I was trying to solve the blocking problem, I found that also solve the pad-added random untriggered problem.

Not sure if there are any side effects but it works fine for me

import { Epoll } from 'epoll';

......

const pollfd = bus.getPollfd();
const msg = await new Promise<Gst.Message>((resolve) => {
  const poller = new Epoll((err, fd, events) => {
    let msg: Gst.Message;
    while ((msg = bus.pop())) {
      if (
        msg.type === Gst.MessageType.ERROR ||
        msg.type === Gst.MessageType.EOS
      ) {
        poller.remove(fd);
        resolve(msg);
      }
    }
  });
  poller.add(pollfd.fd, Epoll.EPOLLIN);
});

if (msg.type === Gst.MessageType.ERROR) {
  const [err, debugInfo] = msg.parseError();
  process.stderr.write(`Error: ${err.message}\n`);
  process.stderr.write(`Debug information: ${debugInfo}\n`);
}