package webrtc
import (
"fmt"
"sync"
"sync/atomic"
"github.com/pion/rtp"
)
type RTPTransceiver struct {
mid atomic .Value
sender atomic .Value
receiver atomic .Value
direction atomic .Value
currentDirection atomic .Value
codecs []RTPCodecParameters
kind RTPCodecType
api *API
mu sync .RWMutex
}
func newRTPTransceiver(
receiver *RTPReceiver ,
sender *RTPSender ,
direction RTPTransceiverDirection ,
kind RTPCodecType ,
api *API ,
) *RTPTransceiver {
t := &RTPTransceiver {kind : kind , api : api }
t .setReceiver (receiver )
t .setSender (sender )
t .setDirection (direction )
t .setCurrentDirection (RTPTransceiverDirectionUnknown )
return t
}
func (t *RTPTransceiver ) SetCodecPreferences (codecs []RTPCodecParameters ) error {
t .mu .Lock ()
defer t .mu .Unlock ()
for _ , codec := range codecs {
if _ , matchType := codecParametersFuzzySearch (
codec , t .api .mediaEngine .getCodecsByKind (t .kind ),
); matchType == codecMatchNone {
return fmt .Errorf ("%w %s" , errRTPTransceiverCodecUnsupported , codec .MimeType )
}
}
t .codecs = codecs
return nil
}
func (t *RTPTransceiver ) getCodecs () []RTPCodecParameters {
t .mu .RLock ()
defer t .mu .RUnlock ()
mediaEngineCodecs := t .api .mediaEngine .getCodecsByKind (t .kind )
if len (t .codecs ) == 0 {
return mediaEngineCodecs
}
filteredCodecs := []RTPCodecParameters {}
for _ , codec := range t .codecs {
if c , matchType := codecParametersFuzzySearch (codec , mediaEngineCodecs ); matchType != codecMatchNone {
if codec .PayloadType == 0 {
codec .PayloadType = c .PayloadType
}
codec .RTCPFeedback = rtcpFeedbackIntersection (codec .RTCPFeedback , c .RTCPFeedback )
filteredCodecs = append (filteredCodecs , codec )
}
}
return filteredCodecs
}
func (t *RTPTransceiver ) Sender () *RTPSender {
if v , ok := t .sender .Load ().(*RTPSender ); ok {
return v
}
return nil
}
func (t *RTPTransceiver ) SetSender (s *RTPSender , track TrackLocal ) error {
t .setSender (s )
return t .setSendingTrack (track )
}
func (t *RTPTransceiver ) setSender (s *RTPSender ) {
if s != nil {
s .setRTPTransceiver (t )
}
if prevSender := t .Sender (); prevSender != nil {
prevSender .setRTPTransceiver (nil )
}
t .sender .Store (s )
}
func (t *RTPTransceiver ) Receiver () *RTPReceiver {
if v , ok := t .receiver .Load ().(*RTPReceiver ); ok {
return v
}
return nil
}
func (t *RTPTransceiver ) SetMid (mid string ) error {
if currentMid := t .Mid (); currentMid != "" {
return fmt .Errorf ("%w: %s to %s" , errRTPTransceiverCannotChangeMid , currentMid , mid )
}
t .mid .Store (mid )
return nil
}
func (t *RTPTransceiver ) Mid () string {
if v , ok := t .mid .Load ().(string ); ok {
return v
}
return ""
}
func (t *RTPTransceiver ) Kind () RTPCodecType {
return t .kind
}
func (t *RTPTransceiver ) Direction () RTPTransceiverDirection {
if direction , ok := t .direction .Load ().(RTPTransceiverDirection ); ok {
return direction
}
return RTPTransceiverDirection (0 )
}
func (t *RTPTransceiver ) Stop () error {
if sender := t .Sender (); sender != nil {
if err := sender .Stop (); err != nil {
return err
}
}
if receiver := t .Receiver (); receiver != nil {
if err := receiver .Stop (); err != nil {
return err
}
}
t .setDirection (RTPTransceiverDirectionInactive )
t .setCurrentDirection (RTPTransceiverDirectionInactive )
return nil
}
func (t *RTPTransceiver ) setReceiver (r *RTPReceiver ) {
if r != nil {
r .setRTPTransceiver (t )
}
if prevReceiver := t .Receiver (); prevReceiver != nil {
prevReceiver .setRTPTransceiver (nil )
}
t .receiver .Store (r )
}
func (t *RTPTransceiver ) setDirection (d RTPTransceiverDirection ) {
t .direction .Store (d )
}
func (t *RTPTransceiver ) setCurrentDirection (d RTPTransceiverDirection ) {
t .currentDirection .Store (d )
}
func (t *RTPTransceiver ) getCurrentDirection () RTPTransceiverDirection {
if v , ok := t .currentDirection .Load ().(RTPTransceiverDirection ); ok {
return v
}
return RTPTransceiverDirectionUnknown
}
func (t *RTPTransceiver ) setSendingTrack (track TrackLocal ) error {
if err := t .Sender ().ReplaceTrack (track ); err != nil {
return err
}
if track == nil {
t .setSender (nil )
}
switch {
case track != nil && t .Direction () == RTPTransceiverDirectionRecvonly :
t .setDirection (RTPTransceiverDirectionSendrecv )
case track != nil && t .Direction () == RTPTransceiverDirectionInactive :
t .setDirection (RTPTransceiverDirectionSendonly )
case track == nil && t .Direction () == RTPTransceiverDirectionSendrecv :
t .setDirection (RTPTransceiverDirectionRecvonly )
case track != nil && t .Direction () == RTPTransceiverDirectionSendonly :
case track != nil && t .Direction () == RTPTransceiverDirectionSendrecv :
case track == nil && t .Direction () == RTPTransceiverDirectionSendonly :
t .setDirection (RTPTransceiverDirectionInactive )
default :
return errRTPTransceiverSetSendingInvalidState
}
return nil
}
func findByMid(mid string , localTransceivers []*RTPTransceiver ) (*RTPTransceiver , []*RTPTransceiver ) {
for i , t := range localTransceivers {
if t .Mid () == mid {
return t , append (localTransceivers [:i ], localTransceivers [i +1 :]...)
}
}
return nil , localTransceivers
}
func satisfyTypeAndDirection(
remoteKind RTPCodecType ,
remoteDirection RTPTransceiverDirection ,
localTransceivers []*RTPTransceiver ,
) (*RTPTransceiver , []*RTPTransceiver ) {
getPreferredDirections := func () []RTPTransceiverDirection {
switch remoteDirection {
case RTPTransceiverDirectionSendrecv :
return []RTPTransceiverDirection {
RTPTransceiverDirectionRecvonly ,
RTPTransceiverDirectionSendrecv ,
RTPTransceiverDirectionSendonly ,
}
case RTPTransceiverDirectionSendonly :
return []RTPTransceiverDirection {RTPTransceiverDirectionRecvonly , RTPTransceiverDirectionSendrecv }
case RTPTransceiverDirectionRecvonly :
return []RTPTransceiverDirection {RTPTransceiverDirectionSendonly , RTPTransceiverDirectionSendrecv }
default :
return []RTPTransceiverDirection {}
}
}
for _ , possibleDirection := range getPreferredDirections () {
for i := range localTransceivers {
t := localTransceivers [i ]
if t .Mid () == "" && t .kind == remoteKind && possibleDirection == t .Direction () {
return t , append (localTransceivers [:i ], localTransceivers [i +1 :]...)
}
}
}
return nil , localTransceivers
}
func handleUnknownRTPPacket(
buf []byte ,
midExtensionID ,
streamIDExtensionID ,
repairStreamIDExtensionID uint8 ,
mid , rid , rsid *string ,
) (payloadType PayloadType , paddingOnly bool , err error ) {
rp := &rtp .Packet {}
if err = rp .Unmarshal (buf ); err != nil {
return 0 , false , err
}
if rp .Padding && len (rp .Payload ) == 0 {
paddingOnly = true
}
if !rp .Header .Extension {
return payloadType , paddingOnly , nil
}
payloadType = PayloadType (rp .PayloadType )
if payload := rp .GetExtension (midExtensionID ); payload != nil {
*mid = string (payload )
}
if payload := rp .GetExtension (streamIDExtensionID ); payload != nil {
*rid = string (payload )
}
if payload := rp .GetExtension (repairStreamIDExtensionID ); payload != nil {
*rsid = string (payload )
}
return payloadType , paddingOnly , nil
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .