github.com/pion/webrtc/v3@v3.2.24/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/v3/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 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  func (r *RTPSender) getParameters() RTPSendParameters {
   114  	var encodings []RTPEncodingParameters
   115  	for _, trackEncoding := range r.trackEncodings {
   116  		var rid string
   117  		if trackEncoding.track != nil {
   118  			rid = trackEncoding.track.RID()
   119  		}
   120  		encodings = append(encodings, RTPEncodingParameters{
   121  			RTPCodingParameters: RTPCodingParameters{
   122  				RID:         rid,
   123  				SSRC:        trackEncoding.ssrc,
   124  				PayloadType: r.payloadType,
   125  			},
   126  		})
   127  	}
   128  	sendParameters := RTPSendParameters{
   129  		RTPParameters: r.api.mediaEngine.getRTPParametersByKind(
   130  			r.kind,
   131  			[]RTPTransceiverDirection{RTPTransceiverDirectionSendonly},
   132  		),
   133  		Encodings: encodings,
   134  	}
   135  	if r.rtpTransceiver != nil {
   136  		sendParameters.Codecs = r.rtpTransceiver.getCodecs()
   137  	} else {
   138  		sendParameters.Codecs = r.api.mediaEngine.getCodecsByKind(r.kind)
   139  	}
   140  	return sendParameters
   141  }
   142  
   143  // GetParameters describes the current configuration for the encoding and
   144  // transmission of media on the sender's track.
   145  func (r *RTPSender) GetParameters() RTPSendParameters {
   146  	r.mu.RLock()
   147  	defer r.mu.RUnlock()
   148  	return r.getParameters()
   149  }
   150  
   151  // AddEncoding adds an encoding to RTPSender. Used by simulcast senders.
   152  func (r *RTPSender) AddEncoding(track TrackLocal) error {
   153  	r.mu.Lock()
   154  	defer r.mu.Unlock()
   155  
   156  	if track == nil {
   157  		return errRTPSenderTrackNil
   158  	}
   159  
   160  	if track.RID() == "" {
   161  		return errRTPSenderRidNil
   162  	}
   163  
   164  	if r.hasStopped() {
   165  		return errRTPSenderStopped
   166  	}
   167  
   168  	if r.hasSent() {
   169  		return errRTPSenderSendAlreadyCalled
   170  	}
   171  
   172  	var refTrack TrackLocal
   173  	if len(r.trackEncodings) != 0 {
   174  		refTrack = r.trackEncodings[0].track
   175  	}
   176  	if refTrack == nil || refTrack.RID() == "" {
   177  		return errRTPSenderNoBaseEncoding
   178  	}
   179  
   180  	if refTrack.ID() != track.ID() || refTrack.StreamID() != track.StreamID() || refTrack.Kind() != track.Kind() {
   181  		return errRTPSenderBaseEncodingMismatch
   182  	}
   183  
   184  	for _, encoding := range r.trackEncodings {
   185  		if encoding.track == nil {
   186  			continue
   187  		}
   188  
   189  		if encoding.track.RID() == track.RID() {
   190  			return errRTPSenderRIDCollision
   191  		}
   192  	}
   193  
   194  	r.addEncoding(track)
   195  	return nil
   196  }
   197  
   198  func (r *RTPSender) addEncoding(track TrackLocal) {
   199  	ssrc := SSRC(randutil.NewMathRandomGenerator().Uint32())
   200  	trackEncoding := &trackEncoding{
   201  		track:      track,
   202  		srtpStream: &srtpWriterFuture{ssrc: ssrc},
   203  		ssrc:       ssrc,
   204  	}
   205  	trackEncoding.srtpStream.rtpSender = r
   206  	trackEncoding.rtcpInterceptor = r.api.interceptor.BindRTCPReader(
   207  		interceptor.RTPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) {
   208  			n, err = trackEncoding.srtpStream.Read(in)
   209  			return n, a, err
   210  		}),
   211  	)
   212  
   213  	r.trackEncodings = append(r.trackEncodings, trackEncoding)
   214  }
   215  
   216  // Track returns the RTCRtpTransceiver track, or nil
   217  func (r *RTPSender) Track() TrackLocal {
   218  	r.mu.RLock()
   219  	defer r.mu.RUnlock()
   220  
   221  	if len(r.trackEncodings) == 0 {
   222  		return nil
   223  	}
   224  
   225  	return r.trackEncodings[0].track
   226  }
   227  
   228  // ReplaceTrack replaces the track currently being used as the sender's source with a new TrackLocal.
   229  // The new track must be of the same media kind (audio, video, etc) and switching the track should not
   230  // require negotiation.
   231  func (r *RTPSender) ReplaceTrack(track TrackLocal) error {
   232  	r.mu.Lock()
   233  	defer r.mu.Unlock()
   234  
   235  	if track != nil && r.kind != track.Kind() {
   236  		return ErrRTPSenderNewTrackHasIncorrectKind
   237  	}
   238  
   239  	// cannot replace simulcast envelope
   240  	if track != nil && len(r.trackEncodings) > 1 {
   241  		return ErrRTPSenderNewTrackHasIncorrectEnvelope
   242  	}
   243  
   244  	var replacedTrack TrackLocal
   245  	var context *baseTrackLocalContext
   246  	for _, e := range r.trackEncodings {
   247  		replacedTrack = e.track
   248  		context = e.context
   249  
   250  		if r.hasSent() && replacedTrack != nil {
   251  			if err := replacedTrack.Unbind(context); err != nil {
   252  				return err
   253  			}
   254  		}
   255  
   256  		if !r.hasSent() || track == nil {
   257  			e.track = track
   258  		}
   259  	}
   260  
   261  	if !r.hasSent() || track == nil {
   262  		return nil
   263  	}
   264  
   265  	// If we reach this point in the routine, there is only 1 track encoding
   266  	codec, err := track.Bind(&baseTrackLocalContext{
   267  		id:              context.ID(),
   268  		params:          r.api.mediaEngine.getRTPParametersByKind(track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}),
   269  		ssrc:            context.SSRC(),
   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, trackEncoding := range r.trackEncodings {
   305  		writeStream := &interceptorToTrackLocalWriter{}
   306  		trackEncoding.context = &baseTrackLocalContext{
   307  			id:              r.id,
   308  			params:          r.api.mediaEngine.getRTPParametersByKind(trackEncoding.track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}),
   309  			ssrc:            parameters.Encodings[idx].SSRC,
   310  			writeStream:     writeStream,
   311  			rtcpInterceptor: trackEncoding.rtcpInterceptor,
   312  		}
   313  
   314  		codec, err := trackEncoding.track.Bind(trackEncoding.context)
   315  		if err != nil {
   316  			return err
   317  		}
   318  		trackEncoding.context.params.Codecs = []RTPCodecParameters{codec}
   319  
   320  		trackEncoding.streamInfo = *createStreamInfo(
   321  			r.id,
   322  			parameters.Encodings[idx].SSRC,
   323  			codec.PayloadType,
   324  			codec.RTPCodecCapability,
   325  			parameters.HeaderExtensions,
   326  		)
   327  		srtpStream := trackEncoding.srtpStream
   328  		rtpInterceptor := r.api.interceptor.BindLocalStream(
   329  			&trackEncoding.streamInfo,
   330  			interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
   331  				return srtpStream.WriteRTP(header, payload)
   332  			}),
   333  		)
   334  		writeStream.interceptor.Store(rtpInterceptor)
   335  	}
   336  
   337  	close(r.sendCalled)
   338  	return nil
   339  }
   340  
   341  // Stop irreversibly stops the RTPSender
   342  func (r *RTPSender) Stop() error {
   343  	r.mu.Lock()
   344  
   345  	if stopped := r.hasStopped(); stopped {
   346  		r.mu.Unlock()
   347  		return nil
   348  	}
   349  
   350  	close(r.stopCalled)
   351  	r.mu.Unlock()
   352  
   353  	if !r.hasSent() {
   354  		return nil
   355  	}
   356  
   357  	if err := r.ReplaceTrack(nil); err != nil {
   358  		return err
   359  	}
   360  
   361  	errs := []error{}
   362  	for _, trackEncoding := range r.trackEncodings {
   363  		r.api.interceptor.UnbindLocalStream(&trackEncoding.streamInfo)
   364  		errs = append(errs, trackEncoding.srtpStream.Close())
   365  	}
   366  
   367  	return util.FlattenErrs(errs)
   368  }
   369  
   370  // Read reads incoming RTCP for this RTPSender
   371  func (r *RTPSender) Read(b []byte) (n int, a interceptor.Attributes, err error) {
   372  	select {
   373  	case <-r.sendCalled:
   374  		return r.trackEncodings[0].rtcpInterceptor.Read(b, a)
   375  	case <-r.stopCalled:
   376  		return 0, nil, io.ErrClosedPipe
   377  	}
   378  }
   379  
   380  // ReadRTCP is a convenience method that wraps Read and unmarshals for you.
   381  func (r *RTPSender) ReadRTCP() ([]rtcp.Packet, interceptor.Attributes, error) {
   382  	b := make([]byte, r.api.settingEngine.getReceiveMTU())
   383  	i, attributes, err := r.Read(b)
   384  	if err != nil {
   385  		return nil, nil, err
   386  	}
   387  
   388  	pkts, err := rtcp.Unmarshal(b[:i])
   389  	if err != nil {
   390  		return nil, nil, err
   391  	}
   392  
   393  	return pkts, attributes, nil
   394  }
   395  
   396  // ReadSimulcast reads incoming RTCP for this RTPSender for given rid
   397  func (r *RTPSender) ReadSimulcast(b []byte, rid string) (n int, a interceptor.Attributes, err error) {
   398  	select {
   399  	case <-r.sendCalled:
   400  		for _, t := range r.trackEncodings {
   401  			if t.track != nil && t.track.RID() == rid {
   402  				return t.rtcpInterceptor.Read(b, a)
   403  			}
   404  		}
   405  		return 0, nil, fmt.Errorf("%w: %s", errRTPSenderNoTrackForRID, rid)
   406  	case <-r.stopCalled:
   407  		return 0, nil, io.ErrClosedPipe
   408  	}
   409  }
   410  
   411  // ReadSimulcastRTCP is a convenience method that wraps ReadSimulcast and unmarshal for you
   412  func (r *RTPSender) ReadSimulcastRTCP(rid string) ([]rtcp.Packet, interceptor.Attributes, error) {
   413  	b := make([]byte, r.api.settingEngine.getReceiveMTU())
   414  	i, attributes, err := r.ReadSimulcast(b, rid)
   415  	if err != nil {
   416  		return nil, nil, err
   417  	}
   418  
   419  	pkts, err := rtcp.Unmarshal(b[:i])
   420  	return pkts, attributes, err
   421  }
   422  
   423  // SetReadDeadline sets the deadline for the Read operation.
   424  // Setting to zero means no deadline.
   425  func (r *RTPSender) SetReadDeadline(t time.Time) error {
   426  	return r.trackEncodings[0].srtpStream.SetReadDeadline(t)
   427  }
   428  
   429  // SetReadDeadlineSimulcast sets the max amount of time the RTCP stream for a given rid will block before returning. 0 is forever.
   430  func (r *RTPSender) SetReadDeadlineSimulcast(deadline time.Time, rid string) error {
   431  	r.mu.RLock()
   432  	defer r.mu.RUnlock()
   433  
   434  	for _, t := range r.trackEncodings {
   435  		if t.track != nil && t.track.RID() == rid {
   436  			return t.srtpStream.SetReadDeadline(deadline)
   437  		}
   438  	}
   439  	return fmt.Errorf("%w: %s", errRTPSenderNoTrackForRID, rid)
   440  }
   441  
   442  // hasSent tells if data has been ever sent for this instance
   443  func (r *RTPSender) hasSent() bool {
   444  	select {
   445  	case <-r.sendCalled:
   446  		return true
   447  	default:
   448  		return false
   449  	}
   450  }
   451  
   452  // hasStopped tells if stop has been called
   453  func (r *RTPSender) hasStopped() bool {
   454  	select {
   455  	case <-r.stopCalled:
   456  		return true
   457  	default:
   458  		return false
   459  	}
   460  }