mirror of
				https://github.com/aler9/gortsplib
				synced 2025-10-31 18:42:40 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			228 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package gortsplib
 | |
| 
 | |
| import (
 | |
| 	"crypto/rand"
 | |
| 	"net"
 | |
| 	"strconv"
 | |
| 	"sync/atomic"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/pion/rtcp"
 | |
| 	"golang.org/x/net/ipv4"
 | |
| )
 | |
| 
 | |
| func randUint32() uint32 {
 | |
| 	var b [4]byte
 | |
| 	rand.Read(b[:])
 | |
| 	return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
 | |
| }
 | |
| 
 | |
| func randIntn(n int) int {
 | |
| 	return int(randUint32() & (uint32(n) - 1))
 | |
| }
 | |
| 
 | |
| type clientUDPListener struct {
 | |
| 	c               *Client
 | |
| 	pc              *net.UDPConn
 | |
| 	remoteReadIP    net.IP
 | |
| 	remoteReadPort  int
 | |
| 	remoteWriteAddr *net.UDPAddr
 | |
| 	trackID         int
 | |
| 	isRTP           bool
 | |
| 	running         bool
 | |
| 	rtpPacketBuffer *rtpPacketMultiBuffer
 | |
| 	lastPacketTime  *int64
 | |
| 	processFunc     func(time.Time, []byte)
 | |
| 
 | |
| 	readerDone chan struct{}
 | |
| }
 | |
| 
 | |
