parent
810643ab30
commit
5ca5005040
|
@ -71,7 +71,12 @@ docker run --rm -it -v $PWD/rtsp-simple-server.yml:/rtsp-simple-server.yml -p 85
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
To see or change the configuration, edit the `rtsp-simple-server.yml` file, provided with the executable. The default configuration is [available here](rtsp-simple-server.yml).
|
To see or change the configuration, edit the `rtsp-simple-server.yml` file, provided with the executable, and also [available here](rtsp-simple-server.yml).
|
||||||
|
|
||||||
|
The configuration can be overridden with environment variables in the format `RTSP_PARAMNAME`, where `PARAMNAME` is the name of a parameter, in uppercase. For instance, the `rtspPort` parameter can be overridden in the following way:
|
||||||
|
```
|
||||||
|
RTSP_RTSPPORT=8555 ./rtsp-simple-server
|
||||||
|
```
|
||||||
|
|
||||||
### RTSP proxy mode
|
### RTSP proxy mode
|
||||||
|
|
||||||
|
|
13
conf.go
13
conf.go
|
@ -12,6 +12,8 @@ import (
|
||||||
"github.com/aler9/gortsplib"
|
"github.com/aler9/gortsplib"
|
||||||
"github.com/aler9/gortsplib/headers"
|
"github.com/aler9/gortsplib/headers"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"github.com/aler9/rtsp-simple-server/confenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type pathConf struct {
|
type pathConf struct {
|
||||||
|
@ -57,16 +59,16 @@ type conf struct {
|
||||||
func loadConf(fpath string, stdin io.Reader) (*conf, error) {
|
func loadConf(fpath string, stdin io.Reader) (*conf, error) {
|
||||||
conf := &conf{}
|
conf := &conf{}
|
||||||
|
|
||||||
|
// read from file or stdin
|
||||||
err := func() error {
|
err := func() error {
|
||||||
if fpath == "stdin" {
|
if fpath == "stdin" {
|
||||||
err := yaml.NewDecoder(stdin).Decode(conf)
|
err := yaml.NewDecoder(stdin).Decode(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
// rtsp-simple-server.yml is optional
|
// rtsp-simple-server.yml is optional
|
||||||
if fpath == "rtsp-simple-server.yml" {
|
if fpath == "rtsp-simple-server.yml" {
|
||||||
if _, err := os.Stat(fpath); err != nil {
|
if _, err := os.Stat(fpath); err != nil {
|
||||||
|
@ -86,12 +88,17 @@ func loadConf(fpath string, stdin io.Reader) (*conf, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read from environment
|
||||||
|
err = confenv.Process("RTSP", conf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if len(conf.Protocols) == 0 {
|
if len(conf.Protocols) == 0 {
|
||||||
conf.Protocols = []string{"udp", "tcp"}
|
conf.Protocols = []string{"udp", "tcp"}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
package confenv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func process(env map[string]string, envKey string, rv reflect.Value) error {
|
||||||
|
rt := rv.Type()
|
||||||
|
|
||||||
|
switch rt {
|
||||||
|
case reflect.TypeOf(time.Duration(0)):
|
||||||
|
if ev, ok := env[envKey]; ok {
|
||||||
|
d, err := time.ParseDuration(ev)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %s", envKey, err)
|
||||||
|
}
|
||||||
|
rv.Set(reflect.ValueOf(d))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch rt.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
if ev, ok := env[envKey]; ok {
|
||||||
|
rv.SetString(ev)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case reflect.Int:
|
||||||
|
if ev, ok := env[envKey]; ok {
|
||||||
|
iv, err := strconv.ParseInt(ev, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %s", envKey, err)
|
||||||
|
}
|
||||||
|
rv.SetInt(iv)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case reflect.Bool:
|
||||||
|
if ev, ok := env[envKey]; ok {
|
||||||
|
switch strings.ToLower(ev) {
|
||||||
|
case "yes", "true":
|
||||||
|
rv.SetBool(true)
|
||||||
|
|
||||||
|
case "no", "false":
|
||||||
|
rv.SetBool(false)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%s: invalid value '%s'", envKey, ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
if rt.Elem().Kind() == reflect.String {
|
||||||
|
if ev, ok := env[envKey]; ok {
|
||||||
|
nv := reflect.Zero(rt)
|
||||||
|
for _, sv := range strings.Split(ev, ",") {
|
||||||
|
nv = reflect.Append(nv, reflect.ValueOf(sv))
|
||||||
|
}
|
||||||
|
rv.Set(nv)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Map:
|
||||||
|
for k := range env {
|
||||||
|
if !strings.HasPrefix(k, envKey) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp := strings.Split(strings.TrimPrefix(k[len(envKey):], "_"), "_")
|
||||||
|
mapKey := strings.ToLower(tmp[0])
|
||||||
|
|
||||||
|
nv := rv.MapIndex(reflect.ValueOf(mapKey))
|
||||||
|
zero := reflect.Value{}
|
||||||
|
if nv == zero {
|
||||||
|
nv = reflect.New(rt.Elem().Elem())
|
||||||
|
rv.SetMapIndex(reflect.ValueOf(mapKey), nv)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := process(env, envKey+"_"+strings.ToUpper(mapKey), nv.Elem())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
flen := rt.NumField()
|
||||||
|
for i := 0; i < flen; i++ {
|
||||||
|
fieldName := rt.Field(i).Name
|
||||||
|
|
||||||
|
// process only public fields
|
||||||
|
if fieldName[0] < 'A' || fieldName[0] > 'Z' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldEnvKey := envKey + "_" + strings.ToUpper(fieldName)
|
||||||
|
err := process(env, fieldEnvKey, rv.Field(i))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("unsupported type: %v", rt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Process(envKey string, v interface{}) error {
|
||||||
|
env := make(map[string]string)
|
||||||
|
for _, kv := range os.Environ() {
|
||||||
|
tmp := strings.Split(kv, "=")
|
||||||
|
env[tmp[0]] = tmp[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return process(env, envKey, reflect.ValueOf(v).Elem())
|
||||||
|
}
|
66
main_test.go
66
main_test.go
|
@ -3,12 +3,14 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/aler9/gortsplib"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -97,6 +99,70 @@ func (c *container) ip() string {
|
||||||
return string(out[:len(out)-1])
|
return string(out[:len(out)-1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEnvironment(t *testing.T) {
|
||||||
|
// string
|
||||||
|
os.Setenv("RTSP_RUNONCONNECT", "testcmd")
|
||||||
|
defer os.Unsetenv("RTSP_RUNONCONNECT")
|
||||||
|
|
||||||
|
// int
|
||||||
|
os.Setenv("RTSP_RTSPPORT", "8555")
|
||||||
|
defer os.Unsetenv("RTSP_RTSPPORT")
|
||||||
|
|
||||||
|
// bool
|
||||||
|
os.Setenv("RTSP_METRICS", "yes")
|
||||||
|
defer os.Unsetenv("RTSP_METRICS")
|
||||||
|
|
||||||
|
// duration
|
||||||
|
os.Setenv("RTSP_READTIMEOUT", "22s")
|
||||||
|
defer os.Unsetenv("RTSP_READTIMEOUT")
|
||||||
|
|
||||||
|
// slice
|
||||||
|
os.Setenv("RTSP_LOGDESTINATIONS", "stdout,file")
|
||||||
|
defer os.Unsetenv("RTSP_LOGDESTINATIONS")
|
||||||
|
|
||||||
|
// map key
|
||||||
|
os.Setenv("RTSP_PATHS_TEST2", "")
|
||||||
|
defer os.Unsetenv("RTSP_PATHS_TEST2")
|
||||||
|
|
||||||
|
// map value
|
||||||
|
os.Setenv("RTSP_PATHS_TEST_SOURCE", "rtsp://testing")
|
||||||
|
defer os.Unsetenv("RTSP_PATHS_TEST_SOURCE")
|
||||||
|
os.Setenv("RTSP_PATHS_TEST_SOURCEPROTOCOL", "tcp")
|
||||||
|
defer os.Unsetenv("RTSP_PATHS_TEST_SOURCEPROTOCOL")
|
||||||
|
|
||||||
|
p, err := newProgram([]string{}, bytes.NewBuffer(nil))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer p.close()
|
||||||
|
|
||||||
|
require.Equal(t, "testcmd", p.conf.RunOnConnect)
|
||||||
|
|
||||||
|
require.Equal(t, 8555, p.conf.RtspPort)
|
||||||
|
|
||||||
|
require.Equal(t, true, p.conf.Metrics)
|
||||||
|
|
||||||
|
require.Equal(t, 22*time.Second, p.conf.ReadTimeout)
|
||||||
|
|
||||||
|
require.Equal(t, []string{"stdout", "file"}, p.conf.LogDestinations)
|
||||||
|
|
||||||
|
pa, ok := p.conf.Paths["test2"]
|
||||||
|
require.Equal(t, true, ok)
|
||||||
|
require.Equal(t, &pathConf{
|
||||||
|
Source: "record",
|
||||||
|
}, pa)
|
||||||
|
|
||||||
|
pa, ok = p.conf.Paths["test"]
|
||||||
|
require.Equal(t, true, ok)
|
||||||
|
require.Equal(t, &pathConf{
|
||||||
|
Source: "rtsp://testing",
|
||||||
|
sourceUrl: func() *url.URL {
|
||||||
|
u, _ := url.Parse("rtsp://testing:554")
|
||||||
|
return u
|
||||||
|
}(),
|
||||||
|
SourceProtocol: "tcp",
|
||||||
|
sourceProtocolParsed: gortsplib.StreamProtocolTCP,
|
||||||
|
}, pa)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPublish(t *testing.T) {
|
func TestPublish(t *testing.T) {
|
||||||
for _, conf := range []struct {
|
for _, conf := range []struct {
|
||||||
publishSoft string
|
publishSoft string
|
||||||
|
|
Loading…
Reference in New Issue