Skip to content

feat: add the missing peer connection signaling and control calls - #334

Merged
devopvoid merged 2 commits into
mainfrom
feat/peer-connection-api-gaps
Oct 9, 2026
Merged

devopvoid merged 2 commits into
mainfrom
feat/peer-connection-api-gaps

Conversation

@devopvoid

Copy link
Copy Markdown
Owner

Closes more of the gap with the Android SDK's PeerConnection, and fixes two bugs found along the way.

Fixes

  • Data channel priority was ignored. RTCDataChannelInit.priority was looked up natively but never copied, so every channel was created, and announced to the remote peer, as low priority. It is now applied, and RTCDataChannel.getPriority() reads it back (a priority the remote peer announces between two levels rounds up to the next). WebRTC only uses it to share the SCTP transport when the WebRTC-DataChannelMessageInterleaving field trial is on for both peers; the javadoc and guide say so.
  • A malformed certificate aborted the JVM. A certificate in RTCConfiguration.certificates that did not parse hit an RTC_CHECK. createPeerConnection and setConfiguration now throw a RuntimeException (a NullPointerException for a null entry), and catch conversion errors instead of letting a C++ exception leave the JNI call.

New API

RTCPeerConnection:

  • addIceCandidate(candidate, AddIceCandidateObserver): queued behind pending offer/answer operations, as the standard requires, and reports whether the candidate was accepted. The existing addIceCandidate(candidate) fails silently.
  • removeIceCandidate(candidate): returns whether the candidate was found and removed.
  • setLocalDescription(observer): creates the offer or answer itself and applies it, as perfect negotiation needs.
  • setBitrate(min, current, max): limits the connection's send bandwidth, in bps; null leaves a limit unchanged.
  • setAudioPlayout(boolean), setAudioRecording(boolean): act on the factory's audio device module, so they apply to all of its connections. Tracks fed by a CustomAudioSource are not affected by recording being off.

PeerConnectionObserver:

  • onIceCandidateRemoved(RTCIceCandidate): a local candidate went away.
  • onSelectedCandidatePairChanged(RTCCandidatePairChangeEvent): both candidates of the selected pair, the reason for the switch, the last time data was received and the estimated disconnection time. Its default implementation passes the remote address, port and type on to the existing three-argument callback, so observers that override that one keep working.

Not added: getCertificate(), since getConfiguration().certificates already returns the configured certificate, which is all Android's version returns.

Exception handling

The new callbacks hand a Java exception thrown by the application to the thread's uncaught exception handler, instead of throwing it as a C++ exception through WebRTC's frames (which leaks WebRTC's locks). ReportUncaughtException moved from the data channel send observer to WebRTCUtils to be shared. The older PeerConnectionObserver callbacks still use ExceptionCheck and are left for a separate change.

Docs

  • get-started.md: implicit setLocalDescription, addIceCandidate with an observer, candidate removal and the pair change event.
  • peer-connection-config.md: a section on setBitrate and the audio switches, and how certificates are handled.
  • data-channels.md: what priority does.
  • directionality.md: the sample passed a null observer to setLocalDescription, which throws; it now uses the implicit form.

Tests

  • RTCPeerConnectionSignalingTests (new, two peers): implicit offer and answer, candidates added with an observer and then removed, a candidate for an unknown MID rejected, the full pair change event, the older pair callback still firing, and a remote channel getting the announced priority.
  • RTCPeerConnectionTests: setBitrate validation, audio switches, addIceCandidate without a remote description, null arguments, implicit offer on one peer.
  • RTCDataChannelTests.priority, RTCConfigurationFieldsTests.malformedCertificateThrows and setConfigurationRejectsMalformedCertificate.

The full webrtc suite passes on Windows (249 run, 0 failures, 2 skipped), and the affected classes pass under -Pjni-check with no JNI errors. onIceCandidateRemoved has no test, since a local candidate is only removed on a network change.

RTCDataChannelInit.priority was looked up natively but never copied
into the native init, so every data channel was created with low
priority and announced as such to the remote peer. Copy it, leaving it
unset when null, and add RTCDataChannel.getPriority(), which rounds a
priority the remote peer announced between two levels up to the next.

A certificate in RTCConfiguration.certificates that did not parse hit
an RTC_CHECK and aborted the JVM. Throw a RuntimeException instead, and
a NullPointerException for a null entry, from both createPeerConnection
and setConfiguration, which now catch conversion errors rather than
letting a C++ exception leave the JNI call.
RTCPeerConnection gains:
- addIceCandidate(candidate, AddIceCandidateObserver), which is queued
  behind pending offer/answer operations and reports whether the
  candidate was accepted.
- removeIceCandidate(candidate), and PeerConnectionObserver gains
  onIceCandidateRemoved for local candidates that go away.
- setLocalDescription(observer), which creates the offer or answer
  itself, as perfect negotiation needs.
- setBitrate(min, current, max) for the connection's send bandwidth.
- setAudioPlayout and setAudioRecording.

PeerConnectionObserver also gains onSelectedCandidatePairChanged with an
RTCCandidatePairChangeEvent carrying both candidates of the pair, the
reason for the switch and the disconnection estimate. Its default passes
the remote address, port and type on to the existing callback, so
observers that override that one keep working.

The new callbacks report a Java exception to the thread's uncaught
exception handler instead of throwing it through WebRTC's frames;
ReportUncaughtException moves to WebRTCUtils to be shared with the data
channel send observer.
@devopvoid
devopvoid added this pull request to stack #336 October 9, 2026 22:10
@devopvoid
devopvoid merged commit edd9cbf into main Oct 9, 2026
24 checks passed
@devopvoid
devopvoid deleted the feat/peer-connection-api-gaps branch October 9, 2026 22:50
SendableMetatype added a commit to EduGeyser/webrtc-java that referenced this pull request Oct 10, 2026
Brings in the work upstream added on October 10: the missing peer
connection signaling and control calls (devopvoid#334), callback exceptions kept
from unwinding through WebRTC (devopvoid#335), first-packet observers, sender and
receiver IDs and track volume (devopvoid#338), video processors for camera and
desktop frames (devopvoid#342), and fixes to audio device changes, camera
hotplug and media stream views (devopvoid#333, devopvoid#337, devopvoid#341, devopvoid#343).

Fork-side adaptations:
- setAudioPlayout and setAudioRecording reach the voice engine, so the
  data channels variant leaves them out, like the other audio natives.
- The new tests that need audio or video carry the media tag.
- The Maven publish action stays deleted.
SendableMetatype added a commit to EduGeyser/webrtc-java that referenced this pull request Oct 10, 2026
Brings in the work upstream added on October 10: the missing peer
connection signaling and control calls (devopvoid#334), callback exceptions kept
from unwinding through WebRTC (devopvoid#335), first-packet observers, sender and
receiver IDs and track volume (devopvoid#338), video processors for camera and
desktop frames (devopvoid#342), and fixes to audio device changes, camera
hotplug and media stream views (devopvoid#333, devopvoid#337, devopvoid#341, devopvoid#343).

Fork-side adaptations:
- setBitrate, setAudioPlayout and setAudioRecording reach the call and
  the voice engine, which a factory without a media engine does not
  create, so the data channels variant leaves them out, like the other
  media natives.
- The new tests that need audio or video carry the media tag.
- The Maven publish action stays deleted.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant