Greetings,
Here is an update about my experiments with WebRTC.
Sadly, under Linux, WebRTC often deadlocks on disconnection. Here is a stack
trace for the deadlock (it is always the same in these cases):
libc.so.6`___lldb_unnamed_symbol3481 + 198
libc.so.6`pthread_cond_wait + 440
libllwebrtc.so`___lldb_unnamed_symbol3282 + 571
libllwebrtc.so`___lldb_unnamed_symbol4589 + 318
libllwebrtc.so`___lldb_unnamed_symbol4590 + 14
libllwebrtc.so`___lldb_unnamed_symbol4588 + 33
libllwebrtc.so`llwebrtc::LLWebRTCPeerConnectionImpl::terminate() + 203
libllwebrtc.so`llwebrtc::LLWebRTCPeerConnectionImpl::shutdownConnection() + 10
cool_vl_viewer-bin`LLVoiceConnection::breakVoiceConnectionCoro(this=0x0000555567e15930) at llvoicewebrtc.cpp:625:25
cool_vl_viewer-bin`boost::fibers::worker_context<LLCoros::launch(.../...)::$_0>::run_(boost::context::fiber&&) [inlined] boost::function0<void>::operator()(this=0x000055554341fa48) const at function_template.hpp:771:14
cool_vl_viewer-bin`boost::fibers::worker_context<LLCoros::launch(.../...)::$_0>::run_(boost::context::fiber&&) [inlined] LLCoros::toplevel(this=<unavailable>, name="breakVoiceConnectionCoro", callable=LLCoros::callable_t @ 0x000055554341fa48) at llcoros.cpp:168:3
Apparently, pthread_cond_wait() is waiting forever for a condition that is
never met...
This of course freezes the viewer ! :-(
Given the above stack trace, I came to suspect a weird interaction between
pthread mutexes and boost::fiber coroutines (non-coroutine aware mutex ?)...
I thought about possible workarounds, such as breaking down
breakVoiceConnectionCoro() into several steps: the data and audio interfaces
tear down would be posted outside of the coroutine (just before launching it),
the HTTP post would be kept in the coroutine, but on return, it would post the
shutdownConnection() call to the main loop work queue, so that the deadlock-prone
shutdownConnection() function is not called from within a coroutine, and finally
an additional state would be added to the state machine to account for the
splitting of the disconnection process.
However, I told to myself that this won't cover other possible deadlock and
crash issues coming from that webrtc-sdk library (which looks to me as
especially fragile and badly guarded against unexpected connection states)...
So, I finally opted for a more radical approach: since I cannot decently
trust WebRTC to not crash/hang and ruin the viewer stability, and since
it uses its own (non-standard) threading model, let's isolate it entirely
from the viewer binary !
There are two ways to achieve this: create a separate voice client, akin to
what is done with SLVoice(.exe), and launch/communicate with it just what
is done in llviewervivox.cpp... Or, even better: use the viewer plugin
infrastructure to create a webrtc plugin !
I of course opted for the second solution (more elegant, simpler), and I
did implement a webrtc plugin for my viewer; it works like a charm to
isolate the WebRTC calls from the viewer code, and when it crashes or
freezes, all I need is to restart it !
And sadly, the deadlocks indeed still happen within the plugin (meaning the
problem was not related with the coroutines, but likely due to some bug in
the webrtc-sdk library itself). I'm wondering if it won't best to ditch
that buggy webrtc-sdk fork and use Google's webrtc instead (it is working
in CEF, so I would expect it to work too for WebRTC voice)...
Note that the plugin model also got another advantage (beyond viewer
stability): it does not cause any FPS "hiccup" when calling the WebRTC
functions and processing the plugin messaging is extremely lightweight
(just and exchange of an LLSD every now and then, and the read-message-
from-plugin part is even threaded !).
You will find the plugin code in the Cool VL Viewer v1.32.1.0 sources (1)
I just published (this is a new experimental branch of my viewer, for
WebRTC and PBR upcoming updates); you are of course most welcome to reuse
it in your own viewer (and free to re-license it for such a purpose).
Beyond the plugin itself (media_plugins/webrtc/voice_plugin_webrtc.cpp)
which could likely be reused "as is", you would need to backport the few
changes done to LLPluginClassMedia (indra/llplugin/llpluginclassmedia.*)
but I took care to regroup all those changes and comment them, so that
would be an easy task. Then, of course, you would need to backport the
plugin management code I wrote in llvoicewebrtc.*, but here again, that
should prove easy (they are enclosed in #if HB_USE_WEBRTC_PLUGIN/#else/
#endif preprocessor clauses, and thus trivial to understand and follow).
Finally, I am attaching the latest diff between Roxie's llwebrtc and
mine, which is patched to work under Linux (especially to not hang
on llwebrtc::terminate() and to properly list audio devices).
To go further with WebRTC support in my viewer (which still does not
properly work), I am currently faced with the following issues:
- The Linux audio devices are still not properly set by WebRTC (not sure
why: I will likely have to dig into WebRTC sources and instrument them
to find out).
- Under both Linux and Windows, I get a 404 error reply from the server
to the last post of ICE updates (which is the "completed=true" post,
done to the exact same URL as the former ICE candidates updates which
were accepted without error); this causes the voice connection to stop
and be retried (disconnected) after 30s or so...
- The latest Windows build of WebRTC (2) is _again_ a DEBUG build, and
cannot therefore be linked properly with a RELEASE viewer (or plugin).
Regards,
Henri.
(1)
http://sldev.free.fr/sources/
(2)
https://github.com/secondlife/3p-webrtc-build/releases/tag/m114.5735.08.58
See also:
https://github.com/secondlife/viewer/commit/f5c5c5ec9497211010b5a4dd15bea44d4bbc8482#comments