Skip to content
benashbyPublic

About

Minimal reproduction: jsyn WebRTC outbound audio intermittently never starts; Conference.close() intermittently hangs

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

jsyn MRE: WebRTC outbound audio intermittently never starts; Conference.close() intermittently hangs

Minimal reproduction of two issues we hit replacing Kurento with jsyn in a voice-agent backend. There is no application code here: one Micronaut 4.9.0 app, jsyn, and a headless Chromium peer (Playwright) doing real ICE, DTLS-SRTP and Opus.

Both issues are intermittent in this harness. In our production app (same jsyn, same machine) issue 1 happened on nearly every call and issue 2 on every hang-up.

The topology (what our app does)

One conference per call, two participants:

browser --WebRTC/Opus--> "caller" (WebRtcParticipant) --matrix--> "agent" (NativeParticipant) .read()  -> speech-to-text   ✅ always works
TTS -> .write() "agent" (NativeParticipant) --matrix--> "caller" (WebRtcParticipant) --WebRTC--> browser               ❌ often nothing arrives

Matrix: connect("caller","agent") + connect("agent","caller").

Issue 1: jsyn sends no RTP to the WebRTC participant for the whole call

On an affected call:

  • ICE and DTLS-SRTP connect (connectionState=connected), and the browser sends its mic.
  • browser → jsyn works. The native participant's egress ring delivers the browser's fake-mic tone (thousands of non-silent samples).
  • jsyn → browser sends nothing. Chrome's inbound-rtp shows packetsReceived=0 for the entire call.
  • The native ingress ring is being consumed: bytesWritten grows at the real-time rate, ingressRingUtilization stays at one or two frames, and ingressOverruns=0. jsyn takes the audio and it never reaches the wire.

It's all or nothing per call. A good call gets audio within a second (40 to 50 packets by the first sample); a bad call gets zero packets ever. It happens with every native format, whether the matrix is wired before or after ICE connects, and also with a plain WebRTC self-route (connect("caller","caller"), the shape of jsyn's own WebRtcMediaE2eIT.audioFlowsBackToTheBrowser). So this looks like the WebRTC participant's send path intermittently never starting, not something specific to native participants.

Separately, a native → native control (two native participants in one conference) passed on every run we did.

Issue 2: Conference.close() intermittently never returns

After a live WebRTC call, Conference.close() blocks forever in the native call:

at com.synauson.jsyn.internal.NativeBridge.terminateConference(Native Method)
at com.synauson.jsyn.participant.Conference.lambda$new$0(Conference.java:64)
at java.base/jdk.internal.ref.CleanerImpl$PhantomCleanableRef.performCleanup(CleanerImpl.java:176)
at java.base/jdk.internal.ref.PhantomCleanable.clean(PhantomCleanable.java:96)
at com.synauson.jsyn.internal.NativeResource.close(NativeResource.java:56)

In our app it was hit on every disconnect. Because we closed on a Netty event-loop thread, it froze every later WebSocket on that loop. We now close on a separate thread with a timeout, which leaks the thread and the native conference.

The test follows our production teardown: the peer hangs up, the ICE subscription is cancelled, the NativeParticipant is closed while its I/O thread is still running, then conference.close().

Results (2026-09-24)

./gradlew test runs 26 live calls. Every failed call in issue 1 had packetsReceived=0 at the browser while ICE/DTLS were connected.

Test 1.0.0 (release) 1.0.0-SNAPSHOT (natives 20260806.015015-10)
control: native → native 1/1 pass 1/1 pass
issue 1: native → WebRTC, 3 formats × wired before/after connect × 3 10/18 no audio 12/18 no audio
↳ matrix wired before ICE connects (as our app does) 6/9 no audio 6/9 no audio
↳ matrix wired after ICE connects 4/9 no audio 6/9 no audio
issue 1b: WebRTC self-route (echo) 1/3 no audio 2/3 no audio
issue 2: close() > 10 s after a WebRTC call 0/4 hung (did hang in an earlier 1.0.0 run) 1/4 hung

Per format, over both versions (12 calls each): µ-law 8 kHz 6/12 no audio, PCM16 8 kHz 7/12, PCM16 48 kHz 9/12. We don't read that as format-dependent.

Full logs: each failing assertion prints the browser snapshot plus the native ring counters, and a hung close logs its stack.

Environment

  • Windows 10 Pro 22H2 (10.0.19045), x86_64
  • GStreamer 1.26.7 (MSVC x86_64 runtime), on PATH
  • JDK 25.0.2 (Gradle toolchain), Gradle 9.7.1
  • Micronaut 4.9.0 (io.micronaut.application Gradle plugin 4.6.2), matching our production app
  • jsyn + jsyn-natives-windows 1.0.0, and 1.0.0-SNAPSHOT (-PjsynVersion=1.0.0-SNAPSHOT)
  • Chromium 129.0.6668.29 via Playwright 1.47.0 (the version jsyn's own tests use)
  • jsyn config: STUN stun://stun.l.google.com:19302, WebRTC jitter buffer 200 ms, no VAD / Smart-Turn models

We haven't tried Linux: the released jsyn-natives-linux:1.0.0 needs GLIBC_2.43 and won't load on current distros.

Running it

Prerequisites: GStreamer ≥ 1.26 on PATH, and Synauson Nexus credentials.

export NEXUS_USERNAME=... NEXUS_PASSWORD=...
# Windows (Git Bash): export PATH="/c/Program Files/gstreamer/1.0/msvc_x86_64/bin:$PATH"

./gradlew installPlaywrightBrowsers    # one-time: downloads the Chromium Playwright 1.47 drives
./gradlew test                         # against jsyn 1.0.0
./gradlew test -PjsynVersion=1.0.0-SNAPSHOT

The interesting output is the [bug1 ...], [bug1b ...] and [bug2 ...] lines. Every test logs its outcome, pass or fail.

Layout

File What
src/main/java/mre/MediaEngine.java The one JSyn per JVM, configured as in production
src/test/java/mre/JsynWebRtcOutboundTest.java The control and the issue 1 / 1b / 2 tests
src/test/java/mre/BrowserPeer.java Headless Chromium peer (fake mic tone; remote track on an Audio sink so Chrome decodes it, as in jsyn's WebRtcBrowserPeer)
src/test/java/mre/ToneGenerator.java 440 Hz tone in µ-law / PCM16

About

Minimal reproduction: jsyn WebRTC outbound audio intermittently never starts; Conference.close() intermittently hangs

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages