Repository navigation
Add support for flutter_webrtc plugin (GLib main context incompatibility) #509
Description
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:
- Initialization sequences to hang or fail silently
- Native resources to remain uninitialized
- 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
-
flutter-pi side: Add GLib main context integration alongside sd-event, or provide an API for plugins to schedule work on the main thread
-
flutter_webrtc side: Create an alternative
task_runnerimplementation using sd-event for flutter-pi compatibility -
Hybrid approach: flutter-pi could expose a platform-agnostic callback mechanism that plugins can use for thread marshalling
Related Issues/References
- flutter-elinux-plugins#7 - WebRTC plugin support - Documents the thread marshalling fix needed for embedded Linux
- webrtc-sdk/libwebrtc - The C++ wrapper used by flutter_webrtc for desktop/Linux/embedded
Activity
ardera commentedon Jan 26, 2026
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
iagormoraes commentedon Jan 27, 2026
Certainly, I'd be glad to help with that!
ardera commentedon Jan 31, 2026
nice! current development version is in the
devbranch, 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