Do not create receiver for ealy media in offerer

For offerer, if the remote side sends early media
before the remote description (answer) is received,
the undeclared SSRC processor can create a receiver
and that receiver could be left dangling as
transceiver `mid` is not updated from remote
description answer.

Still leaving the simulcast probe path and only
avoiding creating a receiver for non-simulcast path.

Add a flag `handleUndeclaredSSRCWithoutAnswer` to control handling
of early media without SDP answer for non-simulcast tracks.
The default behaviour is to not process early media without SDP answer.
This commit is contained in:
boks1971
2025-08-21 14:19:06 +05:30
committed by Raja Subramanian
parent 29e1e00639
commit 8efd17e592
3 changed files with 22 additions and 4 deletions

View File

@@ -231,6 +231,10 @@ var (
errPeerConnSetIdentityProviderNotImplemented = errors.New("TODO SetIdentityProvider")
errPeerConnWriteRTCPOpenWriteStream = errors.New("WriteRTCP failed to open WriteStream")
errPeerConnTranscieverMidNil = errors.New("cannot find transceiver with mid")
errPeerConnEarlyMediaWithoutAnswer = errors.New(
"cannot process early media without SDP answer," +
"use SettingEngine.SetHandleUndeclaredSSRCWithoutAnswer(true) to process without answer",
)
errRTPReceiverDTLSTransportNil = errors.New("DTLSTransport must not be nil")
errRTPReceiverReceiveAlreadyCalled = errors.New("Receive has already been called")

View File

@@ -1719,10 +1719,12 @@ func (pc *PeerConnection) handleIncomingSSRC(rtpStream io.Reader, ssrc SSRC) err
// (urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id and urn:ietf:params:rtp-hdrext:sdes:mid)
// and even if the RTP stream contains an incorrect MID or RID.
// while this can be incorrect, this is done to maintain compatibility with older behavior.
if len(remoteDescription.parsed.MediaDescriptions) == 1 {
mediaSection := remoteDescription.parsed.MediaDescriptions[0]
if handled, err := pc.handleUndeclaredSSRC(ssrc, mediaSection); handled || err != nil {
return err
if remoteDescription.Type != SDPTypeAnswer || pc.api.settingEngine.handleUndeclaredSSRCWithoutAnswer {
if len(remoteDescription.parsed.MediaDescriptions) == 1 {
mediaSection := remoteDescription.parsed.MediaDescriptions[0]
if handled, err := pc.handleUndeclaredSSRC(ssrc, mediaSection); handled || err != nil {
return err
}
}
}
@@ -1748,6 +1750,11 @@ func (pc *PeerConnection) handleIncomingSSRC(rtpStream io.Reader, ssrc SSRC) err
RTPHeaderExtensionCapability{sdp.SDESMidURI},
)
if !audioSupported && !videoSupported {
if remoteDescription.Type == SDPTypeAnswer && !pc.api.settingEngine.handleUndeclaredSSRCWithoutAnswer {
// if we are offerer, wait for answer with media setion to process this SSRC
return errPeerConnEarlyMediaWithoutAnswer
}
// try to find media section by payload type as a last resort for legacy clients.
mediaSection, ok := pc.findMediaSectionByPayloadType(payloadType, remoteDescription)
if ok {

View File

@@ -109,6 +109,7 @@ type SettingEngine struct {
fireOnTrackBeforeFirstRTP bool
disableCloseByDTLS bool
dataChannelBlockWrite bool
handleUndeclaredSSRCWithoutAnswer bool
}
func (e *SettingEngine) getSCTPMaxMessageSize() uint32 {
@@ -570,3 +571,9 @@ func (e *SettingEngine) SetFireOnTrackBeforeFirstRTP(fireOnTrackBeforeFirstRTP b
func (e *SettingEngine) DisableCloseByDTLS(isEnabled bool) {
e.disableCloseByDTLS = isEnabled
}
// SetHandleUndeclaredSSRCWithoutAnswer controls if an SDP answer is required for
// processing early media of non-simulcast tracks.
func (e *SettingEngine) SetHandleUndeclaredSSRCWithoutAnswer(handleUndeclaredSSRCWithoutAnswer bool) {
e.handleUndeclaredSSRCWithoutAnswer = handleUndeclaredSSRCWithoutAnswer
}