| func newClientUDPListenerPair(c *Client) (*clientUDPListener, *clientUDPListener) {
 | |
| 	// choose two consecutive ports in range 65535-10000
 | |
| 	// RTP port must be even and RTCP port odd
 | |
| 	for {
 | |
| 		rtpPort := (randIntn((65535-10000)/2) * 2) + 10000
 | |
| 		rtpListener, err := newClientUDPListener(c, false, ":"+strconv.FormatInt(int64(rtpPort), 10))
 | |
| 		if err != nil {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		rtcpPort := rtpPort + 1
 | |
| 		rtcpListener, err := newClientUDPListener(c, false, ":"+strconv.FormatInt(int64(rtcpPort), 10))
 | |
| 		if err != nil {
 | |
| 			rtpListener.close()
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		return rtpListener, rtcpListener
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newClientUDPListener(c *Client, multicast bool, address string) (*clientUDPListener, error) {
 | |
| 	var pc *net.UDPConn
 | |
| 	if multicast {
 | |
| 		host, port, err := net.SplitHostPort(address)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		tmp, err := c.ListenPacket("udp", "224.0.0.0:"+port)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		p := ipv4.NewPacketConn(tmp)
 | |
| 
 | |
| 		err = p.SetMulticastTTL(multicastTTL)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		intfs, err := net.Interfaces()
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		for _, intf := range intfs {
 | |
| 			err := p.JoinGroup(&intf, &net.UDPAddr{IP: net.ParseIP(host)})
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		pc = tmp.(*net.UDPConn)
 | |
| 	} else {
 | |
| 		tmp, err := c.ListenPacket("udp", address)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		pc = tmp.(*net.UDPConn)
 | |
| 	}
 | |
| 
 | |
| 	err := pc.SetReadBuffer(udpKernelReadBufferSize)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return &clientUDPListener{
 | |
| 		c:               c,
 | |
| 		pc:              pc,
 | |
| 		rtpPacketBuffer: newRTPPacketMultiBuffer(uint64(c.ReadBufferCount)),
 | |
| 		lastPacketTime: func() *int64 {
 | |
| 			v := int64(0)
 | |
| 			return &v
 | |
| 		}(),
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (u *clientUDPListener) close() {
 | |
| 	if u.running {
 | |
| 		u.stop()
 | |
| 	}
 | |
| 	u.pc.Close()
 | |
| }
 | |
| 
 | |
| func (u *clientUDPListener) port() int {
 | |
| 	return u.pc.LocalAddr().(*net.UDPAddr).Port
 | |
| }
 | |
| 
 | |
| func (u *clientUDPListener) start(forPlay bool) {
 | |
| 	if forPlay {
 | |
| 		if u.isRTP {
 | |
| 			u.processFunc = u.processPlayRTP
 | |
| 		} else {
 | |
| 			u.processFunc = u.processPlayRTCP
 | |
| 		}
 | |
| 	} else {
 | |
| 		u.processFunc = u.processRecordRTCP
 | |
| 	}
 | |
| 
 | |
| 	u.running = true
 | |
| 	u.pc.SetReadDeadline(time.Time{})
 | |
| 	u.readerDone = make(chan struct{})
 | |
| 	go u.runReader()
 | |
| }
 | |
| 
 | |
| func (u *clientUDPListener) stop() {
 | |
| 	u.pc.SetReadDeadline(time.Now())
 | |
| 	<-u.readerDone
 | |
| }
 | |
| 
 | |
| func (u *clientUDPListener) runReader() {
 | |
| 	defer close(u.readerDone)
 | |
| 
 | |
| 	for {
 | |
| 		buf := make([]byte, maxPacketSize)
 | |
| 		n, addr, err := u.pc.ReadFrom(buf)
 | |
| 		if err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		uaddr := addr.(*net.UDPAddr)
 | |
| 
 | |
| 		if !u.remoteReadIP.Equal(uaddr.IP) || (!u.c.AnyPortEnable && u.remoteReadPort != uaddr.Port) {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		now := time.Now()
 | |
| 		atomic.StoreInt64(u.lastPacketTime, now.Unix())
 | |
| 
 | |
| 		u.processFunc(now, buf[:n])
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (u *clientUDPListener) processPlayRTP(now time.Time, payload []byte) {
 | |
| 	pkt := u.rtpPacketBuffer.next()
 | |
| 	err := pkt.Unmarshal(payload)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	ctx := ClientOnPacketRTPCtx{
 | |
| 		TrackID: u.trackID,
 | |
| 		Packet:  pkt,
 | |
| 	}
 | |
| 	ct := u.c.tracks[u.trackID]
 | |
| 	u.c.processPacketRTP(ct, &ctx)
 | |
| 	ct.rtcpReceiver.ProcessPacketRTP(time.Now(), pkt, ctx.PTSEqualsDTS)
 | |
| 	u.c.OnPacketRTP(&ctx)
 | |
| }
 | |
| 
 | |
| func (u *clientUDPListener) processPlayRTCP(now time.Time, payload []byte) {
 | |
| 	packets, err := rtcp.Unmarshal(payload)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	for _, pkt := range packets {
 | |
| 		u.c.tracks[u.trackID].rtcpReceiver.ProcessPacketRTCP(now, pkt)
 | |
| 		u.c.OnPacketRTCP(&ClientOnPacketRTCPCtx{
 | |
| 			TrackID: u.trackID,
 | |
| 			Packet:  pkt,
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (u *clientUDPListener) processRecordRTCP(now time.Time, payload []byte) {
 | |
| 	packets, err := rtcp.Unmarshal(payload)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	for _, pkt := range packets {
 | |
| 		u.c.OnPacketRTCP(&ClientOnPacketRTCPCtx{
 | |
| 			TrackID: u.trackID,
 | |
| 			Packet:  pkt,
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (u *clientUDPListener) write(payload []byte) error {
 | |
| 	// no mutex is needed here since Write() has an internal lock.
 | |
| 	// https://github.com/golang/go/issues/27203#issuecomment-534386117
 | |
| 
 | |
| 	u.pc.SetWriteDeadline(time.Now().Add(u.c.WriteTimeout))
 | |
| 	_, err := u.pc.WriteTo(payload, u.remoteWriteAddr)
 | |
| 	return err
 | |
| }
 | 
