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
|
||||
|
||||
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
|
||||
|
||||
|
|
51
conf.go
51
conf.go
|
@ -12,6 +12,8 @@ import (
|
|||
"github.com/aler9/gortsplib"
|
||||
"github.com/aler9/gortsplib/headers"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/confenv"
|
||||
)
|
||||
|
||||
type pathConf struct {
|
||||
|
@ -57,41 +59,46 @@ type conf struct {
|
|||
func loadConf(fpath string, stdin io.Reader) (*conf, error) {
|
||||
conf := &conf{}
|
||||
|
||||
// read from file or stdin
|
||||
err := func() error {
|
||||
if fpath == "stdin" {
|
||||
err := yaml.NewDecoder(stdin).Decode(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
} else {
|
||||
// rtsp-simple-server.yml is optional
|
||||
if fpath == "rtsp-simple-server.yml" {
|
||||
if _, err := os.Stat(fpath); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
f, err := os.Open(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = yaml.NewDecoder(f).Decode(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// rtsp-simple-server.yml is optional
|
||||
if fpath == "rtsp-simple-server.yml" {
|
||||
if _, err := os.Stat(fpath); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
f, err := os.Open(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = yaml.NewDecoder(f).Decode(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// read from environment
|
||||
err = confenv.Process("RTSP", conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(conf.Protocols) == 0 {
|
||||
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 (
|
||||
"bytes"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aler9/gortsplib"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -97,6 +99,70 @@ func (c *container) ip() string {
|
|||
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) {
|
||||
for _, conf := range []struct {
|
||||
publishSoft string
|
||||
|
|
Loading…
Reference in New Issue