From 70324aad28f42ed0f260a8e446c85eade65d0f15 Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Wed, 6 May 2026 15:55:42 -0400 Subject: [PATCH] feat(webrtc): add Connected() channel to Peer for ICE establishment timing --- core/webrtc/peer.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/core/webrtc/peer.go b/core/webrtc/peer.go index 7782ed2..a5db256 100644 --- a/core/webrtc/peer.go +++ b/core/webrtc/peer.go @@ -63,8 +63,10 @@ type Peer struct { videoSub chan *rtp.Packet audioSub chan *rtp.Packet - done chan struct{} - once sync.Once + done chan struct{} + once sync.Once + connected chan struct{} + connOnce sync.Once } // CreatePeer builds a PeerConnection, sets the remote offer, generates an @@ -131,9 +133,13 @@ func (f *PeerFactory) CreatePeer(ctx context.Context, src *Source, offer webrtc. source: src, sub: sub, done: make(chan struct{}), + connected: make(chan struct{}), } pc.OnConnectionStateChange(func(st webrtc.PeerConnectionState) { + if st == webrtc.PeerConnectionStateConnected { + p.connOnce.Do(func() { close(p.connected) }) + } if st == webrtc.PeerConnectionStateFailed || st == webrtc.PeerConnectionStateDisconnected || st == webrtc.PeerConnectionStateClosed { @@ -158,6 +164,12 @@ func (p *Peer) ResourceID() string { return p.resourceID } // index cleanup without polling. func (p *Peer) Done() <-chan struct{} { return p.done } +// Connected returns a channel that is closed the first time Pion reports +// PeerConnectionStateConnected. Callers that need to measure ICE +// establishment duration select on Connected() vs Done() from the moment +// the peer is created. +func (p *Peer) Connected() <-chan struct{} { return p.connected } + // Close tears down the peer connection and unsubscribes from each // source. Safe to call multiple times. func (p *Peer) Close() error { @@ -248,9 +260,13 @@ func (f *PeerFactory) CreatePeerFromSources(ctx context.Context, videoSub: videoSub, audioSub: audioSub, done: make(chan struct{}), + connected: make(chan struct{}), } pc.OnConnectionStateChange(func(st webrtc.PeerConnectionState) { + if st == webrtc.PeerConnectionStateConnected { + p.connOnce.Do(func() { close(p.connected) }) + } if st == webrtc.PeerConnectionStateFailed || st == webrtc.PeerConnectionStateDisconnected || st == webrtc.PeerConnectionStateClosed { @@ -263,7 +279,6 @@ func (f *PeerFactory) CreatePeerFromSources(ctx context.Context, return p, nil } - // AddICECandidate forwards a trickle-ICE candidate to the underlying // PeerConnection. Returns the underlying error if the candidate is // malformed or the connection has already been closed.