mirror of
https://github.com/bluenviron/mediamtx
synced 2025-02-16 19:46:59 +00:00
move high-level tests into dedicate workflow (#1219)
This commit is contained in:
parent
a4363dfaa3
commit
bf14467331
20
.github/workflows/test_highlevel.yml
vendored
Normal file
20
.github/workflows/test_highlevel.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: test_highlevel
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.19"
|
||||
|
||||
- run: make test-highlevel-nodocker
|
1
Makefile
1
Makefile
@ -15,6 +15,7 @@ help:
|
||||
@echo " format format source files"
|
||||
@echo " test run tests"
|
||||
@echo " test32 run tests on a 32-bit system"
|
||||
@echo " test-highlevel run high-level tests"
|
||||
@echo " lint run linters"
|
||||
@echo " bench NAME=n run bench environment"
|
||||
@echo " run run app"
|
||||
|
@ -66,7 +66,7 @@ func TestAPIConfigGet(t *testing.T) {
|
||||
|
||||
p, ok := newInstance("api: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
var out map[string]interface{}
|
||||
err := httpRequest(http.MethodGet, "http://localhost:9997/v1/config/get", nil, &out)
|
||||
@ -77,7 +77,7 @@ func TestAPIConfigGet(t *testing.T) {
|
||||
func TestAPIConfigSet(t *testing.T) {
|
||||
p, ok := newInstance("api: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
err := httpRequest(http.MethodPost, "http://localhost:9997/v1/config/set", map[string]interface{}{
|
||||
"rtmpDisable": true,
|
||||
@ -99,7 +99,7 @@ func TestAPIConfigSet(t *testing.T) {
|
||||
func TestAPIConfigPathsAdd(t *testing.T) {
|
||||
p, ok := newInstance("api: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
err := httpRequest(http.MethodPost, "http://localhost:9997/v1/config/paths/add/my/path", map[string]interface{}{
|
||||
"source": "rtsp://127.0.0.1:9999/mypath",
|
||||
@ -117,7 +117,7 @@ func TestAPIConfigPathsAdd(t *testing.T) {
|
||||
func TestAPIConfigPathsEdit(t *testing.T) {
|
||||
p, ok := newInstance("api: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
err := httpRequest(http.MethodPost, "http://localhost:9997/v1/config/paths/add/my/path", map[string]interface{}{
|
||||
"source": "rtsp://127.0.0.1:9999/mypath",
|
||||
@ -144,7 +144,7 @@ func TestAPIConfigPathsEdit(t *testing.T) {
|
||||
func TestAPIConfigPathsRemove(t *testing.T) {
|
||||
p, ok := newInstance("api: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
err := httpRequest(http.MethodPost, "http://localhost:9997/v1/config/paths/add/my/path", map[string]interface{}{
|
||||
"source": "rtsp://127.0.0.1:9999/mypath",
|
||||
@ -184,7 +184,7 @@ func TestAPIPathsList(t *testing.T) {
|
||||
"paths:\n" +
|
||||
" mypath:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
tracks := gortsplib.Tracks{
|
||||
&gortsplib.TrackH264{
|
||||
@ -242,7 +242,7 @@ func TestAPIPathsList(t *testing.T) {
|
||||
"paths:\n" +
|
||||
" mypath:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
tracks := gortsplib.Tracks{
|
||||
&gortsplib.TrackH264{
|
||||
@ -291,7 +291,7 @@ func TestAPIPathsList(t *testing.T) {
|
||||
" source: rtsp://127.0.0.1:1234/mypath\n" +
|
||||
" sourceOnDemand: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
var out pathList
|
||||
err := httpRequest(http.MethodGet, "http://localhost:9997/v1/paths/list", nil, &out)
|
||||
@ -316,7 +316,7 @@ func TestAPIPathsList(t *testing.T) {
|
||||
" source: rtmp://127.0.0.1:1234/mypath\n" +
|
||||
" sourceOnDemand: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
var out pathList
|
||||
err := httpRequest(http.MethodGet, "http://localhost:9997/v1/paths/list", nil, &out)
|
||||
@ -341,7 +341,7 @@ func TestAPIPathsList(t *testing.T) {
|
||||
" source: http://127.0.0.1:1234/mypath\n" +
|
||||
" sourceOnDemand: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
var out pathList
|
||||
err := httpRequest(http.MethodGet, "http://localhost:9997/v1/paths/list", nil, &out)
|
||||
@ -397,7 +397,7 @@ func TestAPIProtocolSpecificList(t *testing.T) {
|
||||
|
||||
p, ok := newInstance(conf)
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
track := &gortsplib.TrackH264{
|
||||
PayloadType: 96,
|
||||
@ -561,7 +561,7 @@ func TestAPIKick(t *testing.T) {
|
||||
|
||||
p, ok := newInstance(conf)
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
track := &gortsplib.TrackH264{
|
||||
PayloadType: 96,
|
||||
@ -589,16 +589,29 @@ func TestAPIKick(t *testing.T) {
|
||||
defer source.Close()
|
||||
|
||||
case "rtmp":
|
||||
cnt1, err := newContainer("ffmpeg", "source", []string{
|
||||
"-re",
|
||||
"-stream_loop", "-1",
|
||||
"-i", "emptyvideo.mkv",
|
||||
"-c", "copy",
|
||||
"-f", "flv",
|
||||
"rtmp://localhost:1935/test1/test2",
|
||||
})
|
||||
u, err := url.Parse("rtmp://localhost:1935/mypath")
|
||||
require.NoError(t, err)
|
||||
|
||||
nconn, err := net.Dial("tcp", u.Host)
|
||||
require.NoError(t, err)
|
||||
defer nconn.Close()
|
||||
conn := rtmp.NewConn(nconn)
|
||||
|
||||
err = conn.InitializeClient(u, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
videoTrack := &gortsplib.TrackH264{
|
||||
PayloadType: 96,
|
||||
SPS: []byte{ // 1920x1080 baseline
|
||||
0x67, 0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02,
|
||||
0x27, 0xe5, 0x84, 0x00, 0x00, 0x03, 0x00, 0x04,
|
||||
0x00, 0x00, 0x03, 0x00, 0xf0, 0x3c, 0x60, 0xc9, 0x20,
|
||||
},
|
||||
PPS: []byte{0x08, 0x06, 0x07, 0x08},
|
||||
}
|
||||
|
||||
err = conn.WriteTracks(videoTrack, nil)
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
}
|
||||
|
||||
var pa string
|
||||
|
@ -103,7 +103,8 @@ func New(args []string) (*Core, bool) {
|
||||
return p, true
|
||||
}
|
||||
|
||||
func (p *Core) close() {
|
||||
// Close closes Core and waits for all goroutines to return.
|
||||
func (p *Core) Close() {
|
||||
p.ctxCancel()
|
||||
<-p.done
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -70,54 +69,6 @@ y++U32uuSFiXDcSLarfIsE992MEJLSAynbF1Rsgsr3gXbGiuToJRyxbIeVy7gwzD
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`)
|
||||
|
||||
type container struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func newContainer(image string, name string, args []string) (*container, error) {
|
||||
c := &container{
|
||||
name: name,
|
||||
}
|
||||
|
||||
exec.Command("docker", "kill", "rtsp-simple-server-test-"+name).Run()
|
||||
exec.Command("docker", "wait", "rtsp-simple-server-test-"+name).Run()
|
||||
|
||||
// --network=host is needed to test multicast
|
||||
cmd := []string{
|
||||
"docker", "run",
|
||||
"--network=host",
|
||||
"--name=rtsp-simple-server-test-" + name,
|
||||
"rtsp-simple-server-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", "rtsp-simple-server-test-"+c.name).Run()
|
||||
exec.Command("docker", "wait", "rtsp-simple-server-test-"+c.name).Run()
|
||||
exec.Command("docker", "rm", "rtsp-simple-server-test-"+c.name).Run()
|
||||
}
|
||||
|
||||
func (c *container) wait() int {
|
||||
exec.Command("docker", "wait", "rtsp-simple-server-test-"+c.name).Run()
|
||||
out, _ := exec.Command("docker", "inspect", "rtsp-simple-server-test-"+c.name,
|
||||
"-f", "{{.State.ExitCode}}").Output()
|
||||
code, _ := strconv.ParseInt(string(out[:len(out)-1]), 10, 64)
|
||||
return int(code)
|
||||
}
|
||||
|
||||
func writeTempFile(byts []byte) (string, error) {
|
||||
tmpf, err := os.CreateTemp(os.TempDir(), "rtsp-")
|
||||
if err != nil {
|
||||
@ -153,7 +104,7 @@ func TestCorePathAutoDeletion(t *testing.T) {
|
||||
p, ok := newInstance("paths:\n" +
|
||||
" all:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
func() {
|
||||
conn, err := net.Dial("tcp", "localhost:8554")
|
||||
@ -288,7 +239,7 @@ func main() {
|
||||
" runOnDemand: %s\n"+
|
||||
" runOnDemandCloseAfter: 1s\n", execFile))
|
||||
require.Equal(t, true, ok)
|
||||
defer p1.close()
|
||||
defer p1.Close()
|
||||
|
||||
func() {
|
||||
conn, err := net.Dial("tcp", "localhost:8554")
|
||||
@ -367,7 +318,7 @@ func TestCorePathRunOnReady(t *testing.T) {
|
||||
" runOnReady: touch %s\n",
|
||||
doneFile))
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
track := &gortsplib.TrackH264{
|
||||
PayloadType: 96,
|
||||
@ -402,7 +353,7 @@ func TestCoreHotReloading(t *testing.T) {
|
||||
|
||||
p, ok := New([]string{confPath})
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
func() {
|
||||
track := &gortsplib.TrackH264{
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -79,7 +78,7 @@ func (ts *testHTTPAuthenticator) onAuth(ctx *gin.Context) {
|
||||
func TestHLSServerNotFound(t *testing.T) {
|
||||
p, ok := newInstance("")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8888/stream/", nil)
|
||||
require.NoError(t, err)
|
||||
@ -89,109 +88,3 @@ func TestHLSServerNotFound(t *testing.T) {
|
||||
defer res.Body.Close()
|
||||
require.Equal(t, http.StatusNotFound, res.StatusCode)
|
||||
}
|
||||
|
||||
func TestHLSServerRead(t *testing.T) {
|
||||
p, ok := newInstance("paths:\n" +
|
||||
" all:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
|
||||
cnt1, err := newContainer("ffmpeg", "source", []string{
|
||||
"-re",
|
||||
"-stream_loop", "-1",
|
||||
"-i", "emptyvideo.mkv",
|
||||
"-c", "copy",
|
||||
"-f", "rtsp",
|
||||
"rtsp://127.0.0.1:8554/test/stream",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
cnt2, err := newContainer("ffmpeg", "dest", []string{
|
||||
"-i", "http://127.0.0.1:8888/test/stream/index.m3u8",
|
||||
"-vframes", "1",
|
||||
"-f", "image2",
|
||||
"-y", "/dev/null",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt2.close()
|
||||
require.Equal(t, 0, cnt2.wait())
|
||||
}
|
||||
|
||||
func TestHLSServerAuth(t *testing.T) {
|
||||
for _, mode := range []string{
|
||||
"internal",
|
||||
"external",
|
||||
} {
|
||||
for _, result := range []string{
|
||||
"success",
|
||||
"fail",
|
||||
} {
|
||||
t.Run(mode+"_"+result, func(t *testing.T) {
|
||||
var conf string
|
||||
if mode == "internal" {
|
||||
conf = "paths:\n" +
|
||||
" all:\n" +
|
||||
" readUser: testreader\n" +
|
||||
" readPass: testpass\n" +
|
||||
" readIPs: [127.0.0.0/16]\n"
|
||||
} else {
|
||||
conf = "externalAuthenticationURL: http://127.0.0.1:9120/auth\n" +
|
||||
"paths:\n" +
|
||||
" all:\n"
|
||||
}
|
||||
|
||||
p, ok := newInstance(conf)
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
|
||||
var a *testHTTPAuthenticator
|
||||
if mode == "external" {
|
||||
var err error
|
||||
a, err = newTestHTTPAuthenticator("publish")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
cnt1, err := newContainer("ffmpeg", "source", []string{
|
||||
"-re",
|
||||
"-stream_loop", "-1",
|
||||
"-i", "emptyvideo.mkv",
|
||||
"-c", "copy",
|
||||
"-f", "rtsp",
|
||||
"rtsp://testpublisher:testpass@127.0.0.1:8554/teststream?param=value",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
if mode == "external" {
|
||||
a.close()
|
||||
var err error
|
||||
a, err = newTestHTTPAuthenticator("read")
|
||||
require.NoError(t, err)
|
||||
defer a.close()
|
||||
}
|
||||
|
||||
var usr string
|
||||
if result == "success" {
|
||||
usr = "testreader"
|
||||
} else {
|
||||
usr = "testreader2"
|
||||
}
|
||||
|
||||
res, err := http.Get("http://" + usr + ":testpass@127.0.0.1:8888/teststream/index.m3u8?param=value")
|
||||
require.NoError(t, err)
|
||||
defer res.Body.Close()
|
||||
|
||||
if result == "success" {
|
||||
require.Equal(t, http.StatusOK, res.StatusCode)
|
||||
} else {
|
||||
require.Equal(t, http.StatusUnauthorized, res.StatusCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ func TestHLSSource(t *testing.T) {
|
||||
" source: http://localhost:5780/stream.m3u8\n" +
|
||||
" sourceOnDemand: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
|
@ -2,13 +2,17 @@ package core
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/aler9/gortsplib"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/rtmp"
|
||||
)
|
||||
|
||||
func TestMetrics(t *testing.T) {
|
||||
@ -27,7 +31,7 @@ func TestMetrics(t *testing.T) {
|
||||
"paths:\n" +
|
||||
" all:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
track := &gortsplib.TrackH264{
|
||||
PayloadType: 96,
|
||||
@ -42,16 +46,29 @@ func TestMetrics(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer source.Close()
|
||||
|
||||
cnt1, err := newContainer("ffmpeg", "source", []string{
|
||||
"-re",
|
||||
"-stream_loop", "-1",
|
||||
"-i", "emptyvideo.mkv",
|
||||
"-c", "copy",
|
||||
"-f", "flv",
|
||||
"rtmp://localhost:1935/rtmp_path",
|
||||
})
|
||||
u, err := url.Parse("rtmp://localhost:1935/rtmp_path")
|
||||
require.NoError(t, err)
|
||||
|
||||
nconn, err := net.Dial("tcp", u.Host)
|
||||
require.NoError(t, err)
|
||||
defer nconn.Close()
|
||||
conn := rtmp.NewConn(nconn)
|
||||
|
||||
err = conn.InitializeClient(u, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
videoTrack := &gortsplib.TrackH264{
|
||||
PayloadType: 96,
|
||||
SPS: []byte{ // 1920x1080 baseline
|
||||
0x67, 0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02,
|
||||
0x27, 0xe5, 0x84, 0x00, 0x00, 0x03, 0x00, 0x04,
|
||||
0x00, 0x00, 0x03, 0x00, 0xf0, 0x3c, 0x60, 0xc9, 0x20,
|
||||
},
|
||||
PPS: []byte{0x08, 0x06, 0x07, 0x08},
|
||||
}
|
||||
|
||||
err = conn.WriteTracks(videoTrack, nil)
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
|
||||
func() {
|
||||
res, err := http.Get("http://localhost:8888/rtsp_path/index.m3u8")
|
||||
|
@ -29,7 +29,7 @@ func TestRTMPServerPublishRead(t *testing.T) {
|
||||
"paths:\n" +
|
||||
" all:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
} else {
|
||||
port = "1936"
|
||||
|
||||
@ -49,7 +49,7 @@ func TestRTMPServerPublishRead(t *testing.T) {
|
||||
"paths:\n" +
|
||||
" all:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
}
|
||||
|
||||
u, err := url.Parse("rtmp://127.0.0.1:" + port + "/mystream")
|
||||
@ -169,7 +169,7 @@ func TestRTMPServerAuth(t *testing.T) {
|
||||
|
||||
p, ok := newInstance(conf)
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
var a *testHTTPAuthenticator
|
||||
if ca == "external" {
|
||||
@ -239,7 +239,7 @@ func TestRTMPServerAuthFail(t *testing.T) {
|
||||
" publishUser: testuser2\n" +
|
||||
" publishPass: testpass\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
u1, err := url.Parse("rtmp://127.0.0.1:1935/teststream?user=testuser&pass=testpass")
|
||||
require.NoError(t, err)
|
||||
@ -289,7 +289,7 @@ func TestRTMPServerAuthFail(t *testing.T) {
|
||||
"paths:\n" +
|
||||
" all:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
a, err := newTestHTTPAuthenticator("publish")
|
||||
require.NoError(t, err)
|
||||
@ -346,7 +346,7 @@ func TestRTMPServerAuthFail(t *testing.T) {
|
||||
" readUser: testuser2\n" +
|
||||
" readPass: testpass\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
u1, err := url.Parse("rtmp://127.0.0.1:1935/teststream")
|
||||
require.NoError(t, err)
|
||||
|
@ -102,7 +102,7 @@ func TestRTMPSource(t *testing.T) {
|
||||
" source: rtmp://localhost:1937/teststream\n" +
|
||||
" sourceOnDemand: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
} else {
|
||||
p, ok := newInstance("paths:\n" +
|
||||
" proxied:\n" +
|
||||
@ -110,7 +110,7 @@ func TestRTMPSource(t *testing.T) {
|
||||
" sourceFingerprint: 33949E05FFFB5FF3E8AA16F8213A6251B4D9363804BA53233C4DA9A46D6F2739\n" +
|
||||
" sourceOnDemand: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
}
|
||||
|
||||
c := gortsplib.Client{
|
||||
|
@ -1,9 +1,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aler9/gortsplib"
|
||||
"github.com/aler9/gortsplib/pkg/url"
|
||||
@ -11,191 +9,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRTSPServerPublishRead(t *testing.T) {
|
||||
for _, ca := range []struct {
|
||||
publisherSoft string
|
||||
publisherProto string
|
||||
readerSoft string
|
||||
readerProto string
|
||||
}{
|
||||
{"ffmpeg", "udp", "ffmpeg", "udp"},
|
||||
{"ffmpeg", "udp", "ffmpeg", "multicast"},
|
||||
{"ffmpeg", "udp", "ffmpeg", "tcp"},
|
||||
{"ffmpeg", "udp", "gstreamer", "udp"},
|
||||
{"ffmpeg", "udp", "gstreamer", "multicast"},
|
||||
{"ffmpeg", "udp", "gstreamer", "tcp"},
|
||||
{"ffmpeg", "udp", "vlc", "udp"},
|
||||
{"ffmpeg", "udp", "vlc", "multicast"},
|
||||
{"ffmpeg", "udp", "vlc", "tcp"},
|
||||
{"ffmpeg", "tcp", "ffmpeg", "udp"},
|
||||
{"gstreamer", "udp", "ffmpeg", "udp"},
|
||||
{"gstreamer", "tcp", "ffmpeg", "udp"},
|
||||
{"ffmpeg", "tls", "ffmpeg", "tls"},
|
||||
{"ffmpeg", "tls", "gstreamer", "tls"},
|
||||
{"gstreamer", "tls", "ffmpeg", "tls"},
|
||||
} {
|
||||
t.Run(ca.publisherSoft+"_"+ca.publisherProto+"_"+
|
||||
ca.readerSoft+"_"+ca.readerProto, func(t *testing.T) {
|
||||
var proto string
|
||||
var port string
|
||||
if ca.publisherProto != "tls" {
|
||||
proto = "rtsp"
|
||||
port = "8554"
|
||||
|
||||
p, ok := newInstance("rtmpDisable: yes\n" +
|
||||
"hlsDisable: yes\n" +
|
||||
"readTimeout: 20s\n" +
|
||||
"paths:\n" +
|
||||
" all:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
} else {
|
||||
proto = "rtsps"
|
||||
port = "8322"
|
||||
|
||||
serverCertFpath, err := writeTempFile(serverCert)
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(serverCertFpath)
|
||||
|
||||
serverKeyFpath, err := writeTempFile(serverKey)
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(serverKeyFpath)
|
||||
|
||||
p, ok := newInstance("rtmpDisable: yes\n" +
|
||||
"hlsDisable: yes\n" +
|
||||
"readTimeout: 20s\n" +
|
||||
"protocols: [tcp]\n" +
|
||||
"encryption: \"yes\"\n" +
|
||||
"serverCert: " + serverCertFpath + "\n" +
|
||||
"serverKey: " + serverKeyFpath + "\n" +
|
||||
"paths:\n" +
|
||||
" all:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
}
|
||||
|
||||
switch ca.publisherSoft {
|
||||
case "ffmpeg":
|
||||
ps := func() string {
|
||||
switch ca.publisherProto {
|
||||
case "udp", "tcp":
|
||||
return ca.publisherProto
|
||||
|
||||
default: // tls
|
||||
return "tcp"
|
||||
}
|
||||
}()
|
||||
|
||||
cnt1, err := newContainer("ffmpeg", "source", []string{
|
||||
"-re",
|
||||
"-stream_loop", "-1",
|
||||
"-i", "emptyvideo.mkv",
|
||||
"-c", "copy",
|
||||
"-f", "rtsp",
|
||||
"-rtsp_transport",
|
||||
ps,
|
||||
proto + "://localhost:" + port + "/teststream",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
case "gstreamer":
|
||||
ps := func() string {
|
||||
switch ca.publisherProto {
|
||||
case "udp", "tcp":
|
||||
return ca.publisherProto
|
||||
|
||||
default: // tls
|
||||
return "tcp"
|
||||
}
|
||||
}()
|
||||
|
||||
cnt1, err := newContainer("gstreamer", "source", []string{
|
||||
"filesrc location=emptyvideo.mkv ! matroskademux ! video/x-h264 ! rtspclientsink " +
|
||||
"location=" + proto + "://localhost:" + port + "/teststream " +
|
||||
"protocols=" + ps + " tls-validation-flags=0 latency=0 timeout=0 rtx-time=0",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
switch ca.readerSoft {
|
||||
case "ffmpeg":
|
||||
ps := func() string {
|
||||
switch ca.readerProto {
|
||||
case "udp", "tcp":
|
||||
return ca.publisherProto
|
||||
|
||||
case "multicast":
|
||||
return "udp_multicast"
|
||||
|
||||
default: // tls
|
||||
return "tcp"
|
||||
}
|
||||
}()
|
||||
|
||||
cnt2, err := newContainer("ffmpeg", "dest", []string{
|
||||
"-rtsp_transport", ps,
|
||||
"-i", proto + "://localhost:" + port + "/teststream",
|
||||
"-vframes", "1",
|
||||
"-f", "image2",
|
||||
"-y", "/dev/null",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt2.close()
|
||||
require.Equal(t, 0, cnt2.wait())
|
||||
|
||||
case "gstreamer":
|
||||
ps := func() string {
|
||||
switch ca.readerProto {
|
||||
case "udp", "tcp":
|
||||
return ca.publisherProto
|
||||
|
||||
case "multicast":
|
||||
return "udp-mcast"
|
||||
|
||||
default: // tls
|
||||
return "tcp"
|
||||
}
|
||||
}()
|
||||
|
||||
cnt2, err := newContainer("gstreamer", "read", []string{
|
||||
"rtspsrc location=" + proto + "://127.0.0.1:" + port + "/teststream " +
|
||||
"protocols=" + ps + " " +
|
||||
"tls-validation-flags=0 latency=0 " +
|
||||
"! application/x-rtp,media=video ! decodebin ! exitafterframe ! fakesink",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt2.close()
|
||||
require.Equal(t, 0, cnt2.wait())
|
||||
|
||||
case "vlc":
|
||||
args := []string{}
|
||||
if ca.readerProto == "tcp" {
|
||||
args = append(args, "--rtsp-tcp")
|
||||
}
|
||||
|
||||
ur := proto + "://localhost:" + port + "/teststream"
|
||||
if ca.readerProto == "multicast" {
|
||||
ur += "?vlcmulticast"
|
||||
}
|
||||
|
||||
args = append(args, ur)
|
||||
cnt2, err := newContainer("vlc", "dest", args)
|
||||
require.NoError(t, err)
|
||||
defer cnt2.close()
|
||||
require.Equal(t, 0, cnt2.wait())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRTSPServerAuth(t *testing.T) {
|
||||
for _, ca := range []string{
|
||||
"internal",
|
||||
@ -222,7 +35,7 @@ func TestRTSPServerAuth(t *testing.T) {
|
||||
|
||||
p, ok := newInstance(conf)
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
var a *testHTTPAuthenticator
|
||||
if ca == "external" {
|
||||
@ -278,7 +91,7 @@ func TestRTSPServerAuth(t *testing.T) {
|
||||
" publishUser: sha256:rl3rgi4NcZkpAEcacZnQ2VuOfJ0FxAqCRaKB/SwdZoQ=\n" +
|
||||
" publishPass: sha256:E9JJ8stBJ7QM+nV4ZoUCeHk/gU3tPFh/5YieiJp6n2w=\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
track := &gortsplib.TrackH264{
|
||||
PayloadType: 96,
|
||||
@ -326,7 +139,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
|
||||
" publishUser: testuser\n" +
|
||||
" publishPass: testpass\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
track := &gortsplib.TrackH264{
|
||||
PayloadType: 96,
|
||||
@ -373,7 +186,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
|
||||
" readUser: testuser\n" +
|
||||
" readPass: testpass\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
c := gortsplib.Client{}
|
||||
|
||||
@ -396,7 +209,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
|
||||
" all:\n" +
|
||||
" publishIPs: [128.0.0.1/32]\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
track := &gortsplib.TrackH264{
|
||||
PayloadType: 96,
|
||||
@ -418,7 +231,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
|
||||
"paths:\n" +
|
||||
" all:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
a, err := newTestHTTPAuthenticator("publish")
|
||||
require.NoError(t, err)
|
||||
@ -457,7 +270,7 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
|
||||
|
||||
p, ok := newInstance(conf)
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
track := &gortsplib.TrackH264{
|
||||
PayloadType: 96,
|
||||
@ -546,43 +359,6 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRTSPServerRedirect(t *testing.T) {
|
||||
p1, ok := newInstance("rtmpDisable: yes\n" +
|
||||
"hlsDisable: yes\n" +
|
||||
"paths:\n" +
|
||||
" path1:\n" +
|
||||
" source: redirect\n" +
|
||||
" sourceRedirect: rtsp://localhost:8554/path2\n" +
|
||||
" path2:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p1.close()
|
||||
|
||||
cnt1, err := newContainer("ffmpeg", "source", []string{
|
||||
"-re",
|
||||
"-stream_loop", "-1",
|
||||
"-i", "emptyvideo.mkv",
|
||||
"-c", "copy",
|
||||
"-f", "rtsp",
|
||||
"-rtsp_transport", "udp",
|
||||
"rtsp://localhost:8554/path2",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
cnt2, err := newContainer("ffmpeg", "dest", []string{
|
||||
"-rtsp_transport", "udp",
|
||||
"-i", "rtsp://localhost:8554/path1",
|
||||
"-vframes", "1",
|
||||
"-f", "image2",
|
||||
"-y", "/dev/null",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt2.close()
|
||||
require.Equal(t, 0, cnt2.wait())
|
||||
}
|
||||
|
||||
func TestRTSPServerFallback(t *testing.T) {
|
||||
for _, ca := range []string{
|
||||
"absolute",
|
||||
@ -603,7 +379,7 @@ func TestRTSPServerFallback(t *testing.T) {
|
||||
" fallback: " + val + "\n" +
|
||||
" path2:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p1.close()
|
||||
defer p1.Close()
|
||||
|
||||
source := gortsplib.Client{}
|
||||
err := source.StartPublishing("rtsp://localhost:8554/path2",
|
||||
|
@ -134,7 +134,7 @@ func TestRTSPSource(t *testing.T) {
|
||||
" sourceProtocol: " + source + "\n" +
|
||||
" sourceOnDemand: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
} else {
|
||||
p, ok := newInstance("paths:\n" +
|
||||
" proxied:\n" +
|
||||
@ -142,7 +142,7 @@ func TestRTSPSource(t *testing.T) {
|
||||
" sourceFingerprint: 33949E05FFFB5FF3E8AA16F8213A6251B4D9363804BA53233C4DA9A46D6F2739\n" +
|
||||
" sourceOnDemand: yes\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
}
|
||||
|
||||
received := make(chan struct{})
|
||||
@ -231,7 +231,7 @@ func TestRTSPSourceNoPassword(t *testing.T) {
|
||||
" source: rtsp://testuser:@127.0.0.1:8555/teststream\n" +
|
||||
" sourceProtocol: tcp\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
<-done
|
||||
}
|
||||
@ -275,7 +275,7 @@ func TestRTSPSourceDynamicH264Params(t *testing.T) {
|
||||
" proxied:\n" +
|
||||
" source: rtsp://127.0.0.1:8555/teststream\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.close()
|
||||
defer p.Close()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
|
31
internal/highleveltests/build_images_test.go
Normal file
31
internal/highleveltests/build_images_test.go
Normal file
@ -0,0 +1,31 @@
|
||||
//go:build enable_highlevel_tests
|
||||
// +build enable_highlevel_tests
|
||||
|
||||
package highleveltests
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func buildImage(image string) error {
|
||||
ecmd := exec.Command("docker", "build", filepath.Join("images", image),
|
||||
"-t", "rtsp-simple-server-test-"+image)
|
||||
ecmd.Stdout = nil
|
||||
ecmd.Stderr = os.Stderr
|
||||
return ecmd.Run()
|
||||
}
|
||||
|
||||
func TestBuildImages(t *testing.T) {
|
||||
files, err := os.ReadDir("images")
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, file := range files {
|
||||
err := buildImage(file.Name())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
186
internal/highleveltests/hls_server_test.go
Normal file
186
internal/highleveltests/hls_server_test.go
Normal file
@ -0,0 +1,186 @@
|
||||
//go:build enable_highlevel_tests
|
||||
// +build enable_highlevel_tests
|
||||
|
||||
package highleveltests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testHTTPAuthenticator struct {
|
||||
action string
|
||||
|
||||
s *http.Server
|
||||
}
|
||||
|
||||
func newTestHTTPAuthenticator(action string) (*testHTTPAuthenticator, error) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:9120")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ts := &testHTTPAuthenticator{
|
||||
action: action,
|
||||
}
|
||||
|
||||
router := gin.New()
|
||||
router.POST("/auth", ts.onAuth)
|
||||
|
||||
ts.s = &http.Server{Handler: router}
|
||||
go ts.s.Serve(ln)
|
||||
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
func (ts *testHTTPAuthenticator) close() {
|
||||
ts.s.Shutdown(context.Background())
|
||||
}
|
||||
|
||||
func (ts *testHTTPAuthenticator) onAuth(ctx *gin.Context) {
|
||||
var in struct {
|
||||
IP string `json:"ip"`
|
||||
User string `json:"user"`
|
||||
Password string `json:"password"`
|
||||
Path string `json:"path"`
|
||||
Action string `json:"action"`
|
||||
Query string `json:"query"`
|
||||
}
|
||||
err := json.NewDecoder(ctx.Request.Body).Decode(&in)
|
||||
if err != nil {
|
||||
ctx.AbortWithStatus(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var user string
|
||||
if ts.action == "publish" {
|
||||
user = "testpublisher"
|
||||
} else {
|
||||
user = "testreader"
|
||||
}
|
||||
|
||||
if in.IP != "127.0.0.1" ||
|
||||
in.User != user ||
|
||||
in.Password != "testpass" ||
|
||||
in.Path != "teststream" ||
|
||||
in.Action != ts.action ||
|
||||
(in.Query != "user=testreader&pass=testpass¶m=value" &&
|
||||
in.Query != "user=testpublisher&pass=testpass¶m=value" &&
|
||||
in.Query != "param=value") {
|
||||
ctx.AbortWithStatus(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestHLSServerRead(t *testing.T) {
|
||||
p, ok := newInstance("paths:\n" +
|
||||
" all:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.Close()
|
||||
|
||||
cnt1, err := newContainer("ffmpeg", "source", []string{
|
||||
"-re",
|
||||
"-stream_loop", "-1",
|
||||
"-i", "emptyvideo.mkv",
|
||||
"-c", "copy",
|
||||
"-f", "rtsp",
|
||||
"rtsp://127.0.0.1:8554/test/stream",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
cnt2, err := newContainer("ffmpeg", "dest", []string{
|
||||
"-i", "http://127.0.0.1:8888/test/stream/index.m3u8",
|
||||
"-vframes", "1",
|
||||
"-f", "image2",
|
||||
"-y", "/dev/null",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt2.close()
|
||||
require.Equal(t, 0, cnt2.wait())
|
||||
}
|
||||
|
||||
func TestHLSServerAuth(t *testing.T) {
|
||||
for _, mode := range []string{
|
||||
"internal",
|
||||
"external",
|
||||
} {
|
||||
for _, result := range []string{
|
||||
"success",
|
||||
"fail",
|
||||
} {
|
||||
t.Run(mode+"_"+result, func(t *testing.T) {
|
||||
var conf string
|
||||
if mode == "internal" {
|
||||
conf = "paths:\n" +
|
||||
" all:\n" +
|
||||
" readUser: testreader\n" +
|
||||
" readPass: testpass\n" +
|
||||
" readIPs: [127.0.0.0/16]\n"
|
||||
} else {
|
||||
conf = "externalAuthenticationURL: http://127.0.0.1:9120/auth\n" +
|
||||
"paths:\n" +
|
||||
" all:\n"
|
||||
}
|
||||
|
||||
p, ok := newInstance(conf)
|
||||
require.Equal(t, true, ok)
|
||||
defer p.Close()
|
||||
|
||||
var a *testHTTPAuthenticator
|
||||
if mode == "external" {
|
||||
var err error
|
||||
a, err = newTestHTTPAuthenticator("publish")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
cnt1, err := newContainer("ffmpeg", "source", []string{
|
||||
"-re",
|
||||
"-stream_loop", "-1",
|
||||
"-i", "emptyvideo.mkv",
|
||||
"-c", "copy",
|
||||
"-f", "rtsp",
|
||||
"rtsp://testpublisher:testpass@127.0.0.1:8554/teststream?param=value",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
if mode == "external" {
|
||||
a.close()
|
||||
var err error
|
||||
a, err = newTestHTTPAuthenticator("read")
|
||||
require.NoError(t, err)
|
||||
defer a.close()
|
||||
}
|
||||
|
||||
var usr string
|
||||
if result == "success" {
|
||||
usr = "testreader"
|
||||
} else {
|
||||
usr = "testreader2"
|
||||
}
|
||||
|
||||
res, err := http.Get("http://" + usr + ":testpass@127.0.0.1:8888/teststream/index.m3u8?param=value")
|
||||
require.NoError(t, err)
|
||||
defer res.Body.Close()
|
||||
|
||||
if result == "success" {
|
||||
require.Equal(t, http.StatusOK, res.StatusCode)
|
||||
} else {
|
||||
require.Equal(t, http.StatusUnauthorized, res.StatusCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
234
internal/highleveltests/rtsp_server_test.go
Normal file
234
internal/highleveltests/rtsp_server_test.go
Normal file
@ -0,0 +1,234 @@
|
||||
//go:build enable_highlevel_tests
|
||||
// +build enable_highlevel_tests
|
||||
|
||||
package highleveltests
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRTSPServerPublishRead(t *testing.T) {
|
||||
for _, ca := range []struct {
|
||||
publisherSoft string
|
||||
publisherProto string
|
||||
readerSoft string
|
||||
readerProto string
|
||||
}{
|
||||
{"ffmpeg", "udp", "ffmpeg", "udp"},
|
||||
{"ffmpeg", "udp", "ffmpeg", "multicast"},
|
||||
{"ffmpeg", "udp", "ffmpeg", "tcp"},
|
||||
{"ffmpeg", "udp", "gstreamer", "udp"},
|
||||
{"ffmpeg", "udp", "gstreamer", "multicast"},
|
||||
{"ffmpeg", "udp", "gstreamer", "tcp"},
|
||||
{"ffmpeg", "udp", "vlc", "udp"},
|
||||
{"ffmpeg", "udp", "vlc", "multicast"},
|
||||
{"ffmpeg", "udp", "vlc", "tcp"},
|
||||
{"ffmpeg", "tcp", "ffmpeg", "udp"},
|
||||
{"gstreamer", "udp", "ffmpeg", "udp"},
|
||||
{"gstreamer", "tcp", "ffmpeg", "udp"},
|
||||
{"ffmpeg", "tls", "ffmpeg", "tls"},
|
||||
{"ffmpeg", "tls", "gstreamer", "tls"},
|
||||
{"gstreamer", "tls", "ffmpeg", "tls"},
|
||||
} {
|
||||
t.Run(ca.publisherSoft+"_"+ca.publisherProto+"_"+
|
||||
ca.readerSoft+"_"+ca.readerProto, func(t *testing.T) {
|
||||
var proto string
|
||||
var port string
|
||||
if ca.publisherProto != "tls" {
|
||||
proto = "rtsp"
|
||||
port = "8554"
|
||||
|
||||
p, ok := newInstance("rtmpDisable: yes\n" +
|
||||
"hlsDisable: yes\n" +
|
||||
"readTimeout: 20s\n" +
|
||||
"paths:\n" +
|
||||
" all:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.Close()
|
||||
} else {
|
||||
proto = "rtsps"
|
||||
port = "8322"
|
||||
|
||||
serverCertFpath, err := writeTempFile(serverCert)
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(serverCertFpath)
|
||||
|
||||
serverKeyFpath, err := writeTempFile(serverKey)
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(serverKeyFpath)
|
||||
|
||||
p, ok := newInstance("rtmpDisable: yes\n" +
|
||||
"hlsDisable: yes\n" +
|
||||
"readTimeout: 20s\n" +
|
||||
"protocols: [tcp]\n" +
|
||||
"encryption: \"yes\"\n" +
|
||||
"serverCert: " + serverCertFpath + "\n" +
|
||||
"serverKey: " + serverKeyFpath + "\n" +
|
||||
"paths:\n" +
|
||||
" all:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p.Close()
|
||||
}
|
||||
|
||||
switch ca.publisherSoft {
|
||||
case "ffmpeg":
|
||||
ps := func() string {
|
||||
switch ca.publisherProto {
|
||||
case "udp", "tcp":
|
||||
return ca.publisherProto
|
||||
|
||||
default: // tls
|
||||
return "tcp"
|
||||
}
|
||||
}()
|
||||
|
||||
cnt1, err := newContainer("ffmpeg", "source", []string{
|
||||
"-re",
|
||||
"-stream_loop", "-1",
|
||||
"-i", "emptyvideo.mkv",
|
||||
"-c", "copy",
|
||||
"-f", "rtsp",
|
||||
"-rtsp_transport",
|
||||
ps,
|
||||
proto + "://localhost:" + port + "/teststream",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
case "gstreamer":
|
||||
ps := func() string {
|
||||
switch ca.publisherProto {
|
||||
case "udp", "tcp":
|
||||
return ca.publisherProto
|
||||
|
||||
default: // tls
|
||||
return "tcp"
|
||||
}
|
||||
}()
|
||||
|
||||
cnt1, err := newContainer("gstreamer", "source", []string{
|
||||
"filesrc location=emptyvideo.mkv ! matroskademux ! video/x-h264 ! rtspclientsink " +
|
||||
"location=" + proto + "://localhost:" + port + "/teststream " +
|
||||
"protocols=" + ps + " tls-validation-flags=0 latency=0 timeout=0 rtx-time=0",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
switch ca.readerSoft {
|
||||
case "ffmpeg":
|
||||
ps := func() string {
|
||||
switch ca.readerProto {
|
||||
case "udp", "tcp":
|
||||
return ca.publisherProto
|
||||
|
||||
case "multicast":
|
||||
return "udp_multicast"
|
||||
|
||||
default: // tls
|
||||
return "tcp"
|
||||
}
|
||||
}()
|
||||
|
||||
cnt2, err := newContainer("ffmpeg", "dest", []string{
|
||||
"-rtsp_transport", ps,
|
||||
"-i", proto + "://localhost:" + port + "/teststream",
|
||||
"-vframes", "1",
|
||||
"-f", "image2",
|
||||
"-y", "/dev/null",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt2.close()
|
||||
require.Equal(t, 0, cnt2.wait())
|
||||
|
||||
case "gstreamer":
|
||||
ps := func() string {
|
||||
switch ca.readerProto {
|
||||
case "udp", "tcp":
|
||||
return ca.publisherProto
|
||||
|
||||
case "multicast":
|
||||
return "udp-mcast"
|
||||
|
||||
default: // tls
|
||||
return "tcp"
|
||||
}
|
||||
}()
|
||||
|
||||
cnt2, err := newContainer("gstreamer", "read", []string{
|
||||
"rtspsrc location=" + proto + "://127.0.0.1:" + port + "/teststream " +
|
||||
"protocols=" + ps + " " +
|
||||
"tls-validation-flags=0 latency=0 " +
|
||||
"! application/x-rtp,media=video ! decodebin ! exitafterframe ! fakesink",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt2.close()
|
||||
require.Equal(t, 0, cnt2.wait())
|
||||
|
||||
case "vlc":
|
||||
args := []string{}
|
||||
if ca.readerProto == "tcp" {
|
||||
args = append(args, "--rtsp-tcp")
|
||||
}
|
||||
|
||||
ur := proto + "://localhost:" + port + "/teststream"
|
||||
if ca.readerProto == "multicast" {
|
||||
ur += "?vlcmulticast"
|
||||
}
|
||||
|
||||
args = append(args, ur)
|
||||
cnt2, err := newContainer("vlc", "dest", args)
|
||||
require.NoError(t, err)
|
||||
defer cnt2.close()
|
||||
require.Equal(t, 0, cnt2.wait())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRTSPServerRedirect(t *testing.T) {
|
||||
p1, ok := newInstance("rtmpDisable: yes\n" +
|
||||
"hlsDisable: yes\n" +
|
||||
"paths:\n" +
|
||||
" path1:\n" +
|
||||
" source: redirect\n" +
|
||||
" sourceRedirect: rtsp://localhost:8554/path2\n" +
|
||||
" path2:\n")
|
||||
require.Equal(t, true, ok)
|
||||
defer p1.Close()
|
||||
|
||||
cnt1, err := newContainer("ffmpeg", "source", []string{
|
||||
"-re",
|
||||
"-stream_loop", "-1",
|
||||
"-i", "emptyvideo.mkv",
|
||||
"-c", "copy",
|
||||
"-f", "rtsp",
|
||||
"-rtsp_transport", "udp",
|
||||
"rtsp://localhost:8554/path2",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt1.close()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
cnt2, err := newContainer("ffmpeg", "dest", []string{
|
||||
"-rtsp_transport", "udp",
|
||||
"-i", "rtsp://localhost:8554/path1",
|
||||
"-vframes", "1",
|
||||
"-f", "image2",
|
||||
"-y", "/dev/null",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cnt2.close()
|
||||
require.Equal(t, 0, cnt2.wait())
|
||||
}
|
142
internal/highleveltests/tests_test.go
Normal file
142
internal/highleveltests/tests_test.go
Normal file
@ -0,0 +1,142 @@
|
||||
//go:build enable_highlevel_tests
|
||||
// +build enable_highlevel_tests
|
||||
|
||||
package highleveltests
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/core"
|
||||
)
|
||||
|
||||
var serverCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDazCCAlOgAwIBAgIUXw1hEC3LFpTsllv7D3ARJyEq7sIwDQYJKoZIhvcNAQEL
|
||||
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMTMxNzQ0NThaFw0zMDEy
|
||||
MTExNzQ0NThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQDG8DyyS51810GsGwgWr5rjJK7OE1kTTLSNEEKax8Bj
|
||||
zOyiaz8rA2JGl2VUEpi2UjDr9Cm7nd+YIEVs91IIBOb7LGqObBh1kGF3u5aZxLkv
|
||||
NJE+HrLVvUhaDobK2NU+Wibqc/EI3DfUkt1rSINvv9flwTFu1qHeuLWhoySzDKEp
|
||||
OzYxpFhwjVSokZIjT4Red3OtFz7gl2E6OAWe2qoh5CwLYVdMWtKR0Xuw3BkDPk9I
|
||||
qkQKx3fqv97LPEzhyZYjDT5WvGrgZ1WDAN3booxXF3oA1H3GHQc4m/vcLatOtb8e
|
||||
nI59gMQLEbnp08cl873bAuNuM95EZieXTHNbwUnq5iybAgMBAAGjUzBRMB0GA1Ud
|
||||
DgQWBBQBKhJh8eWu0a4au9X/2fKhkFX2vjAfBgNVHSMEGDAWgBQBKhJh8eWu0a4a
|
||||
u9X/2fKhkFX2vjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBj
|
||||
3aCW0YPKukYgVK9cwN0IbVy/D0C1UPT4nupJcy/E0iC7MXPZ9D/SZxYQoAkdptdO
|
||||
xfI+RXkpQZLdODNx9uvV+cHyZHZyjtE5ENu/i5Rer2cWI/mSLZm5lUQyx+0KZ2Yu
|
||||
tEI1bsebDK30msa8QSTn0WidW9XhFnl3gRi4wRdimcQapOWYVs7ih+nAlSvng7NI
|
||||
XpAyRs8PIEbpDDBMWnldrX4TP6EWYUi49gCp8OUDRREKX3l6Ls1vZ02F34yHIt/7
|
||||
7IV/XSKG096bhW+icKBWV0IpcEsgTzPK1J1hMxgjhzIMxGboAeUU+kidthOob6Sd
|
||||
XQxaORfgM//NzX9LhUPk
|
||||
-----END CERTIFICATE-----
|
||||
`)
|
||||
|
||||
var serverKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAxvA8skudfNdBrBsIFq+a4ySuzhNZE0y0jRBCmsfAY8zsoms/
|
||||
KwNiRpdlVBKYtlIw6/Qpu53fmCBFbPdSCATm+yxqjmwYdZBhd7uWmcS5LzSRPh6y
|
||||
1b1IWg6GytjVPlom6nPxCNw31JLda0iDb7/X5cExbtah3ri1oaMkswyhKTs2MaRY
|
||||
cI1UqJGSI0+EXndzrRc+4JdhOjgFntqqIeQsC2FXTFrSkdF7sNwZAz5PSKpECsd3
|
||||
6r/eyzxM4cmWIw0+Vrxq4GdVgwDd26KMVxd6ANR9xh0HOJv73C2rTrW/HpyOfYDE
|
||||
CxG56dPHJfO92wLjbjPeRGYnl0xzW8FJ6uYsmwIDAQABAoIBACi0BKcyQ3HElSJC
|
||||
kaAao+Uvnzh4yvPg8Nwf5JDIp/uDdTMyIEWLtrLczRWrjGVZYbsVROinP5VfnPTT
|
||||
kYwkfKINj2u+gC6lsNuPnRuvHXikF8eO/mYvCTur1zZvsQnF5kp4GGwIqr+qoPUP
|
||||
bB0UMndG1PdpoMryHe+JcrvTrLHDmCeH10TqOwMsQMLHYLkowvxwJWsmTY7/Qr5S
|
||||
Wm3PPpOcW2i0uyPVuyuv4yD1368fqnqJ8QFsQp1K6QtYsNnJ71Hut1/IoxK/e6hj
|
||||
5Z+byKtHVtmcLnABuoOT7BhleJNFBksX9sh83jid4tMBgci+zXNeGmgqo2EmaWAb
|
||||
agQslkECgYEA8B1rzjOHVQx/vwSzDa4XOrpoHQRfyElrGNz9JVBvnoC7AorezBXQ
|
||||
M9WTHQIFTGMjzD8pb+YJGi3gj93VN51r0SmJRxBaBRh1ZZI9kFiFzngYev8POgD3
|
||||
ygmlS3kTHCNxCK/CJkB+/jMBgtPj5ygDpCWVcTSuWlQFphePkW7jaaECgYEA1Blz
|
||||
ulqgAyJHZaqgcbcCsI2q6m527hVr9pjzNjIVmkwu38yS9RTCgdlbEVVDnS0hoifl
|
||||
+jVMEGXjF3xjyMvL50BKbQUH+KAa+V4n1WGlnZOxX9TMny8MBjEuSX2+362vQ3BX
|
||||
4vOlX00gvoc+sY+lrzvfx/OdPCHQGVYzoKCxhLsCgYA07HcviuIAV/HsO2/vyvhp
|
||||
xF5gTu+BqNUHNOZDDDid+ge+Jre2yfQLCL8VPLXIQW3Jff53IH/PGl+NtjphuLvj
|
||||
7UDJvgvpZZuymIojP6+2c3gJ3CASC9aR3JBnUzdoE1O9s2eaoMqc4scpe+SWtZYf
|
||||
3vzSZ+cqF6zrD/Rf/M35IQKBgHTU4E6ShPm09CcoaeC5sp2WK8OevZw/6IyZi78a
|
||||
r5Oiy18zzO97U/k6xVMy6F+38ILl/2Rn31JZDVJujniY6eSkIVsUHmPxrWoXV1HO
|
||||
y++U32uuSFiXDcSLarfIsE992MEJLSAynbF1Rsgsr3gXbGiuToJRyxbIeVy7gwzD
|
||||
94TpAoGAY4/PejWQj9psZfAhyk5dRGra++gYRQ/gK1IIc1g+Dd2/BxbT/RHr05GK
|
||||
6vwrfjsoRyMWteC1SsNs/CurjfQ/jqCfHNP5XPvxgd5Ec8sRJIiV7V5RTuWJsPu1
|
||||
+3K6cnKEyg+0ekYmLertRFIY6SwWmY1fyKgTvxudMcsBY7dC4xs=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`)
|
||||
|
||||
func writeTempFile(byts []byte) (string, error) {
|
||||
tmpf, err := os.CreateTemp(os.TempDir(), "rtsp-")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer tmpf.Close()
|
||||
|
||||
_, err = tmpf.Write(byts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tmpf.Name(), nil
|
||||
}
|
||||
|
||||
func newInstance(conf string) (*core.Core, bool) {
|
||||
if conf == "" {
|
||||
return core.New([]string{})
|
||||
}
|
||||
|
||||
tmpf, err := writeTempFile([]byte(conf))
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
defer os.Remove(tmpf)
|
||||
|
||||
return core.New([]string{tmpf})
|
||||
}
|
||||
|
||||
type container struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func newContainer(image string, name string, args []string) (*container, error) {
|
||||
c := &container{
|
||||
name: name,
|
||||
}
|
||||
|
||||
exec.Command("docker", "kill", "rtsp-simple-server-test-"+name).Run()
|
||||
exec.Command("docker", "wait", "rtsp-simple-server-test-"+name).Run()
|
||||
|
||||
// --network=host is needed to test multicast
|
||||
cmd := []string{
|
||||
"docker", "run",
|
||||
"--network=host",
|
||||
"--name=rtsp-simple-server-test-" + name,
|
||||
"rtsp-simple-server-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", "rtsp-simple-server-test-"+c.name).Run()
|
||||
exec.Command("docker", "wait", "rtsp-simple-server-test-"+c.name).Run()
|
||||
exec.Command("docker", "rm", "rtsp-simple-server-test-"+c.name).Run()
|
||||
}
|
||||
|
||||
func (c *container) wait() int {
|
||||
exec.Command("docker", "wait", "rtsp-simple-server-test-"+c.name).Run()
|
||||
out, _ := exec.Command("docker", "inspect", "rtsp-simple-server-test-"+c.name,
|
||||
"-f", "{{.State.ExitCode}}").Output()
|
||||
code, _ := strconv.ParseInt(string(out[:len(out)-1]), 10, 64)
|
||||
return int(code)
|
||||
}
|
20
scripts/test-highlevel.mk
Normal file
20
scripts/test-highlevel.mk
Normal file
@ -0,0 +1,20 @@
|
||||
test-highlevel-nodocker:
|
||||
go test -v -race -tags enable_highlevel_tests ./internal/highleveltests
|
||||
|
||||
define DOCKERFILE_HIGHLEVEL_TEST
|
||||
FROM $(BASE_IMAGE)
|
||||
RUN apk add --no-cache make docker-cli gcc musl-dev
|
||||
WORKDIR /s
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . ./
|
||||
endef
|
||||
export DOCKERFILE_HIGHLEVEL_TEST
|
||||
|
||||
test-highlevel:
|
||||
echo "$$DOCKERFILE_HIGHLEVEL_TEST" | docker build -q . -f - -t temp
|
||||
docker run --rm -it \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
--network=host \
|
||||
temp \
|
||||
make test-highlevel-nodocker
|
@ -1,7 +1,7 @@
|
||||
define DOCKERFILE_TEST
|
||||
ARG ARCH
|
||||
FROM $$ARCH/$(BASE_IMAGE)
|
||||
RUN apk add --no-cache make docker-cli gcc musl-dev
|
||||
RUN apk add --no-cache make gcc musl-dev
|
||||
WORKDIR /s
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
@ -11,8 +11,6 @@ export DOCKERFILE_TEST
|
||||
test:
|
||||
echo "$$DOCKERFILE_TEST" | docker build -q . -f - -t temp --build-arg ARCH=amd64
|
||||
docker run --rm \
|
||||
--network=host \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
-v $(PWD):/s \
|
||||
temp \
|
||||
make test-nodocker COVERAGE=1
|
||||
@ -20,8 +18,6 @@ test:
|
||||
test32:
|
||||
echo "$$DOCKERFILE_TEST" | docker build -q . -f - -t temp --build-arg ARCH=i386
|
||||
docker run --rm \
|
||||
--network=host \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
-v $(PWD):/s \
|
||||
temp \
|
||||
make test-nodocker COVERAGE=0
|
||||
@ -36,8 +32,6 @@ test-internal:
|
||||
$$(go list ./internal/... | grep -v /core)
|
||||
|
||||
test-core:
|
||||
$(foreach IMG,$(shell echo testimages/*/ | xargs -n1 basename), \
|
||||
docker build -q testimages/$(IMG) -t rtsp-simple-server-test-$(IMG)$(NL))
|
||||
go test -v $(TEST_CORE_OPTS) ./internal/core
|
||||
|
||||
test-nodocker: test-internal test-core
|
||||
|
Loading…
Reference in New Issue
Block a user