mirror of
https://github.com/pion/webrtc.git
synced 2025-10-13 02:43:48 +08:00

When a local peer connection has a single transceiver with a sendrecv direction, and the remote has a transceiver with a recvonly direction, the local peer connection must change the transceiver direction to sendonly. When a local peer connection has a single transceiver with a recvonly direction, and the remote has a transceiver with a recvonly direction, the local peer connection must create another transceiver with a sendonly direction. A unit test is added to cover all possible cases.
188 lines
5.2 KiB
Go
188 lines
5.2 KiB
Go
// +build !js
|
|
|
|
package webrtc
|
|
|
|
import (
|
|
"fmt"
|
|
"sync/atomic"
|
|
|
|
"github.com/pion/rtp"
|
|
)
|
|
|
|
// RTPTransceiver represents a combination of an RTPSender and an RTPReceiver that share a common mid.
|
|
type RTPTransceiver struct {
|
|
mid atomic.Value // string
|
|
sender atomic.Value // *RTPSender
|
|
receiver atomic.Value // *RTPReceiver
|
|
direction atomic.Value // RTPTransceiverDirection
|
|
|
|
stopped bool
|
|
kind RTPCodecType
|
|
}
|
|
|
|
// Sender returns the RTPTransceiver's RTPSender if it has one
|
|
func (t *RTPTransceiver) Sender() *RTPSender {
|
|
if v := t.sender.Load(); v != nil {
|
|
return v.(*RTPSender)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetSender sets the RTPSender and Track to current transceiver
|
|
func (t *RTPTransceiver) SetSender(s *RTPSender, track TrackLocal) error {
|
|
t.setSender(s)
|
|
return t.setSendingTrack(track)
|
|
}
|
|
|
|
func (t *RTPTransceiver) setSender(s *RTPSender) {
|
|
t.sender.Store(s)
|
|
}
|
|
|
|
// Receiver returns the RTPTransceiver's RTPReceiver if it has one
|
|
func (t *RTPTransceiver) Receiver() *RTPReceiver {
|
|
if v := t.receiver.Load(); v != nil {
|
|
return v.(*RTPReceiver)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// setMid sets the RTPTransceiver's mid. If it was already set, will return an error.
|
|
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
|
|
}
|
|
|
|
// Mid gets the Transceiver's mid value. When not already set, this value will be set in CreateOffer or CreateAnswer.
|
|
func (t *RTPTransceiver) Mid() string {
|
|
if v := t.mid.Load(); v != nil {
|
|
return v.(string)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Kind returns RTPTransceiver's kind.
|
|
func (t *RTPTransceiver) Kind() RTPCodecType {
|
|
return t.kind
|
|
}
|
|
|
|
// Direction returns the RTPTransceiver's current direction
|
|
func (t *RTPTransceiver) Direction() RTPTransceiverDirection {
|
|
return t.direction.Load().(RTPTransceiverDirection)
|
|
}
|
|
|
|
// Stop irreversibly stops the RTPTransceiver
|
|
func (t *RTPTransceiver) Stop() error {
|
|
if t.Sender() != nil {
|
|
if err := t.Sender().Stop(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if t.Receiver() != nil {
|
|
if err := t.Receiver().Stop(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
t.setDirection(RTPTransceiverDirectionInactive)
|
|
return nil
|
|
}
|
|
|
|
func (t *RTPTransceiver) setReceiver(r *RTPReceiver) {
|
|
t.receiver.Store(r)
|
|
}
|
|
|
|
func (t *RTPTransceiver) setDirection(d RTPTransceiverDirection) {
|
|
t.direction.Store(d)
|
|
}
|
|
|
|
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:
|
|
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
|
|
}
|
|
|
|
// Given a direction+type pluck a transceiver from the passed list
|
|
// if no entry satisfies the requested type+direction return a inactive Transceiver
|
|
func satisfyTypeAndDirection(remoteKind RTPCodecType, remoteDirection RTPTransceiverDirection, localTransceivers []*RTPTransceiver) (*RTPTransceiver, []*RTPTransceiver) {
|
|
// Get direction order from most preferred to least
|
|
getPreferredDirections := func() []RTPTransceiverDirection {
|
|
switch remoteDirection {
|
|
case RTPTransceiverDirectionSendrecv:
|
|
return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendrecv}
|
|
case RTPTransceiverDirectionSendonly:
|
|
return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}
|
|
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
|
|
}
|
|
|
|
// handleUnknownRTPPacket consumes a single RTP Packet and returns information that is helpful
|
|
// for demuxing and handling an unknown SSRC (usually for Simulcast)
|
|
func handleUnknownRTPPacket(buf []byte, midExtensionID, streamIDExtensionID uint8) (mid, rid string, payloadType PayloadType, err error) {
|
|
rp := &rtp.Packet{}
|
|
if err = rp.Unmarshal(buf); err != nil {
|
|
return
|
|
}
|
|
|
|
if !rp.Header.Extension {
|
|
return
|
|
}
|
|
|
|
payloadType = PayloadType(rp.PayloadType)
|
|
if payload := rp.GetExtension(midExtensionID); payload != nil {
|
|
mid = string(payload)
|
|
}
|
|
|
|
if payload := rp.GetExtension(streamIDExtensionID); payload != nil {
|
|
rid = string(payload)
|
|
}
|
|
|
|
return
|
|
}
|