diff --git a/connclient.go b/connclient.go index 18dd4236..639441e0 100644 --- a/connclient.go +++ b/connclient.go @@ -737,7 +737,15 @@ func (c *ConnClient) Play(u *url.URL) (*Response, error) { // LoopUDP must be called after SetupUDP() and Play(); it keeps // the TCP connection open with keepalives, and returns when the TCP // connection closes. -func (c *ConnClient) LoopUDP(u *url.URL) error { +func (c *ConnClient) LoopUDP() error { + if c.state != connClientStateReading { + return fmt.Errorf("can be called only after a successful Play()") + } + + if *c.streamProtocol != StreamProtocolUDP { + return fmt.Errorf("stream protocol is not UDP") + } + readDone := make(chan error) go func() { for { @@ -767,8 +775,8 @@ func (c *ConnClient) LoopUDP(u *url.URL) error { Method: OPTIONS, Url: &url.URL{ Scheme: "rtsp", - Host: u.Host, - User: u.User, + Host: c.streamUrl.Host, + User: c.streamUrl.User, Path: "/", }, SkipResponse: true, diff --git a/connclientdial.go b/connclientdial.go new file mode 100644 index 00000000..20f67556 --- /dev/null +++ b/connclientdial.go @@ -0,0 +1,108 @@ +package gortsplib + +import ( + "net/url" +) + +// DialRead connects to the address and starts reading all tracks. +func DialRead(address string, proto StreamProtocol) (*ConnClient, Tracks, error) { + u, err := url.Parse(address) + if err != nil { + return nil, nil, err + } + + conn, err := NewConnClient(ConnClientConf{Host: u.Host}) + if err != nil { + return nil, nil, err + } + + _, err = conn.Options(u) + if err != nil { + conn.Close() + return nil, nil, err + } + + tracks, _, err := conn.Describe(u) + if err != nil { + conn.Close() + return nil, nil, err + } + + if proto == StreamProtocolUDP { + for _, track := range tracks { + _, err := conn.SetupUDP(u, SetupModePlay, track, 0, 0) + if err != nil { + return nil, nil, err + } + } + + } else { + for _, track := range tracks { + _, err := conn.SetupTCP(u, SetupModePlay, track) + if err != nil { + conn.Close() + return nil, nil, err + } + } + } + + _, err = conn.Play(u) + if err != nil { + conn.Close() + return nil, nil, err + } + + return conn, tracks, nil +} + +// DialPublish connects to the address and starts publishing the tracks. +func DialPublish(address string, proto StreamProtocol, tracks Tracks) (*ConnClient, error) { + u, err := url.Parse(address) + if err != nil { + return nil, err + } + + conn, err := NewConnClient(ConnClientConf{Host: u.Host}) + if err != nil { + return nil, err + } + + _, err = conn.Options(u) + if err != nil { + conn.Close() + return nil, err + } + + _, err = conn.Announce(u, tracks) + if err != nil { + conn.Close() + return nil, err + } + + if proto == StreamProtocolUDP { + for _, track := range tracks { + _, err = conn.SetupUDP(u, SetupModeRecord, track, 0, 0) + if err != nil { + conn.Close() + return nil, err + } + } + + } else { + for _, track := range tracks { + _, err = conn.SetupTCP(u, SetupModeRecord, track) + if err != nil { + conn.Close() + return nil, err + } + } + } + + _, err = conn.Record(u) + if err != nil { + conn.Close() + return nil, err + } + + return conn, nil +} diff --git a/connclient_test.go b/connclientdial_test.go similarity index 76% rename from connclient_test.go rename to connclientdial_test.go index c4efc3fb..4d378273 100644 --- a/connclient_test.go +++ b/connclientdial_test.go @@ -3,7 +3,6 @@ package gortsplib import ( "fmt" "net" - "net/url" "os" "os/exec" "strconv" @@ -59,108 +58,6 @@ func (c *container) wait() int { return int(code) } -func TestConnClientReadUDP(t *testing.T) { - cnt1, err := newContainer("rtsp-simple-server", "server", []string{}) - require.NoError(t, err) - defer cnt1.close() - - time.Sleep(1 * time.Second) - - cnt2, err := newContainer("ffmpeg", "publish", []string{ - "-re", - "-stream_loop", "-1", - "-i", "/emptyvideo.ts", - "-c", "copy", - "-f", "rtsp", - "-rtsp_transport", "udp", - "rtsp://localhost:8554/teststream", - }) - require.NoError(t, err) - defer cnt2.close() - - time.Sleep(1 * time.Second) - - u, err := url.Parse("rtsp://localhost:8554/teststream") - require.NoError(t, err) - - conn, err := NewConnClient(ConnClientConf{Host: u.Host}) - require.NoError(t, err) - defer conn.Close() - - _, err = conn.Options(u) - require.NoError(t, err) - - tracks, _, err := conn.Describe(u) - require.NoError(t, err) - - for _, track := range tracks { - _, err := conn.SetupUDP(u, SetupModePlay, track, 0, 0) - require.NoError(t, err) - } - - _, err = conn.Play(u) - require.NoError(t, err) - - loopDone := make(chan struct{}) - defer func() { <-loopDone }() - - go func() { - defer close(loopDone) - conn.LoopUDP(u) - }() - - _, err = conn.ReadFrameUDP(tracks[0], StreamTypeRtp) - require.NoError(t, err) - - conn.CloseUDPListeners() -} - -func TestConnClientReadTCP(t *testing.T) { - cnt1, err := newContainer("rtsp-simple-server", "server", []string{}) - require.NoError(t, err) - defer cnt1.close() - - time.Sleep(1 * time.Second) - - cnt2, err := newContainer("ffmpeg", "publish", []string{ - "-re", - "-stream_loop", "-1", - "-i", "/emptyvideo.ts", - "-c", "copy", - "-f", "rtsp", - "-rtsp_transport", "udp", - "rtsp://localhost:8554/teststream", - }) - require.NoError(t, err) - defer cnt2.close() - - time.Sleep(1 * time.Second) - - u, err := url.Parse("rtsp://localhost:8554/teststream") - require.NoError(t, err) - - conn, err := NewConnClient(ConnClientConf{Host: u.Host}) - require.NoError(t, err) - defer conn.Close() - - _, err = conn.Options(u) - require.NoError(t, err) - - tracks, _, err := conn.Describe(u) - require.NoError(t, err) - - for _, track := range tracks { - _, err := conn.SetupTCP(u, SetupModePlay, track) - require.NoError(t, err) - } - - _, err = conn.Play(u) - require.NoError(t, err) - - _, err = conn.ReadFrameTCP() - require.NoError(t, err) -} - func getH264SPSandPPS(pc net.PacketConn) ([]byte, []byte, error) { var sps []byte var pps []byte @@ -201,22 +98,88 @@ func getH264SPSandPPS(pc net.PacketConn) ([]byte, []byte, error) { } } -func TestConnClientPublishUDP(t *testing.T) { +func TestConnClientDialReadUDP(t *testing.T) { cnt1, err := newContainer("rtsp-simple-server", "server", []string{}) require.NoError(t, err) defer cnt1.close() time.Sleep(1 * time.Second) - u, err := url.Parse("rtsp://localhost:8554/teststream") + cnt2, err := newContainer("ffmpeg", "publish", []string{ + "-re", + "-stream_loop", "-1", + "-i", "/emptyvideo.ts", + "-c", "copy", + "-f", "rtsp", + "-rtsp_transport", "udp", + "rtsp://localhost:8554/teststream", + }) + require.NoError(t, err) + defer cnt2.close() + + time.Sleep(1 * time.Second) + + conn, tracks, err := DialRead("rtsp://localhost:8554/teststream", StreamProtocolUDP) + require.NoError(t, err) + defer conn.Close() + + loopDone := make(chan struct{}) + defer func() { <-loopDone }() + + go func() { + defer close(loopDone) + conn.LoopUDP() + }() + + _, err = conn.ReadFrameUDP(tracks[0], StreamTypeRtp) require.NoError(t, err) - conn, err := NewConnClient(ConnClientConf{Host: u.Host}) + conn.CloseUDPListeners() +} + +func TestConnClientDialReadTCP(t *testing.T) { + cnt1, err := newContainer("rtsp-simple-server", "server", []string{}) require.NoError(t, err) + defer cnt1.close() + + time.Sleep(1 * time.Second) + + cnt2, err := newContainer("ffmpeg", "publish", []string{ + "-re", + "-stream_loop", "-1", + "-i", "/emptyvideo.ts", + "-c", "copy", + "-f", "rtsp", + "-rtsp_transport", "udp", + "rtsp://localhost:8554/teststream", + }) + require.NoError(t, err) + defer cnt2.close() + + time.Sleep(1 * time.Second) + + conn, _, err := DialRead("rtsp://localhost:8554/teststream", StreamProtocolTCP) + require.NoError(t, err) + defer conn.Close() + + _, err = conn.ReadFrameTCP() + require.NoError(t, err) +} + +func TestConnClientDialPublishUDP(t *testing.T) { + cnt1, err := newContainer("rtsp-simple-server", "server", []string{}) + require.NoError(t, err) + defer cnt1.close() + + time.Sleep(1 * time.Second) publishDone := make(chan struct{}) defer func() { <-publishDone }() - defer conn.Close() + + var conn *ConnClient + defer func() { + conn.Close() + }() go func() { defer close(publishDone) @@ -235,19 +198,11 @@ func TestConnClientPublishUDP(t *testing.T) { sps, pps, err := getH264SPSandPPS(pc) require.NoError(t, err) - _, err = conn.Options(u) - require.NoError(t, err) - track, err := NewTrackH264(0, sps, pps) require.NoError(t, err) - _, err = conn.Announce(u, Tracks{track}) - require.NoError(t, err) - - _, err = conn.SetupUDP(u, SetupModeRecord, track, 0, 0) - require.NoError(t, err) - - _, err = conn.Record(u) + conn, err = DialPublish("rtsp://localhost:8554/teststream", + StreamProtocolUDP, Tracks{track}) require.NoError(t, err) buf := make([]byte, 2048) @@ -280,22 +235,20 @@ func TestConnClientPublishUDP(t *testing.T) { require.Equal(t, 0, code) } -func TestConnClientPublishTCP(t *testing.T) { +func TestConnClientDialPublishTCP(t *testing.T) { cnt1, err := newContainer("rtsp-simple-server", "server", []string{}) require.NoError(t, err) defer cnt1.close() time.Sleep(1 * time.Second) - u, err := url.Parse("rtsp://localhost:8554/teststream") - require.NoError(t, err) - - conn, err := NewConnClient(ConnClientConf{Host: u.Host}) - require.NoError(t, err) - publishDone := make(chan struct{}) defer func() { <-publishDone }() - defer conn.Close() + + var conn *ConnClient + defer func() { + conn.Close() + }() go func() { defer close(publishDone) @@ -314,19 +267,11 @@ func TestConnClientPublishTCP(t *testing.T) { sps, pps, err := getH264SPSandPPS(pc) require.NoError(t, err) - _, err = conn.Options(u) - require.NoError(t, err) - track, err := NewTrackH264(0, sps, pps) require.NoError(t, err) - _, err = conn.Announce(u, Tracks{track}) - require.NoError(t, err) - - _, err = conn.SetupTCP(u, SetupModeRecord, track) - require.NoError(t, err) - - _, err = conn.Record(u) + conn, err = DialPublish("rtsp://localhost:8554/teststream", + StreamProtocolTCP, Tracks{track}) require.NoError(t, err) buf := make([]byte, 2048) diff --git a/examples/client-publish-tcp.go b/examples/client-publish-tcp.go index 242691b9..e9afc8bd 100644 --- a/examples/client-publish-tcp.go +++ b/examples/client-publish-tcp.go @@ -5,7 +5,6 @@ package main import ( "fmt" "net" - "net/url" "github.com/aler9/gortsplib" "github.com/pion/rtp" @@ -74,48 +73,19 @@ func main() { } fmt.Println("stream connected") - // parse url - u, err := url.Parse("rtsp://localhost:8554/mystream") - if err != nil { - panic(err) - } - - // connect to the server - conn, err := gortsplib.NewConnClient(gortsplib.ConnClientConf{Host: u.Host}) - if err != nil { - panic(err) - } - defer conn.Close() - - // get allowed commands - _, err = conn.Options(u) - if err != nil { - panic(err) - } - // create a H264 track track, err := gortsplib.NewTrackH264(0, sps, pps) if err != nil { panic(err) } - // announce the track - _, err = conn.Announce(u, gortsplib.Tracks{track}) - if err != nil { - panic(err) - } - - // setup the track with TCP - _, err = conn.SetupTCP(u, gortsplib.SetupModeRecord, track) - if err != nil { - panic(err) - } - - // start publishing - _, err = conn.Record(u) + // connect to the server and start publishing the track + conn, err := gortsplib.DialPublish("rtsp://localhost:8554/mystream", + gortsplib.StreamProtocolTCP, gortsplib.Tracks{track}) if err != nil { panic(err) } + defer conn.Close() buf := make([]byte, 2048) for { diff --git a/examples/client-publish-udp.go b/examples/client-publish-udp.go index 8da65fdb..98097eb0 100644 --- a/examples/client-publish-udp.go +++ b/examples/client-publish-udp.go @@ -5,7 +5,6 @@ package main import ( "fmt" "net" - "net/url" "github.com/aler9/gortsplib" "github.com/pion/rtp" @@ -74,48 +73,19 @@ func main() { } fmt.Println("stream connected") - // parse url - u, err := url.Parse("rtsp://localhost:8554/mystream") - if err != nil { - panic(err) - } - - // connect to the server - conn, err := gortsplib.NewConnClient(gortsplib.ConnClientConf{Host: u.Host}) - if err != nil { - panic(err) - } - defer conn.Close() - - // get allowed commands - _, err = conn.Options(u) - if err != nil { - panic(err) - } - // create a H264 track track, err := gortsplib.NewTrackH264(0, sps, pps) if err != nil { panic(err) } - // announce the track - _, err = conn.Announce(u, gortsplib.Tracks{track}) - if err != nil { - panic(err) - } - - // setup the track with UDP - _, err = conn.SetupUDP(u, gortsplib.SetupModeRecord, track, 0, 0) - if err != nil { - panic(err) - } - - // start publishing - _, err = conn.Record(u) + // connect to the server and start publishing the track + conn, err := gortsplib.DialPublish("rtsp://localhost:8554/mystream", + gortsplib.StreamProtocolUDP, gortsplib.Tracks{track}) if err != nil { panic(err) } + defer conn.Close() buf := make([]byte, 2048) for { diff --git a/examples/client-read-tcp.go b/examples/client-read-tcp.go index 7cf15cda..3d084f38 100644 --- a/examples/client-read-tcp.go +++ b/examples/client-read-tcp.go @@ -4,54 +4,21 @@ package main import ( "fmt" - "net/url" "github.com/aler9/gortsplib" ) -// This example shows how to create a RTSP client, connect to a server, list -// and read tracks with the TCP protocol. +// This example shows how to create a RTSP client, connect to a server and +// read all tracks with the TCP protocol. func main() { - // parse url - u, err := url.Parse("rtsp://localhost:8554/mystream") - if err != nil { - panic(err) - } - - // connect to the server - conn, err := gortsplib.NewConnClient(gortsplib.ConnClientConf{Host: u.Host}) + // connect to the server and start reading all tracks + conn, _, err := gortsplib.DialRead("rtsp://localhost:8554/mystream", gortsplib.StreamProtocolTCP) if err != nil { panic(err) } defer conn.Close() - // get allowed commands - _, err = conn.Options(u) - if err != nil { - panic(err) - } - - // list tracks published on the path - tracks, _, err := conn.Describe(u) - if err != nil { - panic(err) - } - - // setup tracks with TCP - for _, track := range tracks { - _, err := conn.SetupTCP(u, gortsplib.SetupModePlay, track) - if err != nil { - panic(err) - } - } - - // start reading - _, err = conn.Play(u) - if err != nil { - panic(err) - } - for { // read frames frame, err := conn.ReadFrameTCP() diff --git a/examples/client-read-udp.go b/examples/client-read-udp.go index 03e1ca8a..6201f1e4 100644 --- a/examples/client-read-udp.go +++ b/examples/client-read-udp.go @@ -4,55 +4,22 @@ package main import ( "fmt" - "net/url" "sync" "github.com/aler9/gortsplib" ) -// This example shows how to create a RTSP client, connect to a server, list -// and read tracks with the UDP protocol. +// This example shows how to create a RTSP client, connect to a server and +// read all tracks with the UDP protocol. func main() { - // parse url - u, err := url.Parse("rtsp://localhost:8554/mystream") - if err != nil { - panic(err) - } - - // connect to the server - conn, err := gortsplib.NewConnClient(gortsplib.ConnClientConf{Host: u.Host}) + // connect to the server and start reading all tracks + conn, tracks, err := gortsplib.DialRead("rtsp://localhost:8554/mystream", gortsplib.StreamProtocolUDP) if err != nil { panic(err) } defer conn.Close() - // get allowed commands - _, err = conn.Options(u) - if err != nil { - panic(err) - } - - // list tracks published on the path - tracks, _, err := conn.Describe(u) - if err != nil { - panic(err) - } - - // setup tracks with UDP - for _, track := range tracks { - _, err := conn.SetupUDP(u, gortsplib.SetupModePlay, track, 0, 0) - if err != nil { - panic(err) - } - } - - // start reading - _, err = conn.Play(u) - if err != nil { - panic(err) - } - var wg sync.WaitGroup defer wg.Wait() defer conn.CloseUDPListeners() @@ -89,6 +56,6 @@ func main() { }(track) } - err = conn.LoopUDP(u) + err = conn.LoopUDP() fmt.Println("connection is closed (%s)", err) }