Add stream redirect handler

This commit is contained in:
Alexey Khit
2023-09-01 10:18:50 +03:00
parent 69da64a49c
commit 7d65c60711
4 changed files with 70 additions and 38 deletions

View File

@@ -2,28 +2,28 @@ package echo
import ( import (
"bytes" "bytes"
"os/exec"
"github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/app"
"github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/internal/streams"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/shell" "github.com/AlexxIT/go2rtc/pkg/shell"
"os/exec"
) )
func Init() { func Init() {
log := app.GetLogger("echo") log := app.GetLogger("echo")
streams.HandleFunc("echo", func(url string) (core.Producer, error) { streams.RedirectFunc("echo", func(url string) (string, error) {
args := shell.QuoteSplit(url[5:]) args := shell.QuoteSplit(url[5:])
b, err := exec.Command(args[0], args[1:]...).Output() b, err := exec.Command(args[0], args[1:]...).Output()
if err != nil { if err != nil {
return nil, err return "", err
} }
b = bytes.TrimSpace(b) b = bytes.TrimSpace(b)
log.Debug().Str("url", url).Msgf("[echo] %s", b) log.Debug().Str("url", url).Msgf("[echo] %s", b)
return streams.GetProducer(string(b)) return string(b), nil
}) })
} }

View File

@@ -1,7 +1,6 @@
package ffmpeg package ffmpeg
import ( import (
"errors"
"net/url" "net/url"
"strings" "strings"
@@ -10,7 +9,6 @@ import (
"github.com/AlexxIT/go2rtc/internal/ffmpeg/hardware" "github.com/AlexxIT/go2rtc/internal/ffmpeg/hardware"
"github.com/AlexxIT/go2rtc/internal/rtsp" "github.com/AlexxIT/go2rtc/internal/rtsp"
"github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/internal/streams"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/ffmpeg" "github.com/AlexxIT/go2rtc/pkg/ffmpeg"
) )
@@ -27,12 +25,9 @@ func Init() {
defaults["global"] += " -v error" defaults["global"] += " -v error"
} }
streams.HandleFunc("ffmpeg", func(url string) (core.Producer, error) { streams.RedirectFunc("ffmpeg", func(url string) (string, error) {
args := parseArgs(url[7:]) // remove `ffmpeg:` args := parseArgs(url[7:])
if args == nil { return "exec:" + args.String(), nil
return nil, errors.New("can't generate ffmpeg command")
}
return streams.GetProducer("exec:" + args.String())
}) })
device.Init(defaults["bin"]) device.Init(defaults["bin"])

View File

@@ -37,12 +37,15 @@ func Init() {
api.HandleFunc("/streams", apiOK) api.HandleFunc("/streams", apiOK)
api.HandleFunc("/stream/", apiStream) api.HandleFunc("/stream/", apiStream)
streams.HandleFunc("hass", func(url string) (core.Producer, error) { streams.RedirectFunc("hass", func(url string) (string, error) {
// check entity by name if location := entities[url[5:]]; location != "" {
if url2 := entities[url[5:]]; url2 != "" { return location, nil
return streams.GetProducer(url2)
} }
return "", nil
})
streams.HandleFunc("hass", func(url string) (core.Producer, error) {
// support hass://supervisor?entity_id=camera.driveway_doorbell // support hass://supervisor?entity_id=camera.driveway_doorbell
client, err := hass.NewClient(url) client, err := hass.NewClient(url)
if err != nil { if err != nil {

View File

@@ -1,41 +1,75 @@
package streams package streams
import ( import (
"fmt" "errors"
"github.com/AlexxIT/go2rtc/pkg/core"
"strings" "strings"
"sync"
"github.com/AlexxIT/go2rtc/pkg/core"
) )
type Handler func(url string) (core.Producer, error) type Handler func(url string) (core.Producer, error)
var handlers = map[string]Handler{} var handlers = map[string]Handler{}
var handlersMu sync.Mutex
func HandleFunc(scheme string, handler Handler) { func HandleFunc(scheme string, handler Handler) {
handlersMu.Lock()
handlers[scheme] = handler handlers[scheme] = handler
handlersMu.Unlock()
}
func getHandler(url string) Handler {
i := strings.IndexByte(url, ':')
if i <= 0 { // TODO: i < 4 ?
return nil
}
handlersMu.Lock()
defer handlersMu.Unlock()
return handlers[url[:i]]
} }
func HasProducer(url string) bool { func HasProducer(url string) bool {
return getHandler(url) != nil if i := strings.IndexByte(url, ':'); i > 0 {
scheme := url[:i]
if _, ok := handlers[scheme]; ok {
return true
}
if _, ok := redirects[scheme]; ok {
return true
}
}
return false
} }
func GetProducer(url string) (core.Producer, error) { func GetProducer(url string) (core.Producer, error) {
handler := getHandler(url) if i := strings.IndexByte(url, ':'); i > 0 {
if handler == nil { scheme := url[:i]
return nil, fmt.Errorf("unsupported scheme: %s", url)
if redirect, ok := redirects[scheme]; ok {
location, err := redirect(url)
if err != nil {
return nil, err
}
if location != "" {
return GetProducer(location)
}
}
if handler, ok := handlers[scheme]; ok {
return handler(url)
}
} }
return handler(url)
return nil, errors.New("streams: unsupported scheme: " + url)
}
// Redirect can return: location URL or error or empty URL and error
type Redirect func(url string) (string, error)
var redirects = map[string]Redirect{}
func RedirectFunc(scheme string, redirect Redirect) {
redirects[scheme] = redirect
}
func Location(url string) (string, error) {
if i := strings.IndexByte(url, ':'); i > 0 {
scheme := url[:i]
if redirect, ok := redirects[scheme]; ok {
return redirect(url)
}
}
return "", nil
} }