package gortsplib import ( "net" "sync/atomic" "time" "github.com/pion/rtcp" "github.com/pion/rtp" "github.com/bluenviron/gortsplib/v4/pkg/description" "github.com/bluenviron/gortsplib/v4/pkg/liberrors" ) type clientMedia struct { c *Client onPacketRTCP OnPacketRTCPFunc media *description.Media formats map[uint8]*clientFormat tcpChannel int udpRTPListener *clientUDPListener udpRTCPListener *clientUDPListener writePacketRTPInQueue func([]byte) error writePacketRTCPInQueue func([]byte) error } func (cm *clientMedia) close() { if cm.udpRTPListener != nil { cm.udpRTPListener.close() cm.udpRTCPListener.close() } } func (cm *clientMedia) allocateUDPListeners( multicastEnable bool, multicastSourceIP net.IP, rtpAddress string, rtcpAddress string, ) error { if rtpAddress != ":0" { l1 := &clientUDPListener{ c: cm.c, multicastEnable: multicastEnable, multicastSourceIP: multicastSourceIP, address: rtpAddress, } err := l1.initialize() if err != nil { return err } l2 := &clientUDPListener{ c: cm.c, multicastEnable: multicastEnable, multicastSourceIP: multicastSourceIP, address: rtcpAddress, } err = l2.initialize() if err != nil { l1.close() return err } cm.udpRTPListener, cm.udpRTCPListener = l1, l2 return nil } var err error cm.udpRTPListener, cm.udpRTCPListener, err = allocateUDPListenerPair(cm.c) return err } func (cm *clientMedia) setMedia(medi *description.Media) { cm.media = medi cm.formats = make(map[uint8]*clientFormat) for _, forma := range medi.Formats { cm.formats[forma.PayloadType()] = &clientFormat{ cm: cm, format: forma, onPacketRTP: func(*rtp.Packet) {}, } } } func (cm *clientMedia) start() { if cm.udpRTPListener != nil { cm.writePacketRTPInQueue = cm.writePacketRTPInQueueUDP cm.writePacketRTCPInQueue = cm.writePacketRTCPInQueueUDP if cm.c.state == clientStateRecord || cm.media.IsBackChannel { cm.udpRTPListener.readFunc = cm.readRTPUDPRecord cm.udpRTCPListener.readFunc = cm.readRTCPUDPRecord } else { cm.udpRTPListener.readFunc = cm.readRTPUDPPlay cm.udpRTCPListener.readFunc = cm.readRTCPUDPPlay } } else { cm.writePacketRTPInQueue = cm.writePacketRTPInQueueTCP cm.writePacketRTCPInQueue = cm.writePacketRTCPInQueueTCP if cm.c.tcpCallbackByChannel == nil { cm.c.tcpCallbackByChannel = make(map[int]readFunc) } if cm.c.state == clientStateRecord || cm.media.IsBackChannel { cm.c.tcpCallbackByChannel[cm.tcpChannel] = cm.readRTPTCPRecord cm.c.tcpCallbackByChannel[cm.tcpChannel+1] = cm.readRTCPTCPRecord } else { cm.c.tcpCallbackByChannel[cm.tcpChannel] = cm.readRTPTCPPlay cm.c.tcpCallbackByChannel[cm.tcpChannel+1] = cm.readRTCPTCPPlay } } for _, ct := range cm.formats { ct.start() } if cm.udpRTPListener != nil { cm.udpRTPListener.start() cm.udpRTCPListener.start() } } func (cm *clientMedia) stop() { if cm.udpRTPListener != nil { cm.udpRTPListener.stop() cm.udpRTCPListener.stop() } for _, ct := range cm.formats { ct.stop() } } func (cm *clientMedia) findFormatWithSSRC(ssrc uint32) *clientFormat { for _, format := range cm.formats { tssrc, ok := format.rtcpReceiver.SenderSSRC() if ok && tssrc == ssrc { return format } } return nil } func (cm *clientMedia) writePacketRTPInQueueUDP(payload []byte) error { return cm.udpRTPListener.write(payload) } func (cm *clientMedia) writePacketRTCPInQueueUDP(payload []byte) error { return cm.udpRTCPListener.write(payload) } func (cm *clientMedia) writePacketRTPInQueueTCP(payload []byte) error { cm.c.tcpFrame.Channel = cm.tcpChannel cm.c.tcpFrame.Payload = payload cm.c.nconn.SetWriteDeadline(time.Now().Add(cm.c.WriteTimeout)) return cm.c.conn.WriteInterleavedFrame(cm.c.tcpFrame, cm.c.tcpBuffer) } func (cm *clientMedia) writePacketRTCPInQueueTCP(payload []byte) error { cm.c.tcpFrame.Channel = cm.tcpChannel + 1 cm.c.tcpFrame.Payload = payload cm.c.nconn.SetWriteDeadline(time.Now().Add(cm.c.WriteTimeout)) return cm.c.conn.WriteInterleavedFrame(cm.c.tcpFrame, cm.c.tcpBuffer) } func (cm *clientMedia) readRTPTCPPlay(payload []byte) bool { now := cm.c.timeNow() atomic.StoreInt64(cm.c.tcpLastFrameTime, now.Unix()) pkt := &rtp.Packet{} err := pkt.Unmarshal(payload) if err != nil { cm.c.OnDecodeError(err) return false } forma, ok := cm.formats[pkt.PayloadType] if !ok { cm.c.OnDecodeError(liberrors.ErrClientRTPPacketUnknownPayloadType{PayloadType: pkt.PayloadType}) return false } forma.readRTPTCP(pkt) return true } func (cm *clientMedia) readRTCPTCPPlay(payload []byte) bool { now := cm.c.timeNow() atomic.StoreInt64(cm.c.tcpLastFrameTime, now.Unix()) if len(payload) > udpMaxPayloadSize { cm.c.OnDecodeError(liberrors.ErrClientRTCPPacketTooBig{L: len(payload), Max: udpMaxPayloadSize}) return false } packets, err := rtcp.Unmarshal(payload) if err != nil { cm.c.OnDecodeError(err) return false } for _, pkt := range packets { if sr, ok := pkt.(*rtcp.SenderReport); ok { format := cm.findFormatWithSSRC(sr.SSRC) if format != nil { format.rtcpReceiver.ProcessSenderReport(sr, now) } } cm.onPacketRTCP(pkt) } return true } func (cm *clientMedia) readRTPTCPRecord(_ []byte) bool { return false } func (cm *clientMedia) readRTCPTCPRecord(payload []byte) bool { if len(payload) > udpMaxPayloadSize { cm.c.OnDecodeError(liberrors.ErrClientRTCPPacketTooBig{L: len(payload), Max: udpMaxPayloadSize}) return false } packets, err := rtcp.Unmarshal(payload) if err != nil { cm.c.OnDecodeError(err) return false } for _, pkt := range packets { cm.onPacketRTCP(pkt) } return true } func (cm *clientMedia) readRTPUDPPlay(payload []byte) bool { plen := len(payload) if plen == (udpMaxPayloadSize + 1) { cm.c.OnDecodeError(liberrors.ErrClientRTPPacketTooBigUDP{}) return false } pkt := &rtp.Packet{} err := pkt.Unmarshal(payload) if err != nil { cm.c.OnDecodeError(err) return false } forma, ok := cm.formats[pkt.PayloadType] if !ok { cm.c.OnDecodeError(liberrors.ErrClientRTPPacketUnknownPayloadType{PayloadType: pkt.PayloadType}) return false } forma.readRTPUDP(pkt) return true } func (cm *clientMedia) readRTCPUDPPlay(payload []byte) bool { now := cm.c.timeNow() plen := len(payload) if plen == (udpMaxPayloadSize + 1) { cm.c.OnDecodeError(liberrors.ErrClientRTCPPacketTooBigUDP{}) return false } packets, err := rtcp.Unmarshal(payload) if err != nil { cm.c.OnDecodeError(err) return false } for _, pkt := range packets { if sr, ok := pkt.(*rtcp.SenderReport); ok { format := cm.findFormatWithSSRC(sr.SSRC) if format != nil { format.rtcpReceiver.ProcessSenderReport(sr, now) } } cm.onPacketRTCP(pkt) } return true } func (cm *clientMedia) readRTPUDPRecord(_ []byte) bool { return false } func (cm *clientMedia) readRTCPUDPRecord(payload []byte) bool { plen := len(payload) if plen == (udpMaxPayloadSize + 1) { cm.c.OnDecodeError(liberrors.ErrClientRTCPPacketTooBigUDP{}) return false } packets, err := rtcp.Unmarshal(payload) if err != nil { cm.c.OnDecodeError(err) return false } for _, pkt := range packets { cm.onPacketRTCP(pkt) } return true }