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 }