diff --git a/.travis.yml b/.travis.yml index 3ec52d21..8c81bc16 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,9 @@ go: - "1.14.x" - "1.15.x" +services: +- docker + env: - GO111MODULE=on diff --git a/Makefile b/Makefile index c77213e3..276c4a17 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ format: define DOCKERFILE_TEST FROM $(BASE_IMAGE) -RUN apk add --no-cache make git gcc musl-dev +RUN apk add --no-cache make docker-cli git gcc musl-dev WORKDIR /s COPY go.mod go.sum ./ RUN go mod download @@ -40,10 +40,14 @@ export DOCKERFILE_TEST test: echo "$$DOCKERFILE_TEST" | docker build -q . -f - -t temp docker run --rm -it \ + -v /var/run/docker.sock:/var/run/docker.sock:ro \ + --network=host \ --name temp \ temp \ make test-nodocker test-nodocker: + $(foreach IMG,$(shell echo testimages/*/ | xargs -n1 basename), \ + docker build -q testimages/$(IMG) -t gortsplib-test-$(IMG)$(NL)) go test -race -v . $(foreach f,$(shell ls examples/*),go build -o /dev/null $(f)$(NL)) diff --git a/connclient_test.go b/connclient_test.go new file mode 100644 index 00000000..7ee18137 --- /dev/null +++ b/connclient_test.go @@ -0,0 +1,161 @@ +package gortsplib + +import ( + "net/url" + "os" + "os/exec" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +type container struct { + name string +} + +func newContainer(image string, name string, args []string) (*container, error) { + c := &container{ + name: name, + } + + exec.Command("docker", "kill", "gortsplib-test-"+name).Run() + exec.Command("docker", "wait", "gortsplib-test-"+name).Run() + + cmd := []string{"docker", "run", + "--network=host", + "--name=gortsplib-test-" + name, + "gortsplib-test-" + image} + cmd = append(cmd, args...) + ecmd := exec.Command(cmd[0], cmd[1:]...) + ecmd.Stdout = nil + ecmd.Stderr = os.Stderr + + err := ecmd.Start() + if err != nil { + return nil, err + } + + time.Sleep(1 * time.Second) + + return c, nil +} + +func (c *container) close() { + exec.Command("docker", "kill", "gortsplib-test-"+c.name).Run() + exec.Command("docker", "wait", "gortsplib-test-"+c.name).Run() + exec.Command("docker", "rm", "gortsplib-test-"+c.name).Run() +} + +func (c *container) wait() int { + exec.Command("docker", "wait", "gortsplib-test-"+c.name).Run() + out, _ := exec.Command("docker", "inspect", "gortsplib-test-"+c.name, + "--format={{.State.ExitCode}}").Output() + code, _ := strconv.ParseInt(string(out[:len(out)-1]), 10, 64) + return int(code) +} + +func TestConnClientTCP(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, track) + require.NoError(t, err) + } + + _, err = conn.Play(u) + require.NoError(t, err) + + frame := &InterleavedFrame{Content: make([]byte, 0, 128*1024)} + frame.Content = frame.Content[:cap(frame.Content)] + + err = conn.ReadFrame(frame) + require.NoError(t, err) +} + +func TestConnClientUDP(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) + + var rtpReads []UDPReadFunc + var rtcpReads []UDPReadFunc + + for _, track := range tracks { + rtpRead, rtcpRead, _, err := conn.SetupUDP(u, track, 9000+track.Id*2, 9001+track.Id*2) + require.NoError(t, err) + + rtpReads = append(rtpReads, rtpRead) + rtcpReads = append(rtcpReads, rtcpRead) + } + + _, err = conn.Play(u) + require.NoError(t, err) + + go conn.LoopUDP(u) + + buf := make([]byte, 2048) + _, err = rtpReads[0](buf) + require.NoError(t, err) +} diff --git a/testimages/ffmpeg/Dockerfile b/testimages/ffmpeg/Dockerfile new file mode 100644 index 00000000..1f380e26 --- /dev/null +++ b/testimages/ffmpeg/Dockerfile @@ -0,0 +1,11 @@ +FROM amd64/alpine:3.12 + +RUN apk add --no-cache \ + ffmpeg + +COPY emptyvideo.ts / + +COPY start.sh / +RUN chmod +x /start.sh + +ENTRYPOINT [ "/start.sh" ] diff --git a/testimages/ffmpeg/emptyvideo.ts b/testimages/ffmpeg/emptyvideo.ts new file mode 100644 index 00000000..6693c037 Binary files /dev/null and b/testimages/ffmpeg/emptyvideo.ts differ diff --git a/testimages/ffmpeg/start.sh b/testimages/ffmpeg/start.sh new file mode 100644 index 00000000..6fe4ec5e --- /dev/null +++ b/testimages/ffmpeg/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh -e + +exec ffmpeg -hide_banner -loglevel error $@ diff --git a/testimages/rtsp-simple-server/Dockerfile b/testimages/rtsp-simple-server/Dockerfile new file mode 100644 index 00000000..85e10cf4 --- /dev/null +++ b/testimages/rtsp-simple-server/Dockerfile @@ -0,0 +1 @@ +FROM aler9/rtsp-simple-server:latest