diff --git a/api_test.go b/api_test.go index c930671d..af7eaf79 100644 --- a/api_test.go +++ b/api_test.go @@ -4,6 +4,8 @@ package webrtc import ( "testing" + + "github.com/stretchr/testify/assert" ) func TestNewAPI(t *testing.T) { @@ -22,7 +24,7 @@ func TestNewAPI_Options(t *testing.T) { s := SettingEngine{} s.DetachDataChannels() m := MediaEngine{} - m.RegisterDefaultCodecs() + assert.NoError(t, m.RegisterDefaultCodecs()) api := NewAPI( WithSettingEngine(s), @@ -33,7 +35,7 @@ func TestNewAPI_Options(t *testing.T) { t.Error("Failed to set settings engine") } - if len(api.mediaEngine.codecs) == 0 { + if len(api.mediaEngine.audioCodecs) == 0 || len(api.mediaEngine.videoCodecs) == 0 { t.Error("Failed to set media engine") } } diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 96688717..ba203baa 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -5,7 +5,6 @@ package main import ( "context" "encoding/json" - "errors" "fmt" "os" "strconv" @@ -13,11 +12,9 @@ import ( "testing" "time" - "github.com/sclevine/agouti" - - "github.com/pion/randutil" "github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3/pkg/media" + "github.com/sclevine/agouti" ) var silentOpusFrame = []byte{0xf8, 0xff, 0xfe} // 20ms, 8kHz, mono @@ -103,7 +100,7 @@ func TestE2E_Audio(t *testing.T) { go func() { for { if err := track.WriteSample( - media.Sample{Data: silentOpusFrame, Samples: 960}, + media.Sample{Data: silentOpusFrame, Duration: time.Millisecond * 20}, ); err != nil { t.Errorf("Failed to WriteSample: %v", err) return @@ -330,28 +327,13 @@ func parseLog(log agouti.Log) (string, string, bool) { return k, v, true } -func createTrack(offer webrtc.SessionDescription) (*webrtc.PeerConnection, *webrtc.SessionDescription, *webrtc.Track, error) { - mediaEngine := webrtc.MediaEngine{} - if err := mediaEngine.PopulateFromSDP(offer); err != nil { - return nil, nil, nil, err - } - var payloadType uint8 - for _, videoCodec := range mediaEngine.GetCodecsByKind(webrtc.RTPCodecTypeAudio) { - if videoCodec.Name == webrtc.Opus { - payloadType = videoCodec.PayloadType - break - } - } - if payloadType == 0 { - return nil, nil, nil, errors.New("Remote peer does not support VP8") - } - api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine)) - pc, errPc := api.NewPeerConnection(webrtc.Configuration{}) +func createTrack(offer webrtc.SessionDescription) (*webrtc.PeerConnection, *webrtc.SessionDescription, *webrtc.TrackLocalStaticSample, error) { + pc, errPc := webrtc.NewPeerConnection(webrtc.Configuration{}) if errPc != nil { return nil, nil, nil, errPc } - track, errTrack := pc.NewTrack(payloadType, randutil.NewMathRandomGenerator().Uint32(), "video", "pion") + track, errTrack := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: "audio/opus"}, "audio", "pion") if errTrack != nil { return nil, nil, nil, errTrack } diff --git a/errors.go b/errors.go index 2a27ad9b..8fb5b9a1 100644 --- a/errors.go +++ b/errors.go @@ -129,6 +129,18 @@ var ( // ErrFailedToGenerateCertificateFingerprint indicates that we failed to generate the fingerprint used for comparing certificates ErrFailedToGenerateCertificateFingerprint = errors.New("failed to generate certificate fingerprint") + // ErrNoCodecsAvailable indicates that operation isn't possible because the MediaEngine has no codecs available + ErrNoCodecsAvailable = errors.New("operation failed no codecs are available") + + // ErrUnsupportedCodec indicates the remote peer doesn't support the requested codec + ErrUnsupportedCodec = errors.New("unable to start track, codec is not supported by remote") + + // ErrUnbindFailed indicates that a TrackLocal was not able to be unbind + ErrUnbindFailed = errors.New("failed to unbind TrackLocal from PeerConnection") + + // ErrNoPayloaderForCodec indicates that the requested codec does not have a payloader + ErrNoPayloaderForCodec = errors.New("the requested codec does not have a payloader") + errDetachNotEnabled = errors.New("enable detaching by calling webrtc.DetachDataChannels()") errDetachBeforeOpened = errors.New("datachannel not opened yet, try calling Detach from OnOpen") errDtlsTransportNotStarted = errors.New("the DTLS transport has not started yet") @@ -149,9 +161,7 @@ var ( errICEProtocolUnknown = errors.New("unknown protocol") errICEGathererNotStarted = errors.New("gatherer not started") - errMediaEngineParseError = errors.New("format parse error") - errMediaEngineCodecNotFound = errors.New("could not find codec") - errNetworkTypeUnknown = errors.New("unknown network type") + errNetworkTypeUnknown = errors.New("unknown network type") errSDPDoesNotMatchOffer = errors.New("new sdp does not match previous offer") errSDPDoesNotMatchAnswer = errors.New("new sdp does not match previous answer") @@ -163,16 +173,15 @@ var ( errPeerConnRemoteDescriptionNil = errors.New("remoteDescription has not been set yet") errPeerConnSingleMediaSectionHasExplicitSSRC = errors.New("single media section has an explicit SSRC") errPeerConnRemoteSSRCAddTransceiver = errors.New("could not add transceiver for remote SSRC") - errPeerConnSimulcastMidAndRidRTPExtensionRequired = errors.New("mid and rid RTP Extensions required for Simulcast") + errPeerConnSimulcastMidRTPExtensionRequired = errors.New("mid RTP Extensions required for Simulcast") + errPeerConnSimulcastStreamIDRTPExtensionRequired = errors.New("stream id RTP Extensions required for Simulcast") errPeerConnSimulcastIncomingSSRCFailed = errors.New("incoming SSRC failed Simulcast probing") errPeerConnAddTransceiverFromKindOnlyAcceptsOne = errors.New("AddTransceiverFromKind only accepts one RtpTransceiverInit") errPeerConnAddTransceiverFromTrackOnlyAcceptsOne = errors.New("AddTransceiverFromTrack only accepts one RtpTransceiverInit") - errPeerConnCodecsNotFound = errors.New("no codecs found") - errPeerConnAddTransceiverFromKindSupport = errors.New("AddTransceiverFromKind currently only supports recvonly and sendrecv") - errPeerConnAddTransceiverFromTrackOneTransceiver = errors.New("AddTransceiverFromTrack only accepts one RtpTransceiverInit") + errPeerConnAddTransceiverFromKindSupport = errors.New("AddTransceiverFromKind currently only supports recvonly") + errPeerConnAddTransceiverFromTrackSupport = errors.New("AddTransceiverFromTrack currently only supports sendonly and sendrecv") errPeerConnSetIdentityProviderNotImplemented = errors.New("TODO SetIdentityProvider") errPeerConnWriteRTCPOpenWriteStream = errors.New("WriteRTCP failed to open WriteStream") - errPeerConnCodecPayloaderNotSet = errors.New("codec payloader not set") errPeerConnTranscieverMidNil = errors.New("cannot find transceiver with mid") errRTPReceiverDTLSTransportNil = errors.New("DTLSTransport must not be nil") @@ -181,11 +190,9 @@ var ( errRTPReceiverForSSRCTrackStreamNotFound = errors.New("no trackStreams found for SSRC") errRTPReceiverForRIDTrackStreamNotFound = errors.New("no trackStreams found for RID") - errRTPSenderTrackNil = errors.New("Track must not be nil") - errRTPSenderDTLSTransportNil = errors.New("DTLSTransport must not be nil") - errRTPSenderCannotConstructRemoteTrack = errors.New("RTPSender can not be constructed with remote track") - errRTPSenderSendAlreadyCalled = errors.New("Send has already been called") - errRTPSenderStopped = errors.New("RTPSender has been stopped") + errRTPSenderTrackNil = errors.New("Track must not be nil") + errRTPSenderDTLSTransportNil = errors.New("DTLSTransport must not be nil") + errRTPSenderSendAlreadyCalled = errors.New("Send has already been called") errRTPTransceiverCannotChangeMid = errors.New("errRTPSenderTrackNil") errRTPTransceiverSetSendingInvalidState = errors.New("invalid state change in RTPTransceiver.setSending") @@ -195,8 +202,6 @@ var ( errSDPZeroTransceivers = errors.New("addTransceiverSDP() called with 0 transceivers") errSDPMediaSectionMediaDataChanInvalid = errors.New("invalid Media Section. Media + DataChannel both enabled") errSDPMediaSectionMultipleTrackInvalid = errors.New("invalid Media Section. Can not have multiple tracks in one MediaSection in UnifiedPlan") - errSDPParseExtMap = errors.New("failed to parse ExtMap") - errSDPRemoteDescriptionChangedExtMap = errors.New("RemoteDescription changed some extmaps values") errSettingEngineSetAnsweringDTLSRole = errors.New("SetAnsweringDTLSRole must DTLSRoleClient or DTLSRoleServer") @@ -204,8 +209,4 @@ var ( errSignalingStateProposedTransitionInvalid = errors.New("invalid proposed signaling state transition") errStatsICECandidateStateInvalid = errors.New("cannot convert to StatsICECandidatePairStateSucceeded invalid ice candidate state") - - errTrackLocalTrackRead = errors.New("this is a local track and must not be read from") - errTrackLocalTrackWrite = errors.New("this is a remote track and must not be written to") - errTrackSSRCNewTrackZero = errors.New("SSRC supplied to NewTrack() must be non-zero") ) diff --git a/examples/broadcast/main.go b/examples/broadcast/main.go index 04f9a260..dfee2c00 100644 --- a/examples/broadcast/main.go +++ b/examples/broadcast/main.go @@ -23,16 +23,6 @@ func main() { // nolint:gocognit signal.Decode(<-sdpChan, &offer) fmt.Println("") - // Since we are answering use PayloadTypes declared by offerer - mediaEngine := webrtc.MediaEngine{} - err := mediaEngine.PopulateFromSDP(offer) - if err != nil { - panic(err) - } - - // Create the API object with the MediaEngine - api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine)) - peerConnectionConfig := webrtc.Configuration{ ICEServers: []webrtc.ICEServer{ { @@ -42,7 +32,7 @@ func main() { // nolint:gocognit } // Create a new RTCPeerConnection - peerConnection, err := api.NewPeerConnection(peerConnectionConfig) + peerConnection, err := webrtc.NewPeerConnection(peerConnectionConfig) if err != nil { panic(err) } @@ -52,23 +42,23 @@ func main() { // nolint:gocognit panic(err) } - localTrackChan := make(chan *webrtc.Track) + localTrackChan := make(chan *webrtc.TrackLocalStaticRTP) // Set a handler for when a new remote track starts, this just distributes all our packets // to connected peers - peerConnection.OnTrack(func(remoteTrack *webrtc.Track, receiver *webrtc.RTPReceiver) { + peerConnection.OnTrack(func(remoteTrack *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) { // Send a PLI on an interval so that the publisher is pushing a keyframe every rtcpPLIInterval // This can be less wasteful by processing incoming RTCP events, then we would emit a NACK/PLI when a viewer requests it go func() { ticker := time.NewTicker(rtcpPLIInterval) for range ticker.C { - if rtcpSendErr := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: remoteTrack.SSRC()}}); rtcpSendErr != nil { + if rtcpSendErr := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(remoteTrack.SSRC())}}); rtcpSendErr != nil { fmt.Println(rtcpSendErr) } } }() // Create a local track, all our SFU clients will be fed via this track - localTrack, newTrackErr := peerConnection.NewTrack(remoteTrack.PayloadType(), remoteTrack.SSRC(), "video", "pion") + localTrack, newTrackErr := webrtc.NewTrackLocalStaticRTP(remoteTrack.Codec().RTPCodecCapability, "video", "pion") if newTrackErr != nil { panic(newTrackErr) } @@ -126,7 +116,7 @@ func main() { // nolint:gocognit signal.Decode(<-sdpChan, &recvOnlyOffer) // Create a new PeerConnection - peerConnection, err := api.NewPeerConnection(peerConnectionConfig) + peerConnection, err := webrtc.NewPeerConnection(peerConnectionConfig) if err != nil { panic(err) } diff --git a/examples/custom-logger/main.go b/examples/custom-logger/main.go index 4f344dab..7e8edcef 100644 --- a/examples/custom-logger/main.go +++ b/examples/custom-logger/main.go @@ -61,6 +61,11 @@ func main() { panic(err) } + // We need a DataChannel so we can have ICE Candidates + if _, err = offerPeerConnection.CreateDataChannel("custom-logger", nil); err != nil { + panic(err) + } + // Create a new RTCPeerConnection answerPeerConnection, err := api.NewPeerConnection(webrtc.Configuration{}) if err != nil { diff --git a/examples/ice-tcp/main.go b/examples/ice-tcp/main.go index ba0c2779..528d254c 100644 --- a/examples/ice-tcp/main.go +++ b/examples/ice-tcp/main.go @@ -17,7 +17,9 @@ func doSignaling(w http.ResponseWriter, r *http.Request) { if peerConnection == nil { m := webrtc.MediaEngine{} - m.RegisterDefaultCodecs() + if err = m.RegisterDefaultCodecs(); err != nil { + panic(err) + } settingEngine := webrtc.SettingEngine{} diff --git a/examples/insertable-streams/main.go b/examples/insertable-streams/main.go index 691626ad..f6169809 100644 --- a/examples/insertable-streams/main.go +++ b/examples/insertable-streams/main.go @@ -7,7 +7,6 @@ import ( "os" "time" - "github.com/pion/randutil" "github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3/examples/internal/signal" "github.com/pion/webrtc/v3/pkg/media" @@ -17,34 +16,7 @@ import ( const cipherKey = 0xAA func main() { - // Wait for the offer to be pasted - offer := webrtc.SessionDescription{} - signal.Decode(signal.MustReadStdin(), &offer) - - // We make our own mediaEngine so we can place the sender's codecs in it. This because we must use the - // dynamic media type from the sender in our answer. This is not required if we are the offerer - mediaEngine := webrtc.MediaEngine{} - err := mediaEngine.PopulateFromSDP(offer) - if err != nil { - panic(err) - } - - // Search for VP8 Payload type. If the offer doesn't support VP8 exit since - // since they won't be able to decode anything we send them - var payloadType uint8 - for _, videoCodec := range mediaEngine.GetCodecsByKind(webrtc.RTPCodecTypeVideo) { - if videoCodec.Name == "VP8" { - payloadType = videoCodec.PayloadType - break - } - } - if payloadType == 0 { - panic("Remote peer does not support VP8") - } - - // Create a new RTCPeerConnection - api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine)) - peerConnection, err := api.NewPeerConnection(webrtc.Configuration{ + peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{ ICEServers: []webrtc.ICEServer{ { URLs: []string{"stun:stun.l.google.com:19302"}, @@ -56,7 +28,7 @@ func main() { } // Create a video track - videoTrack, err := peerConnection.NewTrack(payloadType, randutil.NewMathRandomGenerator().Uint32(), "video", "pion") + videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: "video/vp8"}, "video", "pion") if err != nil { panic(err) } @@ -100,7 +72,7 @@ func main() { } time.Sleep(sleepTime) - if ivfErr = videoTrack.WriteSample(media.Sample{Data: frame, Samples: 90000}); ivfErr != nil { + if ivfErr = videoTrack.WriteSample(media.Sample{Data: frame, Duration: time.Second}); ivfErr != nil { panic(ivfErr) } } @@ -115,6 +87,10 @@ func main() { } }) + // Wait for the offer to be pasted + offer := webrtc.SessionDescription{} + signal.Decode(signal.MustReadStdin(), &offer) + // Set the remote SessionDescription if err = peerConnection.SetRemoteDescription(offer); err != nil { panic(err) diff --git a/examples/play-from-disk-renegotation/index.html b/examples/play-from-disk-renegotation/index.html index a1a22d39..2a0ab5c4 100644 --- a/examples/play-from-disk-renegotation/index.html +++ b/examples/play-from-disk-renegotation/index.html @@ -17,7 +17,7 @@