2024-05-15 23:15:00 +00:00
|
|
|
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
2022-03-10 09:44:48 +00:00
|
|
|
//go:build !js
|
|
|
|
// +build !js
|
|
|
|
|
|
|
|
package webrtc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/pion/interceptor"
|
|
|
|
"github.com/pion/rtp"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TrackRemote represents a single inbound source of media
|
|
|
|
type TrackRemote struct {
|
|
|
|
mu sync.RWMutex
|
|
|
|
|
|
|
|
id string
|
|
|
|
streamID string
|
|
|
|
|
|
|
|
payloadType PayloadType
|
|
|
|
kind RTPCodecType
|
|
|
|
ssrc SSRC
|
2024-06-05 20:10:03 +00:00
|
|
|
rtxSsrc SSRC
|
2022-03-10 09:44:48 +00:00
|
|
|
codec RTPCodecParameters
|
|
|
|
params RTPParameters
|
|
|
|
rid string
|
|
|
|
|
|
|
|
receiver *RTPReceiver
|
|
|
|
peeked []byte
|
|
|
|
peekedAttributes interceptor.Attributes
|
|
|
|
}
|
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
func newTrackRemote(kind RTPCodecType, ssrc, rtxSsrc SSRC, rid string, receiver *RTPReceiver) *TrackRemote {
|
2022-03-10 09:44:48 +00:00
|
|
|
return &TrackRemote{
|
|
|
|
kind: kind,
|
|
|
|
ssrc: ssrc,
|
2024-06-05 20:10:03 +00:00
|
|
|
rtxSsrc: rtxSsrc,
|
2022-03-10 09:44:48 +00:00
|
|
|
rid: rid,
|
|
|
|
receiver: receiver,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ID is the unique identifier for this Track. This should be unique for the
|
|
|
|
// stream, but doesn't have to globally unique. A common example would be 'audio' or 'video'
|
|
|
|
// and StreamID would be 'desktop' or 'webcam'
|
|
|
|
func (t *TrackRemote) ID() string {
|
|
|
|
t.mu.RLock()
|
|
|
|
defer t.mu.RUnlock()
|
|
|
|
return t.id
|
|
|
|
}
|
|
|
|
|
|
|
|
// RID gets the RTP Stream ID of this Track
|
|
|
|
// With Simulcast you will have multiple tracks with the same ID, but different RID values.
|
|
|
|
// In many cases a TrackRemote will not have an RID, so it is important to assert it is non-zero
|
|
|
|
func (t *TrackRemote) RID() string {
|
|
|
|
t.mu.RLock()
|
|
|
|
defer t.mu.RUnlock()
|
|
|
|
|
|
|
|
return t.rid
|
|
|
|
}
|
|
|
|
|
|
|
|
// PayloadType gets the PayloadType of the track
|
|
|
|
func (t *TrackRemote) PayloadType() PayloadType {
|
|
|
|
t.mu.RLock()
|
|
|
|
defer t.mu.RUnlock()
|
|
|
|
return t.payloadType
|
|
|
|
}
|
|
|
|
|
|
|
|
// Kind gets the Kind of the track
|
|
|
|
func (t *TrackRemote) Kind() RTPCodecType {
|
|
|
|
t.mu.RLock()
|
|
|
|
defer t.mu.RUnlock()
|
|
|
|
return t.kind
|
|
|
|
}
|
|
|
|
|
|
|
|
// StreamID is the group this track belongs too. This must be unique
|
|
|
|
func (t *TrackRemote) StreamID() string {
|
|
|
|
t.mu.RLock()
|
|
|
|
defer t.mu.RUnlock()
|
|
|
|
return t.streamID
|
|
|
|
}
|
|
|
|
|
|
|
|
// SSRC gets the SSRC of the track
|
|
|
|
func (t *TrackRemote) SSRC() SSRC {
|
|
|
|
t.mu.RLock()
|
|
|
|
defer t.mu.RUnlock()
|
|
|
|
return t.ssrc
|
|
|
|
}
|
|
|
|
|
|
|
|
// Msid gets the Msid of the track
|
|
|
|
func (t *TrackRemote) Msid() string {
|
|
|
|
return t.StreamID() + " " + t.ID()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Codec gets the Codec of the track
|
|
|
|
func (t *TrackRemote) Codec() RTPCodecParameters {
|
|
|
|
t.mu.RLock()
|
|
|
|
defer t.mu.RUnlock()
|
|
|
|
return t.codec
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read reads data from the track.
|
|
|
|
func (t *TrackRemote) Read(b []byte) (n int, attributes interceptor.Attributes, err error) {
|
|
|
|
t.mu.RLock()
|
|
|
|
r := t.receiver
|
|
|
|
peeked := t.peeked != nil
|
|
|
|
t.mu.RUnlock()
|
|
|
|
|
|
|
|
if peeked {
|
|
|
|
t.mu.Lock()
|
|
|
|
data := t.peeked
|
|
|
|
attributes = t.peekedAttributes
|
|
|
|
|
|
|
|
t.peeked = nil
|
|
|
|
t.peekedAttributes = nil
|
|
|
|
t.mu.Unlock()
|
|
|
|
// someone else may have stolen our packet when we
|
|
|
|
// released the lock. Deal with it.
|
|
|
|
if data != nil {
|
|
|
|
n = copy(b, data)
|
|
|
|
err = t.checkAndUpdateTrack(b)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
// If there's a separate RTX track and an RTX packet is available, return that
|
|
|
|
if rtxPacketReceived := r.readRTX(t); rtxPacketReceived != nil {
|
|
|
|
n = copy(b, rtxPacketReceived.pkt)
|
|
|
|
attributes = rtxPacketReceived.attributes
|
|
|
|
rtxPacketReceived.release()
|
|
|
|
err = nil
|
|
|
|
} else {
|
|
|
|
// If there's no separate RTX track (or there's a separate RTX track but no RTX packet waiting), wait for and return
|
|
|
|
// a packet from the main track
|
|
|
|
n, attributes, err = r.readRTP(b, t)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = t.checkAndUpdateTrack(b)
|
2022-03-10 09:44:48 +00:00
|
|
|
}
|
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
return n, attributes, err
|
2022-03-10 09:44:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// checkAndUpdateTrack checks payloadType for every incoming packet
|
|
|
|
// once a different payloadType is detected the track will be updated
|
|
|
|
func (t *TrackRemote) checkAndUpdateTrack(b []byte) error {
|
|
|
|
if len(b) < 2 {
|
|
|
|
return errRTPTooShort
|
|
|
|
}
|
|
|
|
|
2024-10-10 11:33:36 +00:00
|
|
|
payloadType := PayloadType(b[1] & rtpPayloadTypeBitmask)
|
|
|
|
if payloadType != t.PayloadType() || len(t.params.Codecs) == 0 {
|
2022-03-10 09:44:48 +00:00
|
|
|
t.mu.Lock()
|
|
|
|
defer t.mu.Unlock()
|
|
|
|
|
|
|
|
params, err := t.receiver.api.mediaEngine.getRTPParametersByPayloadType(payloadType)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
t.kind = t.receiver.kind
|
|
|
|
t.payloadType = payloadType
|
|
|
|
t.codec = params.Codecs[0]
|
|
|
|
t.params = params
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadRTP is a convenience method that wraps Read and unmarshals for you.
|
|
|
|
func (t *TrackRemote) ReadRTP() (*rtp.Packet, interceptor.Attributes, error) {
|
|
|
|
b := make([]byte, t.receiver.api.settingEngine.getReceiveMTU())
|
|
|
|
i, attributes, err := t.Read(b)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
r := &rtp.Packet{}
|
|
|
|
if err := r.Unmarshal(b[:i]); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return r, attributes, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// peek is like Read, but it doesn't discard the packet read
|
|
|
|
func (t *TrackRemote) peek(b []byte) (n int, a interceptor.Attributes, err error) {
|
|
|
|
n, a, err = t.Read(b)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
t.mu.Lock()
|
|
|
|
// this might overwrite data if somebody peeked between the Read
|
|
|
|
// and us getting the lock. Oh well, we'll just drop a packet in
|
|
|
|
// that case.
|
|
|
|
data := make([]byte, n)
|
|
|
|
n = copy(data, b[:n])
|
|
|
|
t.peeked = data
|
|
|
|
t.peekedAttributes = a
|
|
|
|
t.mu.Unlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetReadDeadline sets the max amount of time the RTP stream will block before returning. 0 is forever.
|
|
|
|
func (t *TrackRemote) SetReadDeadline(deadline time.Time) error {
|
|
|
|
return t.receiver.setRTPReadDeadline(deadline, t)
|
|
|
|
}
|
2024-06-05 20:10:03 +00:00
|
|
|
|
|
|
|
// RtxSSRC returns the RTX SSRC for a track, or 0 if track does not have a separate RTX stream
|
|
|
|
func (t *TrackRemote) RtxSSRC() SSRC {
|
|
|
|
t.mu.RLock()
|
|
|
|
defer t.mu.RUnlock()
|
|
|
|
return t.rtxSsrc
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasRTX returns true if the track has a separate RTX stream
|
|
|
|
func (t *TrackRemote) HasRTX() bool {
|
|
|
|
t.mu.RLock()
|
|
|
|
defer t.mu.RUnlock()
|
|
|
|
return t.rtxSsrc != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TrackRemote) setRtxSSRC(ssrc SSRC) {
|
|
|
|
t.mu.Lock()
|
|
|
|
defer t.mu.Unlock()
|
|
|
|
t.rtxSsrc = ssrc
|
|
|
|
}
|