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 }