diff --git a/errors.go b/errors.go index fc5ea2cd..e864d68a 100644 --- a/errors.go +++ b/errors.go @@ -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") diff --git a/peerconnection.go b/peerconnection.go index 61264459..b6c1607d 100644 --- a/peerconnection.go +++ b/peerconnection.go @@ -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 { diff --git a/settingengine.go b/settingengine.go index 0291073f..96b85177 100644 --- a/settingengine.go +++ b/settingengine.go @@ -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 +}