Skip to content

fix: make track sinks safe to change from any thread and callback - #517

Merged
devopvoid merged 3 commits into
mainfrom
fix/track-sinks
Nov 1, 2026
Merged

devopvoid merged 3 commits into
mainfrom
fix/track-sinks

Conversation

@devopvoid

Copy link
Copy Markdown
Owner
  • Sinks of AudioTrack and VideoTrack are registered atomically: two threads adding the same sink make one native registration, and dispose() waits for native calls still running.
  • A sink may add or remove sinks, itself included, from within its callback. This used to block for good, since WebRTC holds the lock that adding and removing take while it delivers media. The change takes effect at once on the Java side and is made natively right after the callback; dispose() there throws an IllegalStateException.
  • addSink(sink, VideoFrameAccess.SHARED) hands a sink the source's pixels read-only instead of a copy per sink; COPY stays the default.

AudioTrack and VideoTrack kept their sinks in unguarded maps, so two threads adding the same sink made two native registrations, and an add could overlap dispose(). A shared SinkRegistry now guards the map with a lock that is never held across a native call, and dispose() waits for the native calls still running.
A sink that removed itself in onVideoFrame() blocked for good: WebRTC holds the broadcaster's lock while it delivers a frame, and RemoveSink() takes it again on the worker thread the caller waits for. The same held for adding a sink, for audio sinks of a remote track or a CustomAudioSource, and for voice activity listeners. A change made within a callback now takes effect for the Java side at once and is made natively on another thread after the callback returned; dispose() there fails with an IllegalStateException.
VideoTrackSink copied every frame for each sink, so four sinks of a 1080p30 track copied about 356 MiB a second. addSink(sink, VideoFrameAccess.SHARED) hands a sink the pixels the source handed out, without a copy, in read-only byte buffers; frame.copy() gives a writable one. COPY stays the default and keeps sinks from seeing each other's writes.
@devopvoid
devopvoid merged commit 9701913 into main Nov 1, 2026
25 checks passed
@devopvoid
devopvoid deleted the fix/track-sinks branch November 1, 2026 22:20
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