diff --git a/connclient.go b/connclient.go index 248d17d7..9779c74d 100644 --- a/connclient.go +++ b/connclient.go @@ -10,6 +10,7 @@ package gortsplib import ( "bufio" "fmt" + "math/rand" "net" "net/url" "strconv" @@ -381,6 +382,7 @@ type UDPReadFunc func([]byte) (int, error) // SetupUDP writes a SETUP request, that means that we want to read // a given track with the UDP transport. It then reads a Response. +// If rtpPort and rtcpPort are zero, they will be chosen automatically. func (c *ConnClient) SetupUDP(u *url.URL, track *Track, rtpPort int, rtcpPort int) (UDPReadFunc, UDPReadFunc, *Response, error) { if c.streamUrl != nil && *u != *c.streamUrl { @@ -395,14 +397,53 @@ func (c *ConnClient) SetupUDP(u *url.URL, track *Track, rtpPort int, return nil, nil, nil, fmt.Errorf("track has already been setup") } - rtpListener, err := newConnClientUDPListener(c, rtpPort, track.Id, StreamTypeRtp) - if err != nil { - return nil, nil, nil, err + if (rtpPort == 0 && rtcpPort != 0) || + (rtpPort != 0 && rtcpPort == 0) { + return nil, nil, nil, fmt.Errorf("rtpPort and rtcpPort must be both zero or non-zero") } - rtcpListener, err := newConnClientUDPListener(c, rtcpPort, track.Id, StreamTypeRtcp) + if rtpPort != 0 && rtcpPort != (rtpPort+1) { + return nil, nil, nil, fmt.Errorf("rtcpPort must be rtpPort + 1") + } + + rtpListener, rtcpListener, err := func() (*connClientUDPListener, *connClientUDPListener, error) { + if rtpPort != 0 { + rtpListener, err := newConnClientUDPListener(c, rtpPort, track.Id, StreamTypeRtp) + if err != nil { + return nil, nil, err + } + + rtcpListener, err := newConnClientUDPListener(c, rtcpPort, track.Id, StreamTypeRtcp) + if err != nil { + rtpListener.close() + return nil, nil, err + } + + return rtpListener, rtcpListener, nil + + } else { + for { + // choose two consecutive ports in range 65535-10000 + // rtp must be even and rtcp odd + rtpPort = (rand.Intn((65535-10000)/2) * 2) + 10000 + rtcpPort = rtpPort + 1 + + rtpListener, err := newConnClientUDPListener(c, rtpPort, track.Id, StreamTypeRtp) + if err != nil { + continue + } + + rtcpListener, err := newConnClientUDPListener(c, rtcpPort, track.Id, StreamTypeRtcp) + if err != nil { + rtpListener.close() + continue + } + + return rtpListener, rtcpListener, nil + } + } + }() if err != nil { - rtpListener.close() return nil, nil, nil, err } diff --git a/connclient_test.go b/connclient_test.go index 7ee18137..42ee44de 100644 --- a/connclient_test.go +++ b/connclient_test.go @@ -143,7 +143,7 @@ func TestConnClientUDP(t *testing.T) { var rtcpReads []UDPReadFunc for _, track := range tracks { - rtpRead, rtcpRead, _, err := conn.SetupUDP(u, track, 9000+track.Id*2, 9001+track.Id*2) + rtpRead, rtcpRead, _, err := conn.SetupUDP(u, track, 0, 0) require.NoError(t, err) rtpReads = append(rtpReads, rtpRead) diff --git a/examples/read-udp.go b/examples/read-udp.go index 673c53cb..359d6093 100644 --- a/examples/read-udp.go +++ b/examples/read-udp.go @@ -36,7 +36,7 @@ func main() { var rtcpReads []gortsplib.UDPReadFunc for _, track := range tracks { - rtpRead, rtcpRead, _, err := conn.SetupUDP(u, track, 9000+track.Id*2, 9001+track.Id*2) + rtpRead, rtcpRead, _, err := conn.SetupUDP(u, track, 0, 0) if err != nil { panic(err) }