mirror of
https://github.com/aler9/rtsp-simple-server
synced 2025-10-05 15:46:58 +08:00
move high-level tests into dedicate workflow (#1219)
This commit is contained in:
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 " format format source files"
|
||||||
@echo " test run tests"
|
@echo " test run tests"
|
||||||
@echo " test32 run tests on a 32-bit system"
|
@echo " test32 run tests on a 32-bit system"
|
||||||
|
@echo " test-highlevel run high-level tests"
|
||||||
@echo " lint run linters"
|
@echo " lint run linters"
|
||||||
@echo " bench NAME=n run bench environment"
|
@echo " bench NAME=n run bench environment"
|
||||||
@echo " run run app"
|
@echo " run run app"
|
||||||
|
@@ -66,7 +66,7 @@ func TestAPIConfigGet(t *testing.T) {
|
|||||||
|
|
||||||
p, ok := newInstance("api: yes\n")
|
p, ok := newInstance("api: yes\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
var out map[string]interface{}
|
var out map[string]interface{}
|
||||||
err := httpRequest(http.MethodGet, "http://localhost:9997/v1/config/get", nil, &out)
|
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) {
|
func TestAPIConfigSet(t *testing.T) {
|
||||||
p, ok := newInstance("api: yes\n")
|
p, ok := newInstance("api: yes\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
err := httpRequest(http.MethodPost, "http://localhost:9997/v1/config/set", map[string]interface{}{
|
err := httpRequest(http.MethodPost, "http://localhost:9997/v1/config/set", map[string]interface{}{
|
||||||
"rtmpDisable": true,
|
"rtmpDisable": true,
|
||||||
@@ -99,7 +99,7 @@ func TestAPIConfigSet(t *testing.T) {
|
|||||||
func TestAPIConfigPathsAdd(t *testing.T) {
|
func TestAPIConfigPathsAdd(t *testing.T) {
|
||||||
p, ok := newInstance("api: yes\n")
|
p, ok := newInstance("api: yes\n")
|
||||||
require.Equal(t, true, ok)
|
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{}{
|
err := httpRequest(http.MethodPost, "http://localhost:9997/v1/config/paths/add/my/path", map[string]interface{}{
|
||||||
"source": "rtsp://127.0.0.1:9999/mypath",
|
"source": "rtsp://127.0.0.1:9999/mypath",
|
||||||
@@ -117,7 +117,7 @@ func TestAPIConfigPathsAdd(t *testing.T) {
|
|||||||
func TestAPIConfigPathsEdit(t *testing.T) {
|
func TestAPIConfigPathsEdit(t *testing.T) {
|
||||||
p, ok := newInstance("api: yes\n")
|
p, ok := newInstance("api: yes\n")
|
||||||
require.Equal(t, true, ok)
|
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{}{
|
err := httpRequest(http.MethodPost, "http://localhost:9997/v1/config/paths/add/my/path", map[string]interface{}{
|
||||||
"source": "rtsp://127.0.0.1:9999/mypath",
|
"source": "rtsp://127.0.0.1:9999/mypath",
|
||||||
@@ -144,7 +144,7 @@ func TestAPIConfigPathsEdit(t *testing.T) {
|
|||||||
func TestAPIConfigPathsRemove(t *testing.T) {
|
func TestAPIConfigPathsRemove(t *testing.T) {
|
||||||
p, ok := newInstance("api: yes\n")
|
p, ok := newInstance("api: yes\n")
|
||||||
require.Equal(t, true, ok)
|
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{}{
|
err := httpRequest(http.MethodPost, "http://localhost:9997/v1/config/paths/add/my/path", map[string]interface{}{
|
||||||
"source": "rtsp://127.0.0.1:9999/mypath",
|
"source": "rtsp://127.0.0.1:9999/mypath",
|
||||||
@@ -184,7 +184,7 @@ func TestAPIPathsList(t *testing.T) {
|
|||||||
"paths:\n" +
|
"paths:\n" +
|
||||||
" mypath:\n")
|
" mypath:\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
tracks := gortsplib.Tracks{
|
tracks := gortsplib.Tracks{
|
||||||
&gortsplib.TrackH264{
|
&gortsplib.TrackH264{
|
||||||
@@ -242,7 +242,7 @@ func TestAPIPathsList(t *testing.T) {
|
|||||||
"paths:\n" +
|
"paths:\n" +
|
||||||
" mypath:\n")
|
" mypath:\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
tracks := gortsplib.Tracks{
|
tracks := gortsplib.Tracks{
|
||||||
&gortsplib.TrackH264{
|
&gortsplib.TrackH264{
|
||||||
@@ -291,7 +291,7 @@ func TestAPIPathsList(t *testing.T) {
|
|||||||
" source: rtsp://127.0.0.1:1234/mypath\n" +
|
" source: rtsp://127.0.0.1:1234/mypath\n" +
|
||||||
" sourceOnDemand: yes\n")
|
" sourceOnDemand: yes\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
var out pathList
|
var out pathList
|
||||||
err := httpRequest(http.MethodGet, "http://localhost:9997/v1/paths/list", nil, &out)
|
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" +
|
" source: rtmp://127.0.0.1:1234/mypath\n" +
|
||||||
" sourceOnDemand: yes\n")
|
" sourceOnDemand: yes\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
var out pathList
|
var out pathList
|
||||||
err := httpRequest(http.MethodGet, "http://localhost:9997/v1/paths/list", nil, &out)
|
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" +
|
" source: http://127.0.0.1:1234/mypath\n" +
|
||||||
" sourceOnDemand: yes\n")
|
" sourceOnDemand: yes\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
var out pathList
|
var out pathList
|
||||||
err := httpRequest(http.MethodGet, "http://localhost:9997/v1/paths/list", nil, &out)
|
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)
|
p, ok := newInstance(conf)
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
track := &gortsplib.TrackH264{
|
track := &gortsplib.TrackH264{
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
@@ -561,7 +561,7 @@ func TestAPIKick(t *testing.T) {
|
|||||||
|
|
||||||
p, ok := newInstance(conf)
|
p, ok := newInstance(conf)
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
track := &gortsplib.TrackH264{
|
track := &gortsplib.TrackH264{
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
@@ -589,16 +589,29 @@ func TestAPIKick(t *testing.T) {
|
|||||||
defer source.Close()
|
defer source.Close()
|
||||||
|
|
||||||
case "rtmp":
|
case "rtmp":
|
||||||
cnt1, err := newContainer("ffmpeg", "source", []string{
|
u, err := url.Parse("rtmp://localhost:1935/mypath")
|
||||||
"-re",
|
require.NoError(t, err)
|
||||||
"-stream_loop", "-1",
|
|
||||||
"-i", "emptyvideo.mkv",
|
nconn, err := net.Dial("tcp", u.Host)
|
||||||
"-c", "copy",
|
require.NoError(t, err)
|
||||||
"-f", "flv",
|
defer nconn.Close()
|
||||||
"rtmp://localhost:1935/test1/test2",
|
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)
|
require.NoError(t, err)
|
||||||
defer cnt1.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var pa string
|
var pa string
|
||||||
|
@@ -103,7 +103,8 @@ func New(args []string) (*Core, bool) {
|
|||||||
return p, true
|
return p, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Core) close() {
|
// Close closes Core and waits for all goroutines to return.
|
||||||
|
func (p *Core) Close() {
|
||||||
p.ctxCancel()
|
p.ctxCancel()
|
||||||
<-p.done
|
<-p.done
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -70,54 +69,6 @@ y++U32uuSFiXDcSLarfIsE992MEJLSAynbF1Rsgsr3gXbGiuToJRyxbIeVy7gwzD
|
|||||||
-----END RSA PRIVATE KEY-----
|
-----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) {
|
func writeTempFile(byts []byte) (string, error) {
|
||||||
tmpf, err := os.CreateTemp(os.TempDir(), "rtsp-")
|
tmpf, err := os.CreateTemp(os.TempDir(), "rtsp-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -153,7 +104,7 @@ func TestCorePathAutoDeletion(t *testing.T) {
|
|||||||
p, ok := newInstance("paths:\n" +
|
p, ok := newInstance("paths:\n" +
|
||||||
" all:\n")
|
" all:\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
@@ -288,7 +239,7 @@ func main() {
|
|||||||
" runOnDemand: %s\n"+
|
" runOnDemand: %s\n"+
|
||||||
" runOnDemandCloseAfter: 1s\n", execFile))
|
" runOnDemandCloseAfter: 1s\n", execFile))
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p1.close()
|
defer p1.Close()
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
@@ -367,7 +318,7 @@ func TestCorePathRunOnReady(t *testing.T) {
|
|||||||
" runOnReady: touch %s\n",
|
" runOnReady: touch %s\n",
|
||||||
doneFile))
|
doneFile))
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
track := &gortsplib.TrackH264{
|
track := &gortsplib.TrackH264{
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
@@ -402,7 +353,7 @@ func TestCoreHotReloading(t *testing.T) {
|
|||||||
|
|
||||||
p, ok := New([]string{confPath})
|
p, ok := New([]string{confPath})
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
track := &gortsplib.TrackH264{
|
track := &gortsplib.TrackH264{
|
||||||
|
@@ -6,7 +6,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -79,7 +78,7 @@ func (ts *testHTTPAuthenticator) onAuth(ctx *gin.Context) {
|
|||||||
func TestHLSServerNotFound(t *testing.T) {
|
func TestHLSServerNotFound(t *testing.T) {
|
||||||
p, ok := newInstance("")
|
p, ok := newInstance("")
|
||||||
require.Equal(t, true, ok)
|
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)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8888/stream/", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -89,109 +88,3 @@ func TestHLSServerNotFound(t *testing.T) {
|
|||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
require.Equal(t, http.StatusNotFound, res.StatusCode)
|
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" +
|
" source: http://localhost:5780/stream.m3u8\n" +
|
||||||
" sourceOnDemand: yes\n")
|
" sourceOnDemand: yes\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
@@ -2,13 +2,17 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/aler9/gortsplib"
|
"github.com/aler9/gortsplib"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/aler9/rtsp-simple-server/internal/rtmp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMetrics(t *testing.T) {
|
func TestMetrics(t *testing.T) {
|
||||||
@@ -27,7 +31,7 @@ func TestMetrics(t *testing.T) {
|
|||||||
"paths:\n" +
|
"paths:\n" +
|
||||||
" all:\n")
|
" all:\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
track := &gortsplib.TrackH264{
|
track := &gortsplib.TrackH264{
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
@@ -42,16 +46,29 @@ func TestMetrics(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer source.Close()
|
defer source.Close()
|
||||||
|
|
||||||
cnt1, err := newContainer("ffmpeg", "source", []string{
|
u, err := url.Parse("rtmp://localhost:1935/rtmp_path")
|
||||||
"-re",
|
require.NoError(t, err)
|
||||||
"-stream_loop", "-1",
|
|
||||||
"-i", "emptyvideo.mkv",
|
nconn, err := net.Dial("tcp", u.Host)
|
||||||
"-c", "copy",
|
require.NoError(t, err)
|
||||||
"-f", "flv",
|
defer nconn.Close()
|
||||||
"rtmp://localhost:1935/rtmp_path",
|
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)
|
require.NoError(t, err)
|
||||||
defer cnt1.close()
|
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
res, err := http.Get("http://localhost:8888/rtsp_path/index.m3u8")
|
res, err := http.Get("http://localhost:8888/rtsp_path/index.m3u8")
|
||||||
|
@@ -29,7 +29,7 @@ func TestRTMPServerPublishRead(t *testing.T) {
|
|||||||
"paths:\n" +
|
"paths:\n" +
|
||||||
" all:\n")
|
" all:\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
} else {
|
} else {
|
||||||
port = "1936"
|
port = "1936"
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ func TestRTMPServerPublishRead(t *testing.T) {
|
|||||||
"paths:\n" +
|
"paths:\n" +
|
||||||
" all:\n")
|
" all:\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := url.Parse("rtmp://127.0.0.1:" + port + "/mystream")
|
u, err := url.Parse("rtmp://127.0.0.1:" + port + "/mystream")
|
||||||
@@ -169,7 +169,7 @@ func TestRTMPServerAuth(t *testing.T) {
|
|||||||
|
|
||||||
p, ok := newInstance(conf)
|
p, ok := newInstance(conf)
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
var a *testHTTPAuthenticator
|
var a *testHTTPAuthenticator
|
||||||
if ca == "external" {
|
if ca == "external" {
|
||||||
@@ -239,7 +239,7 @@ func TestRTMPServerAuthFail(t *testing.T) {
|
|||||||
" publishUser: testuser2\n" +
|
" publishUser: testuser2\n" +
|
||||||
" publishPass: testpass\n")
|
" publishPass: testpass\n")
|
||||||
require.Equal(t, true, ok)
|
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")
|
u1, err := url.Parse("rtmp://127.0.0.1:1935/teststream?user=testuser&pass=testpass")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -289,7 +289,7 @@ func TestRTMPServerAuthFail(t *testing.T) {
|
|||||||
"paths:\n" +
|
"paths:\n" +
|
||||||
" all:\n")
|
" all:\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
a, err := newTestHTTPAuthenticator("publish")
|
a, err := newTestHTTPAuthenticator("publish")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -346,7 +346,7 @@ func TestRTMPServerAuthFail(t *testing.T) {
|
|||||||
" readUser: testuser2\n" +
|
" readUser: testuser2\n" +
|
||||||
" readPass: testpass\n")
|
" readPass: testpass\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
u1, err := url.Parse("rtmp://127.0.0.1:1935/teststream")
|
u1, err := url.Parse("rtmp://127.0.0.1:1935/teststream")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@@ -102,7 +102,7 @@ func TestRTMPSource(t *testing.T) {
|
|||||||
" source: rtmp://localhost:1937/teststream\n" +
|
" source: rtmp://localhost:1937/teststream\n" +
|
||||||
" sourceOnDemand: yes\n")
|
" sourceOnDemand: yes\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
} else {
|
} else {
|
||||||
p, ok := newInstance("paths:\n" +
|
p, ok := newInstance("paths:\n" +
|
||||||
" proxied:\n" +
|
" proxied:\n" +
|
||||||
@@ -110,7 +110,7 @@ func TestRTMPSource(t *testing.T) {
|
|||||||
" sourceFingerprint: 33949E05FFFB5FF3E8AA16F8213A6251B4D9363804BA53233C4DA9A46D6F2739\n" +
|
" sourceFingerprint: 33949E05FFFB5FF3E8AA16F8213A6251B4D9363804BA53233C4DA9A46D6F2739\n" +
|
||||||
" sourceOnDemand: yes\n")
|
" sourceOnDemand: yes\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
c := gortsplib.Client{
|
c := gortsplib.Client{
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aler9/gortsplib"
|
"github.com/aler9/gortsplib"
|
||||||
"github.com/aler9/gortsplib/pkg/url"
|
"github.com/aler9/gortsplib/pkg/url"
|
||||||
@@ -11,191 +9,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"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) {
|
func TestRTSPServerAuth(t *testing.T) {
|
||||||
for _, ca := range []string{
|
for _, ca := range []string{
|
||||||
"internal",
|
"internal",
|
||||||
@@ -222,7 +35,7 @@ func TestRTSPServerAuth(t *testing.T) {
|
|||||||
|
|
||||||
p, ok := newInstance(conf)
|
p, ok := newInstance(conf)
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
var a *testHTTPAuthenticator
|
var a *testHTTPAuthenticator
|
||||||
if ca == "external" {
|
if ca == "external" {
|
||||||
@@ -278,7 +91,7 @@ func TestRTSPServerAuth(t *testing.T) {
|
|||||||
" publishUser: sha256:rl3rgi4NcZkpAEcacZnQ2VuOfJ0FxAqCRaKB/SwdZoQ=\n" +
|
" publishUser: sha256:rl3rgi4NcZkpAEcacZnQ2VuOfJ0FxAqCRaKB/SwdZoQ=\n" +
|
||||||
" publishPass: sha256:E9JJ8stBJ7QM+nV4ZoUCeHk/gU3tPFh/5YieiJp6n2w=\n")
|
" publishPass: sha256:E9JJ8stBJ7QM+nV4ZoUCeHk/gU3tPFh/5YieiJp6n2w=\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
track := &gortsplib.TrackH264{
|
track := &gortsplib.TrackH264{
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
@@ -326,7 +139,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
|
|||||||
" publishUser: testuser\n" +
|
" publishUser: testuser\n" +
|
||||||
" publishPass: testpass\n")
|
" publishPass: testpass\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
track := &gortsplib.TrackH264{
|
track := &gortsplib.TrackH264{
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
@@ -373,7 +186,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
|
|||||||
" readUser: testuser\n" +
|
" readUser: testuser\n" +
|
||||||
" readPass: testpass\n")
|
" readPass: testpass\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
|
|
||||||
@@ -396,7 +209,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
|
|||||||
" all:\n" +
|
" all:\n" +
|
||||||
" publishIPs: [128.0.0.1/32]\n")
|
" publishIPs: [128.0.0.1/32]\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
track := &gortsplib.TrackH264{
|
track := &gortsplib.TrackH264{
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
@@ -418,7 +231,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
|
|||||||
"paths:\n" +
|
"paths:\n" +
|
||||||
" all:\n")
|
" all:\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
a, err := newTestHTTPAuthenticator("publish")
|
a, err := newTestHTTPAuthenticator("publish")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -457,7 +270,7 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
|
|||||||
|
|
||||||
p, ok := newInstance(conf)
|
p, ok := newInstance(conf)
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
track := &gortsplib.TrackH264{
|
track := &gortsplib.TrackH264{
|
||||||
PayloadType: 96,
|
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) {
|
func TestRTSPServerFallback(t *testing.T) {
|
||||||
for _, ca := range []string{
|
for _, ca := range []string{
|
||||||
"absolute",
|
"absolute",
|
||||||
@@ -603,7 +379,7 @@ func TestRTSPServerFallback(t *testing.T) {
|
|||||||
" fallback: " + val + "\n" +
|
" fallback: " + val + "\n" +
|
||||||
" path2:\n")
|
" path2:\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p1.close()
|
defer p1.Close()
|
||||||
|
|
||||||
source := gortsplib.Client{}
|
source := gortsplib.Client{}
|
||||||
err := source.StartPublishing("rtsp://localhost:8554/path2",
|
err := source.StartPublishing("rtsp://localhost:8554/path2",
|
||||||
|
@@ -134,7 +134,7 @@ func TestRTSPSource(t *testing.T) {
|
|||||||
" sourceProtocol: " + source + "\n" +
|
" sourceProtocol: " + source + "\n" +
|
||||||
" sourceOnDemand: yes\n")
|
" sourceOnDemand: yes\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
} else {
|
} else {
|
||||||
p, ok := newInstance("paths:\n" +
|
p, ok := newInstance("paths:\n" +
|
||||||
" proxied:\n" +
|
" proxied:\n" +
|
||||||
@@ -142,7 +142,7 @@ func TestRTSPSource(t *testing.T) {
|
|||||||
" sourceFingerprint: 33949E05FFFB5FF3E8AA16F8213A6251B4D9363804BA53233C4DA9A46D6F2739\n" +
|
" sourceFingerprint: 33949E05FFFB5FF3E8AA16F8213A6251B4D9363804BA53233C4DA9A46D6F2739\n" +
|
||||||
" sourceOnDemand: yes\n")
|
" sourceOnDemand: yes\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
received := make(chan struct{})
|
received := make(chan struct{})
|
||||||
@@ -231,7 +231,7 @@ func TestRTSPSourceNoPassword(t *testing.T) {
|
|||||||
" source: rtsp://testuser:@127.0.0.1:8555/teststream\n" +
|
" source: rtsp://testuser:@127.0.0.1:8555/teststream\n" +
|
||||||
" sourceProtocol: tcp\n")
|
" sourceProtocol: tcp\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
@@ -275,7 +275,7 @@ func TestRTSPSourceDynamicH264Params(t *testing.T) {
|
|||||||
" proxied:\n" +
|
" proxied:\n" +
|
||||||
" source: rtsp://127.0.0.1:8555/teststream\n")
|
" source: rtsp://127.0.0.1:8555/teststream\n")
|
||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.Close()
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
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
|
define DOCKERFILE_TEST
|
||||||
ARG ARCH
|
ARG ARCH
|
||||||
FROM $$ARCH/$(BASE_IMAGE)
|
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
|
WORKDIR /s
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
@@ -11,8 +11,6 @@ export DOCKERFILE_TEST
|
|||||||
test:
|
test:
|
||||||
echo "$$DOCKERFILE_TEST" | docker build -q . -f - -t temp --build-arg ARCH=amd64
|
echo "$$DOCKERFILE_TEST" | docker build -q . -f - -t temp --build-arg ARCH=amd64
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
--network=host \
|
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
|
||||||
-v $(PWD):/s \
|
-v $(PWD):/s \
|
||||||
temp \
|
temp \
|
||||||
make test-nodocker COVERAGE=1
|
make test-nodocker COVERAGE=1
|
||||||
@@ -20,8 +18,6 @@ test:
|
|||||||
test32:
|
test32:
|
||||||
echo "$$DOCKERFILE_TEST" | docker build -q . -f - -t temp --build-arg ARCH=i386
|
echo "$$DOCKERFILE_TEST" | docker build -q . -f - -t temp --build-arg ARCH=i386
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
--network=host \
|
|
||||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
|
||||||
-v $(PWD):/s \
|
-v $(PWD):/s \
|
||||||
temp \
|
temp \
|
||||||
make test-nodocker COVERAGE=0
|
make test-nodocker COVERAGE=0
|
||||||
@@ -36,8 +32,6 @@ test-internal:
|
|||||||
$$(go list ./internal/... | grep -v /core)
|
$$(go list ./internal/... | grep -v /core)
|
||||||
|
|
||||||
test-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
|
go test -v $(TEST_CORE_OPTS) ./internal/core
|
||||||
|
|
||||||
test-nodocker: test-internal test-core
|
test-nodocker: test-internal test-core
|
||||||
|
Reference in New Issue
Block a user