github.com/pion/webrtc/v4@v4.0.1/rtpsender.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  //go:build !js
     5  // +build !js
     6  
     7  package webrtc
     8  
     9  import (
    10  	"fmt"
    11  	"io"
    12  	"sync"
    13  	"time"
    14  
    15  	"github.com/pion/interceptor"
    16  	"github.com/pion/randutil"
    17  	"github.com/pion/rtcp"
    18  	"github.com/pion/rtp"
    19  	"github.com/pion/webrtc/v4/internal/util"
    20  )
    21  
    22  type trackEncoding struct {
    23  	track TrackLocal
    24  
    25  	srtpStream *srtpWriterFuture
    26  
    27  	rtcpInterceptor interceptor.RTCPReader
    28  	streamInfo      interceptor.StreamInfo
    29  
    30  	context *baseTrackLocalContext
    31  
    32  	ssrc, ssrcRTX, ssrcFEC SSRC
    33  }
    34  
    35  // RTPSender allows an application to control how a given Track is encoded and transmitted to a remote peer
    36  type RTPSender struct {
    37  	trackEncodings []*trackEncoding
    38  
    39  	transport *DTLSTransport
    40  
    41  	payloadType PayloadType
    42  	kind        RTPCodecType
    43  
    44  	// nolint:godox
    45  	// TODO(sgotti) remove this when in future we'll avoid replacing
    46  	// a transceiver sender since we can just check the
    47  	// transceiver negotiation status
    48  	negotiated bool
    49  
    50  	// A reference to the associated api object
    51  	api *API
    52  	id  string
    53  
    54  	rtpTransceiver *RTPTransceiver
    55  
    56  	mu                     sync.RWMutex
    57  	sendCalled, stopCalled chan struct{}
    58  }
    59  
    60  // NewRTPSender constructs a new RTPSender
    61  func (api *API) NewRTPSender(track TrackLocal, transport *DTLSTransport) (*RTPSender, error) {
    62  	if track == nil {
    63  		return nil, errRTPSenderTrackNil
    64  	} else if transport == nil {
    65  		return nil, errRTPSenderDTLSTransportNil
    66  	}
    67  
    68  	id, err := randutil.GenerateCryptoRandomString(32, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	r := &RTPSender{
    74  		transport:  transport,
    75  		api:        api,
    76  		sendCalled: make(chan struct{}),
    77  		stopCalled: make(chan struct{}),
    78  		id:         id,
    79  		kind:       track.Kind(),
    80  	}
    81  
    82  	r.addEncoding(track)
    83  
    84  	return r, nil
    85  }
    86  
    87  func (r *RTPSender) isNegotiated() bool {
    88  	r.mu.RLock()
    89  	defer r.mu.RUnlock()
    90  	return r.negotiated
    91  }
    92  
    93  func (r *RTPSender) setNegotiated() {
    94  	r.mu.Lock()
    95  	defer r.mu.Unlock()
    96  	r.negotiated = true
    97  }
    98  
    99  func (r *RTPSender) setRTPTransceiver(rtpTransceiver *RTPTransceiver) {
   100  	r.mu.Lock()
   101  	defer r.mu.Unlock()
   102  	r.rtpTransceiver = rtpTransceiver
   103  }
   104  
   105  // Transport returns the currently-configured *DTLSTransport or nil
   106  // if one has not yet been configured
   107  func (r *RTPSender) Transport() *DTLSTransport {
   108  	r.mu.RLock()
   109  	defer r.mu.RUnlock()
   110  	return r.transport
   111  }
   112  
   113  // GetParameters describes the current configuration for the encoding and
   114  // transmission of media on the sender's track.
   115  func (r *RTPSender) GetParameters() RTPSendParameters {
   116  	r.mu.RLock()
   117  	defer r.mu.RUnlock()
   118  
   119  	var encodings []RTPEncodingParameters
   120  	for _, trackEncoding := range r.trackEncodings {
   121  		var rid string
   122  		if trackEncoding.track != nil {
   123  			rid = trackEncoding.track.RID()
   124  		}
   125  		encodings = append(encodings, RTPEncodingParameters{
   126  			RTPCodingParameters: RTPCodingParameters{
   127  				RID:         rid,
   128  				SSRC:        trackEncoding.ssrc,
   129  				RTX:         RTPRtxParameters{SSRC: trackEncoding.ssrcRTX},
   130  				FEC:         RTPFecParameters{SSRC: trackEncoding.ssrcFEC},
   131  				PayloadType: r.payloadType,
   132  			},
   133  		})
   134  	}
   135  	sendParameters := RTPSendParameters{
   136  		RTPParameters: r.api.mediaEngine.getRTPParametersByKind(
   137  			r.kind,
   138  			[]RTPTransceiverDirection{RTPTransceiverDirectionSendonly},
   139  		),
   140  		Encodings: encodings,
   141  	}
   142  	if r.rtpTransceiver != nil {
   143  		sendParameters.Codecs = r.rtpTransceiver.getCodecs()
   144  	} else {
   145  		sendParameters.Codecs = r.api.mediaEngine.getCodecsByKind(r.kind)
   146  	}
   147  	return sendParameters
   148  }
   149  
   150  // AddEncoding adds an encoding to RTPSender. Used by simulcast senders.
   151  func (r *RTPSender) AddEncoding(track TrackLocal) error {
   152  	r.mu.Lock()
   153  	defer r.mu.Unlock()
   154  
   155  	if track == nil {
   156  		return errRTPSenderTrackNil
   157  	}
   158  
   159  	if track.RID() == "" {
   160  		return errRTPSenderRidNil
   161  	}
   162  
   163  	if r.hasStopped() {
   164  		return errRTPSenderStopped
   165  	}
   166  
   167  	if r.hasSent() {
   168  		return errRTPSenderSendAlreadyCalled
   169  	}
   170  
   171  	var refTrack TrackLocal
   172  	if len(r.trackEncodings) != 0 {
   173  		refTrack = r.trackEncodings[0].track
   174  	}
   175  	if refTrack == nil || refTrack.RID() == "" {
   176  		return errRTPSenderNoBaseEncoding
   177  	}
   178  
   179  	if refTrack.ID() != track.ID() || refTrack.StreamID() != track.StreamID() || refTrack.Kind() != track.Kind() {
   180  		return errRTPSenderBaseEncodingMismatch
   181  	}
   182  
   183  	for _, encoding := range r.trackEncodings {
   184  		if encoding.track == nil {
   185  			continue
   186  		}
   187  
   188  		if encoding.track.RID() == track.RID() {
   189  			return errRTPSenderRIDCollision
   190  		}
   191  	}
   192  
   193  	r.addEncoding(track)
   194  	return nil
   195  }
   196  
   197  func (r *RTPSender) addEncoding(track TrackLocal) {
   198  	trackEncoding := &trackEncoding{
   199  		track: track,
   200  		ssrc:  SSRC(util.RandUint32()),
   201  	}
   202  
   203  	if r.api.mediaEngine.isRTXEnabled(r.kind, []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}) {
   204  		trackEncoding.ssrcRTX = SSRC(util.RandUint32())
   205  	}
   206  
   207  	if r.api.mediaEngine.isFECEnabled(r.kind, []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}) {
   208  		trackEncoding.ssrcFEC = SSRC(util.RandUint32())
   209  	}
   210  
   211  	r.trackEncodings = append(r.trackEncodings, trackEncoding)
   212  }
   213  
   214  // Track returns the RTCRtpTransceiver track, or nil
   215  func (r *RTPSender) Track() TrackLocal {
   216  	r.mu.RLock()
   217  	defer r.mu.RUnlock()
   218  
   219  	if len(r.trackEncodings) == 0 {
   220  		return nil
   221  	}
   222  
   223  	return r.trackEncodings[0].track
   224  }
   225  
   226  // ReplaceTrack replaces the track currently being used as the sender's source with a new TrackLocal.
   227  // The new track must be of the same media kind (audio, video, etc) and switching the track should not
   228  // require negotiation.
   229  func (r *RTPSender) ReplaceTrack(track TrackLocal) error {
   230  	r.mu.Lock()
   231  	defer r.mu.Unlock()
   232  
   233  	if track != nil && r.kind != track.Kind() {
   234  		return ErrRTPSenderNewTrackHasIncorrectKind
   235  	}
   236  
   237  	// cannot replace simulcast envelope
   238  	if track != nil && len(r.trackEncodings) > 1 {
   239  		return ErrRTPSenderNewTrackHasIncorrectEnvelope
   240  	}
   241  
   242  	var replacedTrack TrackLocal
   243  	var context *baseTrackLocalContext
   244  	for _, e := range r.trackEncodings {
   245  		replacedTrack = e.track
   246  		context = e.context
   247  
   248  		if r.hasSent() && replacedTrack != nil {
   249  			if err := replacedTrack.Unbind(context); err != nil {
   250  				return err
   251  			}
   252  		}
   253  
   254  		if !r.hasSent() || track == nil {
   255  			e.track = track
   256  		}
   257  	}
   258  
   259  	if !r.hasSent() || track == nil {
   260  		return nil
   261  	}
   262  
   263  	// If we reach this point in the routine, there is only 1 track encoding
   264  	codec, err := track.Bind(&baseTrackLocalContext{
   265  		id:              context.ID(),
   266  		params:          r.api.mediaEngine.getRTPParametersByKind(track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}),
   267  		ssrc:            context.SSRC(),
   268  		ssrcRTX:         context.SSRCRetransmission(),
   269  		ssrcFEC:         context.SSRCForwardErrorCorrection(),
   270  		writeStream:     context.WriteStream(),
   271  		rtcpInterceptor: context.RTCPReader(),
   272  	})
   273  	if err != nil {
   274  		// Re-bind the original track
   275  		if _, reBindErr := replacedTrack.Bind(context); reBindErr != nil {
   276  			return reBindErr
   277  		}
   278  
   279  		return err
   280  	}
   281  
   282  	// Codec has changed
   283  	if r.payloadType != codec.PayloadType {
   284  		context.params.Codecs = []RTPCodecParameters{codec}
   285  	}
   286  
   287  	r.trackEncodings[0].track = track
   288  
   289  	return nil
   290  }
   291  
   292  // Send Attempts to set the parameters controlling the sending of media.
   293  func (r *RTPSender) Send(parameters RTPSendParameters) error {
   294  	r.mu.Lock()
   295  	defer r.mu.Unlock()
   296  
   297  	switch {
   298  	case r.hasSent():
   299  		return errRTPSenderSendAlreadyCalled
   300  	case r.trackEncodings[0].track == nil:
   301  		return errRTPSenderTrackRemoved
   302  	}
   303  
   304  	for idx := range r.trackEncodings {
   305  		trackEncoding := r.trackEncodings[idx]
   306  		srtpStream := &srtpWriterFuture{ssrc: parameters.Encodings[idx].SSRC, rtpSender: r}
   307  		writeStream := &interceptorToTrackLocalWriter{}
   308  		rtpParameters := r.api.mediaEngine.getRTPParametersByKind(trackEncoding.track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly})
   309  
   310  		trackEncoding.srtpStream = srtpStream
   311  		trackEncoding.ssrc = parameters.Encodings[idx].SSRC
   312  		trackEncoding.ssrcRTX = parameters.Encodings[idx].RTX.SSRC
   313  		trackEncoding.ssrcFEC = parameters.Encodings[idx].FEC.SSRC
   314  		trackEncoding.context = &baseTrackLocalContext{
   315  			id:              r.id,
   316  			params:          rtpParameters,
   317  			ssrc:            parameters.Encodings[idx].SSRC,
   318  			ssrcFEC:         parameters.Encodings[idx].FEC.SSRC,
   319  			ssrcRTX:         parameters.Encodings[idx].RTX.SSRC,
   320  			writeStream:     writeStream,
   321  			rtcpInterceptor: trackEncoding.rtcpInterceptor,
   322  		}
   323  
   324  		codec, err := trackEncoding.track.Bind(trackEncoding.context)
   325  		if err != nil {
   326  			return err
   327  		}
   328  		trackEncoding.context.params.Codecs = []RTPCodecParameters{codec}
   329  
   330  		trackEncoding.streamInfo = *createStreamInfo(
   331  			r.id,
   332  			parameters.Encodings[idx].SSRC,
   333  			parameters.Encodings[idx].RTX.SSRC,
   334  			parameters.Encodings[idx].FEC.SSRC,
   335  			codec.PayloadType,
   336  			findRTXPayloadType(codec.PayloadType, rtpParameters.Codecs),
   337  			0,
   338  			codec.RTPCodecCapability,
   339  			parameters.HeaderExtensions,
   340  		)
   341  
   342  		trackEncoding.rtcpInterceptor = r.api.interceptor.BindRTCPReader(
   343  			interceptor.RTCPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) {
   344  				n, err = trackEncoding.srtpStream.Read(in)
   345  				return n, a, err
   346  			}),
   347  		)
   348  
   349  		rtpInterceptor := r.api.interceptor.BindLocalStream(
   350  			&trackEncoding.streamInfo,
   351  			interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, _ interceptor.Attributes) (int, error) {
   352  				return srtpStream.WriteRTP(header, payload)
   353  			}),
   354  		)
   355  
   356  		writeStream.interceptor.Store(rtpInterceptor)
   357  	}
   358  
   359  	close(r.sendCalled)
   360  	return nil
   361  }
   362  
   363  // Stop irreversibly stops the RTPSender
   364  func (r *RTPSender) Stop() error {
   365  	r.mu.Lock()
   366  
   367  	if stopped := r.hasStopped(); stopped {
   368  		r.mu.Unlock()
   369  		return nil
   370  	}
   371  
   372  	close(r.stopCalled)
   373  	r.mu.Unlock()
   374  
   375  	if !r.hasSent() {
   376  		return nil
   377  	}
   378  
   379  	if err := r.ReplaceTrack(nil); err != nil {
   380  		return err
   381  	}
   382  
   383  	errs := []error{}
   384  	for _, trackEncoding := range r.trackEncodings {
   385  		r.api.interceptor.UnbindLocalStream(&trackEncoding.streamInfo)
   386  		if trackEncoding.srtpStream != nil {
   387  			errs = append(errs, trackEncoding.srtpStream.Close())
   388  		}
   389  	}
   390  
   391  	return util.FlattenErrs(errs)
   392  }
   393  
   394  // Read reads incoming RTCP for this RTPSender
   395  func (r *RTPSender) Read(b []byte) (n int, a interceptor.Attributes, err error) {
   396  	select {
   397  	case <-r.sendCalled:
   398  		return r.trackEncodings[0].rtcpInterceptor.Read(b, a)
   399  	case <-r.stopCalled:
   400  		return 0, nil, io.ErrClosedPipe
   401  	}
   402  }
   403  
   404  // ReadRTCP is a convenience method that wraps Read and unmarshals for you.
   405  func (r *RTPSender) ReadRTCP() ([]rtcp.Packet, interceptor.Attributes, error) {
   406  	b := make([]byte, r.api.settingEngine.getReceiveMTU())
   407  	i, attributes, err := r.Read(b)
   408  	if err != nil {
   409  		return nil, nil, err
   410  	}
   411  
   412  	pkts, err := rtcp.Unmarshal(b[:i])
   413  	if err != nil {
   414  		return nil, nil, err
   415  	}
   416  
   417  	return pkts, attributes, nil
   418  }
   419  
   420  // ReadSimulcast reads incoming RTCP for this RTPSender for given rid
   421  func (r *RTPSender) ReadSimulcast(b []byte, rid string) (n int, a interceptor.Attributes, err error) {
   422  	select {
   423  	case <-r.sendCalled:
   424  		for _, t := range r.trackEncodings {
   425  			if t.track != nil && t.track.RID() == rid {
   426  				return t.rtcpInterceptor.Read(b, a)
   427  			}
   428  		}
   429  		return 0, nil, fmt.Errorf("%w: %s", errRTPSenderNoTrackForRID, rid)
   430  	case <-r.stopCalled:
   431  		return 0, nil, io.ErrClosedPipe
   432  	}
   433  }
   434  
   435  // ReadSimulcastRTCP is a convenience method that wraps ReadSimulcast and unmarshal for you
   436  func (r *RTPSender) ReadSimulcastRTCP(rid string) ([]rtcp.Packet, interceptor.Attributes, error) {
   437  	b := make([]byte, r.api.settingEngine.getReceiveMTU())
   438  	i, attributes, err := r.ReadSimulcast(b, rid)
   439  	if err != nil {
   440  		return nil, nil, err
   441  	}
   442  
   443  	pkts, err := rtcp.Unmarshal(b[:i])
   444  	return pkts, attributes, err
   445  }
   446  
   447  // SetReadDeadline sets the deadline for the Read operation.
   448  // Setting to zero means no deadline.
   449  func (r *RTPSender) SetReadDeadline(t time.Time) error {
   450  	return r.trackEncodings[0].srtpStream.SetReadDeadline(t)
   451  }
   452  
   453  // SetReadDeadlineSimulcast sets the max amount of time the RTCP stream for a given rid will block before returning. 0 is forever.
   454  func (r *RTPSender) SetReadDeadlineSimulcast(deadline time.Time, rid string) error {
   455  	r.mu.RLock()
   456  	defer r.mu.RUnlock()
   457  
   458  	for _, t := range r.trackEncodings {
   459  		if t.track != nil && t.track.RID() == rid {
   460  			return t.srtpStream.SetReadDeadline(deadline)
   461  		}
   462  	}
   463  	return fmt.Errorf("%w: %s", errRTPSenderNoTrackForRID, rid)
   464  }
   465  
   466  // hasSent tells if data has been ever sent for this instance
   467  func (r *RTPSender) hasSent() bool {
   468  	select {
   469  	case <-r.sendCalled:
   470  		return true
   471  	default:
   472  		return false
   473  	}
   474  }
   475  
   476  // hasStopped tells if stop has been called
   477  func (r *RTPSender) hasStopped() bool {
   478  	select {
   479  	case <-r.stopCalled:
   480  		return true
   481  	default:
   482  		return false
   483  	}
   484  }
   485  
   486  // Set a SSRC for FEC and RTX if MediaEngine has them enabled
   487  // If the remote doesn't support FEC or RTX we disable locally
   488  func (r *RTPSender) configureRTXAndFEC() {
   489  	r.mu.RLock()
   490  	defer r.mu.RUnlock()
   491  
   492  	for _, trackEncoding := range r.trackEncodings {
   493  		if !r.api.mediaEngine.isRTXEnabled(r.kind, []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}) {
   494  			trackEncoding.ssrcRTX = SSRC(0)
   495  		}
   496  
   497  		if !r.api.mediaEngine.isFECEnabled(r.kind, []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}) {
   498  			trackEncoding.ssrcFEC = SSRC(0)
   499  		}
   500  	}
   501  }