Skip to content

Add support for flutter_webrtc plugin (GLib main context incompatibility) #509

Open

Description

@iagormoraes

Summary

The flutter_webrtc plugin does not work with flutter-pi due to a main loop incompatibility. The plugin's Linux implementation uses GLib's g_main_context_invoke() for thread marshalling, but flutter-pi uses libsystemd's sd-event loop instead.

Environment

  • Device: Raspberry Pi 5
  • flutter-pi version: latest from source
  • flutter_webrtc version: 0.12.12
  • Flutter SDK: 3.10.7

Error Output

[ERROR:flutter/runtime/dart_vm_initializer.cc(40)] Unhandled Exception: Call initialize before setting the stream
#0      RTCVideoRenderer.srcObject= (package:flutter_webrtc/src/native/rtc_video_renderer_impl.dart:61)
#1      WebRtc._cleanup (package:frame_client/features/agent/providers/webrtc_provider.dart:395)
<asynchronous suspension>

[ERROR:flutter/runtime/dart_vm_initializer.cc(40)] Unhandled Exception: WebSocketChannelException: SocketException: Connection refused (OS Error: Connection refused, errno = 111), address = localhost, port = 37130

[ERROR:flutter/runtime/dart_vm_initializer.cc(40)] Unhandled Exception: WebSocketChannelException: SocketException: Connection refused (OS Error: Connection refused, errno = 111), address = localhost, port = 42876

The random high ports (37130, 42876, etc.) suggest libwebrtc's internal networking fails during ICE candidate gathering because initialization callbacks never complete.

Root Cause Analysis

The flutter_webrtc plugin's Linux implementation relies on GLib for thread marshalling in task_runner_linux.cc:

void TaskRunnerLinux::EnqueueTask(TaskClosure task) {
  // ...
  GMainContext* context = g_main_context_default();
  if (context) {
    g_main_context_invoke(context, /* callback */, this);
  }
}

This expects GLib's GMainLoop to be running and pumping the default main context. However, flutter-pi uses libsystemd's sd-event for its event loop (as mentioned in the README: "libsystemd provides the event loop and dbus support for flutter-pi").

Since g_main_context_invoke() schedules work on a GLib main context that flutter-pi never iterates, the WebRTC callbacks are never executed. This causes:

  1. Initialization sequences to hang or fail silently
  2. Native resources to remain uninitialized
  3. Subsequent operations to fail with confusing errors

Comparison with flutter-elinux

The flutter_webrtc plugin works with flutter-elinux after thread marshalling fixes were implemented (see flutter-elinux-plugins#7). The flutter-elinux project appears to use GLib's main loop, which is why g_main_context_invoke() works there.

Possible Solutions

  1. flutter-pi side: Add GLib main context integration alongside sd-event, or provide an API for plugins to schedule work on the main thread

  2. flutter_webrtc side: Create an alternative task_runner implementation using sd-event for flutter-pi compatibility

  3. Hybrid approach: flutter-pi could expose a platform-agnostic callback mechanism that plugins can use for thread marshalling

Related Issues/References

Activity

  1. ardera commented on Jan 26, 2026

    @ardera
    Owner

    Honestly I think it's probably best to switch to glibs event loop. I heard it's not the fastest, but sd_event is not really mt-safe (authors say it's not mt incompatible, you just have to add your own locks, but that's a lie IMO), and there's some non-trivial code involved to make it work in an mt scenario.

    A real multithreaded event loop like glib would probably be better. Plus, that makes it easier to make other plugins, which rely on glib, compatible with flutter-pi.

    Do you want to prepare a PR? Or just wanted to raise the issue? @iagormoraes

  2. iagormoraes commented on Jan 27, 2026

    @iagormoraes
    Author

    Honestly I think it's probably best to switch to glibs event loop. I heard it's not the fastest, but sd_event is not really mt-safe (authors say it's not mt incompatible, you just have to add your own locks, but that's a lie IMO), and there's some non-trivial code involved to make it work in an mt scenario.

    A real multithreaded event loop like glib would probably be better. Plus, that makes it easier to make other plugins, which rely on glib, compatible with flutter-pi.

    Do you want to prepare a PR? Or just wanted to raise the issue? @iagormoraes

    Certainly, I'd be glad to help with that!

  3. ardera commented on Jan 31, 2026

    @ardera
    Owner

    nice! current development version is in the dev branch, I think I've even switched to my own "evloop" wrapper there; that can either be ripped out entirely or you replace it's current sd-event implementation with a glib-based one.

    There's only really three kinds of events we need, deferred tasks, timers and io (fd). Deferred tasks are used if you just need to run some function on a specific thread, as soon as possible. e.g. the vsync callback is called from flutters UI thread I think, but the response should come from the platform thread. IO are used for listening to e.g. display events from kernel, user input events.

    also feel free to ask if you need any guidance, any information about threading, etc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions