1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

Squash: Fix rtc to rtmp sync timestamp using sender report. #2470

This commit is contained in:
winlin 2021-08-17 07:25:03 +08:00
parent 3d58e98d1c
commit 85620a34f5
309 changed files with 14837 additions and 8525 deletions

View file

@ -259,6 +259,12 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu
* [pascal-ace](https://github.com/pascal-ace)
* [Threadnaught](https://github.com/Threadnaught)
* [Dean Eigenmann](https://github.com/decanus)
* [Cameron Elliott](https://github.com/cameronelliott)
* [Pascal Benoit](https://github.com/pascal-ace)
* [Mats](https://github.com/Mindgamesnl)
* [donotanswer](https://github.com/f-viktor)
* [Reese](https://github.com/figadore)
* [David Zhao](https://github.com/davidzhao)
### License
MIT License - see [LICENSE](LICENSE) for full text

View file

@ -27,7 +27,7 @@ type Configuration struct {
// PeerIdentity sets the target peer identity for the PeerConnection.
// The PeerConnection will not establish a connection to a remote peer
// unless it can be successfully authenticated with the provided name.
PeerIdentity string `json:"peerIdentity"`
PeerIdentity string `json:"peerIdentity,omitempty"`
// Certificates describes a set of certificates that the PeerConnection
// uses to authenticate. Valid values for this parameter are created

View file

@ -6,9 +6,9 @@ require (
github.com/onsi/ginkgo v1.14.2 // indirect
github.com/onsi/gomega v1.10.3 // indirect
github.com/pion/datachannel v1.4.21
github.com/pion/dtls/v2 v2.0.4
github.com/pion/ice/v2 v2.0.14
github.com/pion/interceptor v0.0.9
github.com/pion/dtls/v2 v2.0.8
github.com/pion/ice/v2 v2.0.15
github.com/pion/interceptor v0.0.10
github.com/pion/logging v0.2.2
github.com/pion/randutil v0.1.0
github.com/pion/rtcp v1.2.6
@ -19,5 +19,5 @@ require (
github.com/pion/transport v0.12.2
github.com/sclevine/agouti v3.0.0+incompatible
github.com/stretchr/testify v1.7.0
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
)

View file

@ -16,8 +16,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I=
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@ -38,10 +38,12 @@ github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXm
github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
github.com/pion/dtls/v2 v2.0.4 h1:WuUcqi6oYMu/noNTz92QrF1DaFj4eXbhQ6dzaaAwOiI=
github.com/pion/dtls/v2 v2.0.4/go.mod h1:qAkFscX0ZHoI1E07RfYPoRw3manThveu+mlTDdOxoGI=
github.com/pion/ice/v2 v2.0.14 h1:FxXxauyykf89SWAtkQCfnHkno6G8+bhRkNguSh9zU+4=
github.com/pion/ice/v2 v2.0.14/go.mod h1:wqaUbOq5ObDNU5ox1hRsEst0rWfsKuH1zXjQFEWiZwM=
github.com/pion/interceptor v0.0.9 h1:fk5hTdyLO3KURQsf/+RjMpEm4NE3yeTY9Kh97b5BvwA=
github.com/pion/interceptor v0.0.9/go.mod h1:dHgEP5dtxOTf21MObuBAjJeAayPxLUAZjerGH8Xr07c=
github.com/pion/dtls/v2 v2.0.8 h1:reGe8rNIMfO/UAeFLqO61tl64t154Qfkr4U3Gzu1tsg=
github.com/pion/dtls/v2 v2.0.8/go.mod h1:QuDII+8FVvk9Dp5t5vYIMTo7hh7uBkra+8QIm7QGm10=
github.com/pion/ice/v2 v2.0.15 h1:KZrwa2ciL9od8+TUVJiYTNsCW9J5lktBjGwW1MacEnQ=
github.com/pion/ice/v2 v2.0.15/go.mod h1:ZIiVGevpgAxF/cXiIVmuIUtCb3Xs4gCzCbXB6+nFkSI=
github.com/pion/interceptor v0.0.10 h1:dXFyFWRJFwmzQqyn0U8dUAbOJu+JJnMVAqxmvTu30B4=
github.com/pion/interceptor v0.0.10/go.mod h1:qzeuWuD/ZXvPqOnxNcnhWfkCZ2e1kwwslicyyPnhoK4=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/mdns v0.0.4 h1:O4vvVqr4DGX63vzmO6Fw9vpy3lfztVWHGCQfyw0ZLSY=
@ -64,7 +66,7 @@ github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2U
github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
github.com/pion/transport v0.12.0/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
github.com/pion/transport v0.12.1/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
github.com/pion/transport v0.12.2 h1:WYEjhloRHt1R86LhUKjC5y+P52Y11/QqEUalvtzVoys=
github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA=
@ -88,6 +90,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -98,17 +102,24 @@ golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 h1:3uJsdck53FDIpWwLeAXlia9p4C8j0BO2xZrqzKpL0D8=
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=

View file

@ -7,6 +7,7 @@ import (
"github.com/pion/interceptor"
"github.com/pion/interceptor/pkg/nack"
"github.com/pion/interceptor/pkg/report"
"github.com/pion/rtp"
)
@ -18,6 +19,27 @@ func RegisterDefaultInterceptors(mediaEngine *MediaEngine, interceptorRegistry *
return err
}
if err := ConfigureRTCPReports(interceptorRegistry); err != nil {
return err
}
return nil
}
// ConfigureRTCPReports will setup everything necessary for generating Sender and Receiver Reports
func ConfigureRTCPReports(interceptorRegistry *interceptor.Registry) error {
reciver, err := report.NewReceiverInterceptor()
if err != nil {
return err
}
sender, err := report.NewSenderInterceptor()
if err != nil {
return err
}
interceptorRegistry.Add(reciver)
interceptorRegistry.Add(sender)
return nil
}

View file

@ -16,16 +16,16 @@ import (
const (
// MimeTypeH264 H264 MIME type.
// Note: Matching should be case insensitive.
MimeTypeH264 = "video/h264"
MimeTypeH264 = "video/H264"
// MimeTypeOpus Opus MIME type
// Note: Matching should be case insensitive.
MimeTypeOpus = "audio/opus"
// MimeTypeVP8 VP8 MIME type
// Note: Matching should be case insensitive.
MimeTypeVP8 = "video/vp8"
MimeTypeVP8 = "video/VP8"
// MimeTypeVP9 VP9 MIME type
// Note: Matching should be case insensitive.
MimeTypeVP9 = "video/vp9"
MimeTypeVP9 = "video/VP9"
// MimeTypeG722 G722 MIME type
// Note: Matching should be case insensitive.
MimeTypeG722 = "audio/G722"
@ -86,17 +86,6 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
}
}
// Default Pion Audio Header Extensions
for _, extension := range []string{
"urn:ietf:params:rtp-hdrext:sdes:mid",
"urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id",
"urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id",
} {
if err := m.RegisterHeaderExtension(RTPHeaderExtensionCapability{extension}, RTPCodecTypeAudio); err != nil {
return err
}
}
videoRTCPFeedback := []RTCPFeedback{{"goog-remb", ""}, {"ccm", "fir"}, {"nack", ""}, {"nack", "pli"}}
for _, codec := range []RTPCodecParameters{
{
@ -190,18 +179,17 @@ func (m *MediaEngine) RegisterDefaultCodecs() error {
}
}
// Default Pion Video Header Extensions
for _, extension := range []string{
"urn:ietf:params:rtp-hdrext:sdes:mid",
"urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id",
"urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id",
} {
if err := m.RegisterHeaderExtension(RTPHeaderExtensionCapability{extension}, RTPCodecTypeVideo); err != nil {
return err
return nil
}
// addCodec will append codec if it not exists
func (m *MediaEngine) addCodec(codecs []RTPCodecParameters, codec RTPCodecParameters) []RTPCodecParameters {
for _, c := range codecs {
if c.MimeType == codec.MimeType && c.PayloadType == codec.PayloadType {
return codecs
}
}
return nil
return append(codecs, codec)
}
// RegisterCodec adds codec to the MediaEngine
@ -211,9 +199,9 @@ func (m *MediaEngine) RegisterCodec(codec RTPCodecParameters, typ RTPCodecType)
codec.statsID = fmt.Sprintf("RTPCodec-%d", time.Now().UnixNano())
switch typ {
case RTPCodecTypeAudio:
m.audioCodecs = append(m.audioCodecs, codec)
m.audioCodecs = m.addCodec(m.audioCodecs, codec)
case RTPCodecTypeVideo:
m.videoCodecs = append(m.videoCodecs, codec)
m.videoCodecs = m.addCodec(m.videoCodecs, codec)
default:
return ErrUnknownType
}
@ -293,6 +281,20 @@ func (m *MediaEngine) getHeaderExtensionID(extension RTPHeaderExtensionCapabilit
return
}
// copy copies any user modifiable state of the MediaEngine
// all internal state is reset
func (m *MediaEngine) copy() *MediaEngine {
cloned := &MediaEngine{
videoCodecs: append([]RTPCodecParameters{}, m.videoCodecs...),
audioCodecs: append([]RTPCodecParameters{}, m.audioCodecs...),
headerExtensions: append([]mediaEngineHeaderExtension{}, m.headerExtensions...),
}
if len(m.headerExtensions) > 0 {
cloned.negotiatedHeaderExtensions = map[int]mediaEngineHeaderExtension{}
}
return cloned
}
func (m *MediaEngine) getCodecByPayload(payloadType PayloadType) (RTPCodecParameters, RTPCodecType, error) {
for _, codec := range m.negotiatedVideoCodecs {
if codec.PayloadType == payloadType {
@ -332,37 +334,25 @@ func (m *MediaEngine) collectStats(collector *statsReportCollector) {
}
// Look up a codec and enable if it exists
func (m *MediaEngine) updateCodecParameters(remoteCodec RTPCodecParameters, typ RTPCodecType) error {
func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCodecType) (codecMatchType, error) {
codecs := m.videoCodecs
if typ == RTPCodecTypeAudio {
codecs = m.audioCodecs
}
pushCodec := func(codec RTPCodecParameters) error {
if typ == RTPCodecTypeAudio {
m.negotiatedAudioCodecs = append(m.negotiatedAudioCodecs, codec)
} else if typ == RTPCodecTypeVideo {
m.negotiatedVideoCodecs = append(m.negotiatedVideoCodecs, codec)
}
return nil
}
if strings.HasPrefix(remoteCodec.RTPCodecCapability.SDPFmtpLine, "apt=") {
payloadType, err := strconv.Atoi(strings.TrimPrefix(remoteCodec.RTPCodecCapability.SDPFmtpLine, "apt="))
if err != nil {
return err
return codecMatchNone, err
}
if _, _, err = m.getCodecByPayload(PayloadType(payloadType)); err != nil {
return nil // not an error, we just ignore this codec we don't support
return codecMatchNone, nil // not an error, we just ignore this codec we don't support
}
}
if _, err := codecParametersFuzzySearch(remoteCodec, codecs); err == nil {
return pushCodec(remoteCodec)
}
return nil
_, matchType := codecParametersFuzzySearch(remoteCodec, codecs)
return matchType, nil
}
// Look up a header extension and enable if it exists
@ -391,6 +381,16 @@ func (m *MediaEngine) updateHeaderExtension(id int, extension string, typ RTPCod
return nil
}
func (m *MediaEngine) pushCodecs(codecs []RTPCodecParameters, typ RTPCodecType) {
for _, codec := range codecs {
if typ == RTPCodecTypeAudio {
m.negotiatedAudioCodecs = m.addCodec(m.negotiatedAudioCodecs, codec)
} else if typ == RTPCodecTypeVideo {
m.negotiatedVideoCodecs = m.addCodec(m.negotiatedVideoCodecs, codec)
}
}
}
// Update the MediaEngine from a remote description
func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) error {
for _, media := range desc.MediaDescriptions {
@ -411,10 +411,31 @@ func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) e
return err
}
exactMatches := make([]RTPCodecParameters, 0, len(codecs))
partialMatches := make([]RTPCodecParameters, 0, len(codecs))
for _, codec := range codecs {
if err = m.updateCodecParameters(codec, typ); err != nil {
return err
matchType, mErr := m.matchRemoteCodec(codec, typ)
if mErr != nil {
return mErr
}
if matchType == codecMatchExact {
exactMatches = append(exactMatches, codec)
} else if matchType == codecMatchPartial {
partialMatches = append(partialMatches, codec)
}
}
// use exact matches when they exist, otherwise fall back to partial
switch {
case len(exactMatches) > 0:
m.pushCodecs(exactMatches, typ)
case len(partialMatches) > 0:
m.pushCodecs(partialMatches, typ)
default:
// no match, not negotiated
continue
}
extensions, err := rtpExtensionsFromMediaDescription(media)

View file

@ -6,6 +6,7 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"errors"
"fmt"
"io"
"strconv"
@ -132,7 +133,13 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
log: api.settingEngine.LoggerFactory.NewLogger("pc"),
}
pc.interceptorRTCPWriter = api.interceptor.BindRTCPWriter(interceptor.RTCPWriterFunc(pc.writeRTCP))
if !api.settingEngine.disableMediaEngineCopy {
pc.api = &API{
settingEngine: api.settingEngine,
mediaEngine: api.mediaEngine.copy(),
interceptor: api.interceptor,
}
}
var err error
if err = pc.initConfiguration(configuration); err != nil {
@ -168,6 +175,8 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
}
})
pc.interceptorRTCPWriter = api.interceptor.BindRTCPWriter(interceptor.RTCPWriterFunc(pc.writeRTCP))
return pc, nil
}
@ -576,6 +585,8 @@ func (pc *PeerConnection) hasLocalDescriptionChanged(desc *SessionDescription) b
return false
}
var errExcessiveRetries = errors.New("excessive retries in CreateOffer")
// CreateOffer starts the PeerConnection and generates the localDescription
// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer
func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription, error) { //nolint:gocognit
@ -603,6 +614,7 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription
// audio RTCRtpTransceiver was added to connection, but while performing the in-parallel
// steps to create an offer, a video RTCRtpTransceiver was added, requiring additional
// inspection of video system resources.
count := 0
for {
// We cache current transceivers to ensure they aren't
// mutated during offer generation. We later check if they have
@ -673,6 +685,10 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription
if isPlanB || !pc.hasLocalDescriptionChanged(&offer) {
break
}
count++
if count >= 128 {
return SessionDescription{}, errExcessiveRetries
}
}
pc.lastOffer = offer.SDP
@ -1033,10 +1049,21 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error {
if err != nil {
return err
}
t = pc.newRTPTransceiver(receiver, nil, RTPTransceiverDirectionRecvonly, kind)
localDirection := RTPTransceiverDirectionRecvonly
if direction == RTPTransceiverDirectionRecvonly {
localDirection = RTPTransceiverDirectionSendonly
}
t = pc.newRTPTransceiver(receiver, nil, localDirection, kind)
pc.onNegotiationNeeded()
} else if direction == RTPTransceiverDirectionRecvonly {
if t.Direction() == RTPTransceiverDirectionSendrecv {
t.setDirection(RTPTransceiverDirectionSendonly)
}
}
if t.Mid() == "" {
if err := t.setMid(midValue); err != nil {
return err
@ -1084,8 +1111,10 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error {
}
remoteIsLite := false
if liteValue, haveRemoteIs := desc.parsed.Attribute(sdp.AttrKeyICELite); haveRemoteIs && liteValue == sdp.AttrKeyICELite {
remoteIsLite = true
for _, a := range desc.parsed.Attributes {
if strings.TrimSpace(a.Key) == sdp.AttrKeyICELite {
remoteIsLite = true
}
}
fingerprint, fingerprintHash, err := extractFingerprint(desc.parsed)

View file

@ -85,23 +85,34 @@ type RTPParameters struct {
Codecs []RTPCodecParameters
}
type codecMatchType int
const (
codecMatchNone codecMatchType = 0
codecMatchPartial codecMatchType = 1
codecMatchExact codecMatchType = 2
)
// Do a fuzzy find for a codec in the list of codecs
// Used for lookup up a codec in an existing list to find a match
func codecParametersFuzzySearch(needle RTPCodecParameters, haystack []RTPCodecParameters) (RTPCodecParameters, error) {
// Returns codecMatchExact, codecMatchPartial, or codecMatchNone
func codecParametersFuzzySearch(needle RTPCodecParameters, haystack []RTPCodecParameters) (RTPCodecParameters, codecMatchType) {
// First attempt to match on MimeType + SDPFmtpLine
// Exact matches means fmtp line cannot be empty
for _, c := range haystack {
if strings.EqualFold(c.RTPCodecCapability.MimeType, needle.RTPCodecCapability.MimeType) &&
c.RTPCodecCapability.SDPFmtpLine == needle.RTPCodecCapability.SDPFmtpLine {
return c, nil
return c, codecMatchExact
}
}
// Fallback to just MimeType
// Fallback to just MimeType if either haystack or needle does not have fmtpline set
for _, c := range haystack {
if strings.EqualFold(c.RTPCodecCapability.MimeType, needle.RTPCodecCapability.MimeType) {
return c, nil
if strings.EqualFold(c.RTPCodecCapability.MimeType, needle.RTPCodecCapability.MimeType) &&
(c.RTPCodecCapability.SDPFmtpLine == "" || needle.RTPCodecCapability.SDPFmtpLine == "") {
return c, codecMatchPartial
}
}
return RTPCodecParameters{}, ErrCodecNotFound
return RTPCodecParameters{}, codecMatchNone
}

View file

@ -5,6 +5,7 @@ package webrtc
import (
"io"
"sync"
"time"
"github.com/pion/interceptor"
"github.com/pion/randutil"
@ -233,6 +234,12 @@ func (r *RTPSender) ReadRTCP() ([]rtcp.Packet, interceptor.Attributes, error) {
return pkts, attributes, nil
}
// SetReadDeadline sets the deadline for the Read operation.
// Setting to zero means no deadline.
func (r *RTPSender) SetReadDeadline(t time.Time) error {
return r.srtpStream.SetReadDeadline(t)
}
// hasSent tells if data has been ever sent for this instance
func (r *RTPSender) hasSent() bool {
select {

View file

@ -142,7 +142,7 @@ func satisfyTypeAndDirection(remoteKind RTPCodecType, remoteDirection RTPTransce
case RTPTransceiverDirectionSendrecv:
return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendrecv}
case RTPTransceiverDirectionSendonly:
return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendrecv}
return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}
case RTPTransceiverDirectionRecvonly:
return []RTPTransceiverDirection{RTPTransceiverDirectionSendonly, RTPTransceiverDirectionSendrecv}
default:

View file

@ -13,12 +13,8 @@ type SessionDescription struct {
parsed *sdp.SessionDescription
}
// Unmarshal is a helper to deserialize the sdp, and re-use it internally
// if required
// Unmarshal is a helper to deserialize the sdp
func (sd *SessionDescription) Unmarshal() (*sdp.SessionDescription, error) {
if sd.parsed != nil {
return sd.parsed, nil
}
sd.parsed = &sdp.SessionDescription{}
err := sd.parsed.Unmarshal([]byte(sd.SDP))
return sd.parsed, err

View file

@ -59,6 +59,7 @@ type SettingEngine struct {
LoggerFactory logging.LoggerFactory
iceTCPMux ice.TCPMux
iceProxyDialer proxy.Dialer
disableMediaEngineCopy bool
}
// DetachDataChannels enables detaching data channels. When enabled
@ -255,3 +256,10 @@ func (e *SettingEngine) SetICETCPMux(tcpMux ice.TCPMux) {
func (e *SettingEngine) SetICEProxyDialer(d proxy.Dialer) {
e.iceProxyDialer = d
}
// DisableMediaEngineCopy stops the MediaEngine from being copied. This allows a user to modify
// the MediaEngine after the PeerConnection has been constructed. This is useful if you wish to
// modify codecs after signaling. Make sure not to share MediaEngines between PeerConnections.
func (e *SettingEngine) DisableMediaEngineCopy(isDisabled bool) {
e.disableMediaEngineCopy = isDisabled
}

View file

@ -5,6 +5,7 @@ package webrtc
import (
"io"
"sync/atomic"
"time"
"github.com/pion/rtp"
"github.com/pion/srtp/v2"
@ -80,6 +81,18 @@ func (s *srtpWriterFuture) Read(b []byte) (n int, err error) {
return s.Read(b)
}
func (s *srtpWriterFuture) SetReadDeadline(t time.Time) error {
if value := s.rtcpReadStream.Load(); value != nil {
return value.(*srtp.ReadStreamSRTCP).SetReadDeadline(t)
}
if err := s.init(false); err != nil || s.rtcpReadStream.Load() == nil {
return err
}
return s.SetReadDeadline(t)
}
func (s *srtpWriterFuture) WriteRTP(header *rtp.Header, payload []byte) (int, error) {
if value := s.rtpWriteStream.Load(); value != nil {
return value.(*srtp.WriteStreamSRTP).WriteRTP(header, payload)

View file

@ -48,7 +48,7 @@ func (s *TrackLocalStaticRTP) Bind(t TrackLocalContext) (RTPCodecParameters, err
defer s.mu.Unlock()
parameters := RTPCodecParameters{RTPCodecCapability: s.codec}
if codec, err := codecParametersFuzzySearch(parameters, t.CodecParameters()); err == nil {
if codec, matchType := codecParametersFuzzySearch(parameters, t.CodecParameters()); matchType != codecMatchNone {
s.bindings = append(s.bindings, trackBinding{
ssrc: t.SSRC(),
payloadType: codec.PayloadType,
@ -103,21 +103,40 @@ func (s *TrackLocalStaticRTP) Codec() RTPCodecCapability {
return s.codec
}
// packetPool is a pool of packets used by WriteRTP and Write below
// nolint:gochecknoglobals
var rtpPacketPool = sync.Pool{
New: func() interface{} {
return &rtp.Packet{}
},
}
// WriteRTP writes a RTP Packet to the TrackLocalStaticRTP
// If one PeerConnection fails the packets will still be sent to
// all PeerConnections. The error message will contain the ID of the failed
// PeerConnections so you can remove them
func (s *TrackLocalStaticRTP) WriteRTP(p *rtp.Packet) error {
ipacket := rtpPacketPool.Get()
packet := ipacket.(*rtp.Packet)
defer func() {
*packet = rtp.Packet{}
rtpPacketPool.Put(ipacket)
}()
*packet = *p
return s.writeRTP(packet)
}
// writeRTP is like WriteRTP, except that it may modify the packet p
func (s *TrackLocalStaticRTP) writeRTP(p *rtp.Packet) error {
s.mu.RLock()
defer s.mu.RUnlock()
writeErrs := []error{}
outboundPacket := *p
for _, b := range s.bindings {
outboundPacket.Header.SSRC = uint32(b.ssrc)
outboundPacket.Header.PayloadType = uint8(b.payloadType)
if _, err := b.writeStream.WriteRTP(&outboundPacket.Header, outboundPacket.Payload); err != nil {
p.Header.SSRC = uint32(b.ssrc)
p.Header.PayloadType = uint8(b.payloadType)
if _, err := b.writeStream.WriteRTP(&p.Header, p.Payload); err != nil {
writeErrs = append(writeErrs, err)
}
}
@ -130,12 +149,18 @@ func (s *TrackLocalStaticRTP) WriteRTP(p *rtp.Packet) error {
// all PeerConnections. The error message will contain the ID of the failed
// PeerConnections so you can remove them
func (s *TrackLocalStaticRTP) Write(b []byte) (n int, err error) {
packet := &rtp.Packet{}
ipacket := rtpPacketPool.Get()
packet := ipacket.(*rtp.Packet)
defer func() {
*packet = rtp.Packet{}
rtpPacketPool.Put(ipacket)
}()
if err = packet.Unmarshal(b); err != nil {
return 0, err
}
return len(b), s.WriteRTP(packet)
return len(b), s.writeRTP(packet)
}
// TrackLocalStaticSample is a TrackLocal that has a pre-set codec and accepts Samples.