diff --git a/internal/alsa/alsa_linux.go b/internal/alsa/alsa_linux.go index 316a7594e..09e2408ff 100644 --- a/internal/alsa/alsa_linux.go +++ b/internal/alsa/alsa_linux.go @@ -9,7 +9,7 @@ import ( "strconv" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/alsa" "github.com/AlexxIT/go2rtc/pkg/alsa/device" diff --git a/internal/api/api.go b/internal/api/api.go index dfb651177..40945e7ce 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -1,238 +1,31 @@ package api import ( - "crypto/tls" - "encoding/json" - "fmt" - "net" "net/http" "os" - "slices" "strconv" - "strings" "sync" "syscall" - "time" + "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" + "github.com/AlexxIT/go2rtc/internal/streams" "github.com/rs/zerolog" ) func Init() { - var cfg struct { - Mod struct { - Listen string `yaml:"listen"` - Username string `yaml:"username"` - Password string `yaml:"password"` - LocalAuth bool `yaml:"local_auth"` - BasePath string `yaml:"base_path"` - StaticDir string `yaml:"static_dir"` - Origin string `yaml:"origin"` - TLSListen string `yaml:"tls_listen"` - TLSCert string `yaml:"tls_cert"` - TLSKey string `yaml:"tls_key"` - UnixListen string `yaml:"unix_listen"` - - AllowPaths []string `yaml:"allow_paths"` - } `yaml:"api"` - } - - // default config - cfg.Mod.Listen = ":1984" - - // load config from YAML - app.LoadConfig(&cfg) - - if cfg.Mod.Listen == "" && cfg.Mod.UnixListen == "" && cfg.Mod.TLSListen == "" { - return - } - - allowPaths = cfg.Mod.AllowPaths - basePath = cfg.Mod.BasePath + server.Init() log = app.GetLogger("api") - initStatic(cfg.Mod.StaticDir) - - HandleFunc("api", apiHandler) - HandleFunc("api/config", configHandler) - HandleFunc("api/exit", exitHandler) - HandleFunc("api/restart", restartHandler) - HandleFunc("api/log", logHandler) - - Handler = http.DefaultServeMux // 4th - - if cfg.Mod.Origin == "*" { - Handler = middlewareCORS(Handler) // 3rd - } - - if cfg.Mod.Username != "" { - Handler = middlewareAuth(cfg.Mod.Username, cfg.Mod.Password, cfg.Mod.LocalAuth, Handler) // 2nd - } - - if log.Trace().Enabled() { - Handler = middlewareLog(Handler) // 1st - } - - if cfg.Mod.Listen != "" { - _, port, _ := net.SplitHostPort(cfg.Mod.Listen) - Port, _ = strconv.Atoi(port) - go listen("tcp", cfg.Mod.Listen) - } - - if cfg.Mod.UnixListen != "" { - _ = syscall.Unlink(cfg.Mod.UnixListen) - go listen("unix", cfg.Mod.UnixListen) - } - - // Initialize the HTTPS server - if cfg.Mod.TLSListen != "" && cfg.Mod.TLSCert != "" && cfg.Mod.TLSKey != "" { - go tlsListen("tcp", cfg.Mod.TLSListen, cfg.Mod.TLSCert, cfg.Mod.TLSKey) - } + server.HandleFunc("api", apiHandler) + server.HandleFunc("api/config", configHandler) + server.HandleFunc("api/exit", exitHandler) + server.HandleFunc("api/restart", restartHandler) + server.HandleFunc("api/log", logHandler) } -func listen(network, address string) { - ln, err := net.Listen(network, address) - if err != nil { - log.Error().Err(err).Msg("[api] listen") - return - } - - log.Info().Str("addr", address).Msg("[api] listen") - - server := http.Server{ - Handler: Handler, - ReadHeaderTimeout: 5 * time.Second, // Example: Set to 5 seconds - } - if err = server.Serve(ln); err != nil { - log.Fatal().Err(err).Msg("[api] serve") - } -} - -func tlsListen(network, address, certFile, keyFile string) { - var cert tls.Certificate - var err error - if strings.IndexByte(certFile, '\n') < 0 && strings.IndexByte(keyFile, '\n') < 0 { - // check if file path - cert, err = tls.LoadX509KeyPair(certFile, keyFile) - } else { - // if text file content - cert, err = tls.X509KeyPair([]byte(certFile), []byte(keyFile)) - } - if err != nil { - log.Error().Err(err).Caller().Send() - return - } - - ln, err := net.Listen(network, address) - if err != nil { - log.Error().Err(err).Msg("[api] tls listen") - return - } - - log.Info().Str("addr", address).Msg("[api] tls listen") - - server := &http.Server{ - Handler: Handler, - TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}}, - ReadHeaderTimeout: 5 * time.Second, - } - if err = server.ServeTLS(ln, "", ""); err != nil { - log.Fatal().Err(err).Msg("[api] tls serve") - } -} - -var Port int - -const ( - MimeJSON = "application/json" - MimeText = "text/plain" -) - -var Handler http.Handler - -// HandleFunc handle pattern with relative path: -// - "api/streams" => "{basepath}/api/streams" -// - "/streams" => "/streams" -func HandleFunc(pattern string, handler http.HandlerFunc) { - if len(pattern) == 0 || pattern[0] != '/' { - pattern = basePath + "/" + pattern - } - if allowPaths != nil && !slices.Contains(allowPaths, pattern) { - log.Trace().Str("path", pattern).Msg("[api] ignore path not in allow_paths") - return - } - log.Trace().Str("path", pattern).Msg("[api] register path") - http.HandleFunc(pattern, handler) -} - -// ResponseJSON important always add Content-Type -// so go won't need to call http.DetectContentType -func ResponseJSON(w http.ResponseWriter, v any) { - w.Header().Set("Content-Type", MimeJSON) - _ = json.NewEncoder(w).Encode(v) -} - -func ResponsePrettyJSON(w http.ResponseWriter, v any) { - w.Header().Set("Content-Type", MimeJSON) - enc := json.NewEncoder(w) - enc.SetIndent("", " ") - _ = enc.Encode(v) -} - -func Response(w http.ResponseWriter, body any, contentType string) { - w.Header().Set("Content-Type", contentType) - - switch v := body.(type) { - case []byte: - _, _ = w.Write(v) - case string: - _, _ = w.Write([]byte(v)) - default: - _, _ = fmt.Fprint(w, body) - } -} - -const StreamNotFound = "stream not found" - -var allowPaths []string -var basePath string var log zerolog.Logger -func middlewareLog(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - log.Trace().Msgf("[api] %s %s %s", r.Method, r.URL, r.RemoteAddr) - next.ServeHTTP(w, r) - }) -} - -func isLoopback(remoteAddr string) bool { - return strings.HasPrefix(remoteAddr, "127.") || strings.HasPrefix(remoteAddr, "[::1]") || remoteAddr == "@" -} - -func middlewareAuth(username, password string, localAuth bool, next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if localAuth || !isLoopback(r.RemoteAddr) { - user, pass, ok := r.BasicAuth() - if !ok || user != username || pass != password { - w.Header().Set("Www-Authenticate", `Basic realm="go2rtc"`) - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - } - - next.ServeHTTP(w, r) - }) -} - -func middlewareCORS(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") - w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type") - next.ServeHTTP(w, r) - }) -} - var mu sync.Mutex func apiHandler(w http.ResponseWriter, r *http.Request) { @@ -240,7 +33,7 @@ func apiHandler(w http.ResponseWriter, r *http.Request) { app.Info["host"] = r.Host mu.Unlock() - ResponseJSON(w, app.Info) + server.ResponseJSON(w, app.Info) } func exitHandler(w http.ResponseWriter, r *http.Request) { @@ -258,6 +51,9 @@ func exitHandler(w http.ResponseWriter, r *http.Request) { return } + // Stop all streams before exiting. + streams.StopAll() + os.Exit(code) } @@ -286,36 +82,8 @@ func logHandler(w http.ResponseWriter, r *http.Request) { _, _ = app.MemoryLog.WriteTo(w) case "DELETE": app.MemoryLog.Reset() - Response(w, "OK", "text/plain") + server.Response(w, "OK", "text/plain") default: http.Error(w, "Method not allowed", http.StatusBadRequest) } } - -type Source struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Info string `json:"info,omitempty"` - URL string `json:"url,omitempty"` - Location string `json:"location,omitempty"` -} - -func ResponseSources(w http.ResponseWriter, sources []*Source) { - if len(sources) == 0 { - http.Error(w, "no sources", http.StatusNotFound) - return - } - - var response = struct { - Sources []*Source `json:"sources"` - }{ - Sources: sources, - } - ResponseJSON(w, response) -} - -func Error(w http.ResponseWriter, err error) { - log.Error().Err(err).Caller(1).Send() - - http.Error(w, err.Error(), http.StatusInsufficientStorage) -} diff --git a/internal/api/config.go b/internal/api/config.go index 9072e8d35..dc9e7e45e 100644 --- a/internal/api/config.go +++ b/internal/api/config.go @@ -5,6 +5,7 @@ import ( "net/http" "os" + "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "gopkg.in/yaml.v3" ) @@ -23,7 +24,7 @@ func configHandler(w http.ResponseWriter, r *http.Request) { return } // https://www.ietf.org/archive/id/draft-ietf-httpapi-yaml-mediatypes-00.html - Response(w, data, "application/yaml") + server.Response(w, data, "application/yaml") case "POST", "PATCH": data, err := io.ReadAll(r.Body) diff --git a/internal/api/server/handle.go b/internal/api/server/handle.go new file mode 100644 index 000000000..b13d80bd9 --- /dev/null +++ b/internal/api/server/handle.go @@ -0,0 +1,21 @@ +package server + +import ( + "net/http" + "slices" +) + +// HandleFunc handle pattern with relative path: +// - "api/streams" => "{basepath}/api/streams" +// - "/streams" => "/streams" +func HandleFunc(pattern string, handler http.HandlerFunc) { + if len(pattern) == 0 || pattern[0] != '/' { + pattern = basePath + "/" + pattern + } + if allowPaths != nil && !slices.Contains(allowPaths, pattern) { + log.Trace().Str("path", pattern).Msg("[api] ignore path not in allow_paths") + return + } + log.Trace().Str("path", pattern).Msg("[api] register path") + http.HandleFunc(pattern, handler) +} diff --git a/internal/api/server/response.go b/internal/api/server/response.go new file mode 100644 index 000000000..66e7a4903 --- /dev/null +++ b/internal/api/server/response.go @@ -0,0 +1,54 @@ +package server + +import ( + "encoding/json" + "fmt" + "net/http" +) + +// ResponseJSON important always add Content-Type +// so go won't need to call http.DetectContentType +func ResponseJSON(w http.ResponseWriter, v any) { + w.Header().Set("Content-Type", MimeJSON) + _ = json.NewEncoder(w).Encode(v) +} + +func ResponsePrettyJSON(w http.ResponseWriter, v any) { + w.Header().Set("Content-Type", MimeJSON) + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + _ = enc.Encode(v) +} + +func Response(w http.ResponseWriter, body any, contentType string) { + w.Header().Set("Content-Type", contentType) + + switch v := body.(type) { + case []byte: + _, _ = w.Write(v) + case string: + _, _ = w.Write([]byte(v)) + default: + _, _ = fmt.Fprint(w, body) + } +} + +func ResponseSources(w http.ResponseWriter, sources []*Source) { + if len(sources) == 0 { + http.Error(w, "no sources", http.StatusNotFound) + return + } + + var response = struct { + Sources []*Source `json:"sources"` + }{ + Sources: sources, + } + ResponseJSON(w, response) +} + +func Error(w http.ResponseWriter, err error) { + log.Error().Err(err).Caller(1).Send() + + http.Error(w, err.Error(), http.StatusInsufficientStorage) +} diff --git a/internal/api/server/server.go b/internal/api/server/server.go new file mode 100644 index 000000000..c6de9a541 --- /dev/null +++ b/internal/api/server/server.go @@ -0,0 +1,179 @@ +package server + +import ( + "crypto/tls" + "net" + "net/http" + "strconv" + "strings" + "syscall" + "time" + + "github.com/AlexxIT/go2rtc/internal/app" + "github.com/rs/zerolog" +) + +func Init() { + var cfg struct { + Mod struct { + Listen string `yaml:"listen"` + Username string `yaml:"username"` + Password string `yaml:"password"` + LocalAuth bool `yaml:"local_auth"` + BasePath string `yaml:"base_path"` + StaticDir string `yaml:"static_dir"` + Origin string `yaml:"origin"` + TLSListen string `yaml:"tls_listen"` + TLSCert string `yaml:"tls_cert"` + TLSKey string `yaml:"tls_key"` + UnixListen string `yaml:"unix_listen"` + + AllowPaths []string `yaml:"allow_paths"` + } `yaml:"api"` + } + + // default config + cfg.Mod.Listen = ":1984" + + // load config from YAML + app.LoadConfig(&cfg) + + if cfg.Mod.Listen == "" && cfg.Mod.UnixListen == "" && cfg.Mod.TLSListen == "" { + return + } + + allowPaths = cfg.Mod.AllowPaths + basePath = cfg.Mod.BasePath + log = app.GetLogger("api") + + initStatic(cfg.Mod.StaticDir) + + Handler = http.DefaultServeMux // 4th + + if cfg.Mod.Origin == "*" { + Handler = middlewareCORS(Handler) // 3rd + } + + if cfg.Mod.Username != "" { + Handler = middlewareAuth(cfg.Mod.Username, cfg.Mod.Password, cfg.Mod.LocalAuth, Handler) // 2nd + } + + if log.Trace().Enabled() { + Handler = middlewareLog(Handler) // 1st + } + + if cfg.Mod.Listen != "" { + _, port, _ := net.SplitHostPort(cfg.Mod.Listen) + Port, _ = strconv.Atoi(port) + go listen("tcp", cfg.Mod.Listen) + } + + if cfg.Mod.UnixListen != "" { + _ = syscall.Unlink(cfg.Mod.UnixListen) + go listen("unix", cfg.Mod.UnixListen) + } + + // Initialize the HTTPS server + if cfg.Mod.TLSListen != "" && cfg.Mod.TLSCert != "" && cfg.Mod.TLSKey != "" { + go tlsListen("tcp", cfg.Mod.TLSListen, cfg.Mod.TLSCert, cfg.Mod.TLSKey) + } +} + +var Port int + +var Handler http.Handler + +const ( + MimeJSON = "application/json" + MimeText = "text/plain" +) + +var allowPaths []string +var basePath string +var log zerolog.Logger + +func listen(network, address string) { + ln, err := net.Listen(network, address) + if err != nil { + log.Error().Err(err).Msg("[api] listen") + return + } + + log.Info().Str("addr", address).Msg("[api] listen") + + server := http.Server{ + Handler: Handler, + ReadHeaderTimeout: 5 * time.Second, // Example: Set to 5 seconds + } + if err = server.Serve(ln); err != nil { + log.Fatal().Err(err).Msg("[api] serve") + } +} + +func tlsListen(network, address, certFile, keyFile string) { + var cert tls.Certificate + var err error + if strings.IndexByte(certFile, '\n') < 0 && strings.IndexByte(keyFile, '\n') < 0 { + // check if file path + cert, err = tls.LoadX509KeyPair(certFile, keyFile) + } else { + // if text file content + cert, err = tls.X509KeyPair([]byte(certFile), []byte(keyFile)) + } + if err != nil { + log.Error().Err(err).Caller().Send() + return + } + + ln, err := net.Listen(network, address) + if err != nil { + log.Error().Err(err).Msg("[api] tls listen") + return + } + + log.Info().Str("addr", address).Msg("[api] tls listen") + + server := &http.Server{ + Handler: Handler, + TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}}, + ReadHeaderTimeout: 5 * time.Second, + } + if err = server.ServeTLS(ln, "", ""); err != nil { + log.Fatal().Err(err).Msg("[api] tls serve") + } +} + +func middlewareLog(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Trace().Msgf("[api] %s %s %s", r.Method, r.URL, r.RemoteAddr) + next.ServeHTTP(w, r) + }) +} + +func isLoopback(remoteAddr string) bool { + return strings.HasPrefix(remoteAddr, "127.") || strings.HasPrefix(remoteAddr, "[::1]") || remoteAddr == "@" +} + +func middlewareAuth(username, password string, localAuth bool, next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if localAuth || !isLoopback(r.RemoteAddr) { + user, pass, ok := r.BasicAuth() + if !ok || user != username || pass != password { + w.Header().Set("Www-Authenticate", `Basic realm="go2rtc"`) + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + } + + next.ServeHTTP(w, r) + }) +} + +func middlewareCORS(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type") + next.ServeHTTP(w, r) + }) +} diff --git a/internal/api/server/source.go b/internal/api/server/source.go new file mode 100644 index 000000000..e28f8f98d --- /dev/null +++ b/internal/api/server/source.go @@ -0,0 +1,9 @@ +package server + +type Source struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Info string `json:"info,omitempty"` + URL string `json:"url,omitempty"` + Location string `json:"location,omitempty"` +} diff --git a/internal/api/static.go b/internal/api/server/static.go similarity index 97% rename from internal/api/static.go rename to internal/api/server/static.go index 8de400735..22e72106d 100644 --- a/internal/api/static.go +++ b/internal/api/server/static.go @@ -1,4 +1,4 @@ -package api +package server import ( "net/http" diff --git a/internal/api/ws/ws.go b/internal/api/ws/ws.go index 02c2f90cc..83a67625b 100644 --- a/internal/api/ws/ws.go +++ b/internal/api/ws/ws.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/pkg/creds" "github.com/gorilla/websocket" diff --git a/internal/debug/debug.go b/internal/debug/debug.go index fc7d2453f..0a83851ef 100644 --- a/internal/debug/debug.go +++ b/internal/debug/debug.go @@ -1,7 +1,7 @@ package debug import ( - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" ) func Init() { diff --git a/internal/debug/stack.go b/internal/debug/stack.go index 6bc735ad8..fb95ef708 100644 --- a/internal/debug/stack.go +++ b/internal/debug/stack.go @@ -6,7 +6,7 @@ import ( "net/http" "runtime" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" ) var stackSkip = [][]byte{ diff --git a/internal/dvrip/dvrip.go b/internal/dvrip/dvrip.go index db1c60dbc..546b07671 100644 --- a/internal/dvrip/dvrip.go +++ b/internal/dvrip/dvrip.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/dvrip" ) diff --git a/internal/exec/README.md b/internal/exec/README.md index aba1c5553..6b2b5ed82 100644 --- a/internal/exec/README.md +++ b/internal/exec/README.md @@ -20,6 +20,7 @@ Pipe commands support parameters (format: `exec:{command}#{param1}#{param2}`): - `killsignal` - signal which will be sent to stop the process (numeric form) - `killtimeout` - time in seconds for forced termination with sigkill +- `quitstdin` - string to pass on stdin for graceful termination - `backchannel` - enable backchannel for two-way audio - `starttimeout` - time in seconds for waiting first byte from RTSP diff --git a/internal/exec/exec.go b/internal/exec/exec.go index e428aefbe..56e1bbabe 100644 --- a/internal/exec/exec.go +++ b/internal/exec/exec.go @@ -2,6 +2,7 @@ package exec import ( "bufio" + "bytes" "crypto/md5" "encoding/hex" "errors" @@ -104,6 +105,12 @@ func execHandle(rawURL string) (prod core.Producer, err error) { cmd.WaitDelay = time.Duration(core.Atoi(s)) * time.Second } + if s := query.Get("quitstdin"); s != "" { + buffer := bytes.Buffer{} + buffer.Write([]byte(s + "\n")) + cmd.Stdin = &buffer + } + if query.Get("backchannel") == "1" { return pcm.NewBackchannel(cmd, query.Get("audio")) } diff --git a/internal/ffmpeg/device/device_bsd.go b/internal/ffmpeg/device/device_bsd.go index 27d5b6154..4d3828c21 100644 --- a/internal/ffmpeg/device/device_bsd.go +++ b/internal/ffmpeg/device/device_bsd.go @@ -9,7 +9,7 @@ import ( "regexp" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/pkg/core" ) diff --git a/internal/ffmpeg/device/device_darwin.go b/internal/ffmpeg/device/device_darwin.go index ba97c0aa9..fa05fd1f9 100644 --- a/internal/ffmpeg/device/device_darwin.go +++ b/internal/ffmpeg/device/device_darwin.go @@ -8,7 +8,7 @@ import ( "regexp" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/pkg/core" ) diff --git a/internal/ffmpeg/device/device_unix.go b/internal/ffmpeg/device/device_unix.go index 7b62187f3..f7192200d 100644 --- a/internal/ffmpeg/device/device_unix.go +++ b/internal/ffmpeg/device/device_unix.go @@ -9,7 +9,7 @@ import ( "regexp" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/pkg/core" ) diff --git a/internal/ffmpeg/device/device_windows.go b/internal/ffmpeg/device/device_windows.go index ff3283117..92548548f 100644 --- a/internal/ffmpeg/device/device_windows.go +++ b/internal/ffmpeg/device/device_windows.go @@ -7,7 +7,7 @@ import ( "os/exec" "regexp" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/pkg/core" ) diff --git a/internal/ffmpeg/device/devices.go b/internal/ffmpeg/device/devices.go index 69b134446..3f799314b 100644 --- a/internal/ffmpeg/device/devices.go +++ b/internal/ffmpeg/device/devices.go @@ -6,7 +6,7 @@ import ( "strconv" "sync" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" ) func Init(bin string) { diff --git a/internal/ffmpeg/ffmpeg.go b/internal/ffmpeg/ffmpeg.go index a3d589b16..a1f2bf15f 100644 --- a/internal/ffmpeg/ffmpeg.go +++ b/internal/ffmpeg/ffmpeg.go @@ -4,7 +4,7 @@ import ( "net/url" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/ffmpeg/device" "github.com/AlexxIT/go2rtc/internal/ffmpeg/hardware" diff --git a/internal/ffmpeg/hardware/hardware.go b/internal/ffmpeg/hardware/hardware.go index 801668905..89b6ea7a4 100644 --- a/internal/ffmpeg/hardware/hardware.go +++ b/internal/ffmpeg/hardware/hardware.go @@ -5,7 +5,7 @@ import ( "os/exec" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/pkg/ffmpeg" ) diff --git a/internal/ffmpeg/hardware/hardware_bsd.go b/internal/ffmpeg/hardware/hardware_bsd.go index de24ac5c8..6e9d5a45e 100644 --- a/internal/ffmpeg/hardware/hardware_bsd.go +++ b/internal/ffmpeg/hardware/hardware_bsd.go @@ -5,7 +5,7 @@ package hardware import ( "runtime" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" ) const ( diff --git a/internal/ffmpeg/hardware/hardware_darwin.go b/internal/ffmpeg/hardware/hardware_darwin.go index b15055128..dae27fad8 100644 --- a/internal/ffmpeg/hardware/hardware_darwin.go +++ b/internal/ffmpeg/hardware/hardware_darwin.go @@ -3,7 +3,7 @@ package hardware import ( - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" ) const ProbeVideoToolboxH264 = "-f lavfi -i testsrc2=size=svga -t 1 -c h264_videotoolbox -f null -" diff --git a/internal/ffmpeg/hardware/hardware_unix.go b/internal/ffmpeg/hardware/hardware_unix.go index e8000e17d..7635ebf72 100644 --- a/internal/ffmpeg/hardware/hardware_unix.go +++ b/internal/ffmpeg/hardware/hardware_unix.go @@ -5,7 +5,7 @@ package hardware import ( "runtime" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" ) const ( diff --git a/internal/ffmpeg/hardware/hardware_windows.go b/internal/ffmpeg/hardware/hardware_windows.go index cdf0e12cc..b56b5300d 100644 --- a/internal/ffmpeg/hardware/hardware_windows.go +++ b/internal/ffmpeg/hardware/hardware_windows.go @@ -2,7 +2,7 @@ package hardware -import "github.com/AlexxIT/go2rtc/internal/api" +import api "github.com/AlexxIT/go2rtc/internal/api/server" const ProbeDXVA2H264 = "-init_hw_device dxva2 -f lavfi -i testsrc2 -t 1 -c h264_qsv -f null -" const ProbeDXVA2H265 = "-init_hw_device dxva2 -f lavfi -i testsrc2 -t 1 -c hevc_qsv -f null -" diff --git a/internal/ffmpeg/producer.go b/internal/ffmpeg/producer.go index fb0444672..d2e4f43aa 100644 --- a/internal/ffmpeg/producer.go +++ b/internal/ffmpeg/producer.go @@ -31,6 +31,10 @@ func NewProducer(url string) (core.Producer, error) { return nil, errors.New("ffmpeg: unsupported params: " + url[i:]) } + // Append quitstdin param to ensure ffmpeg process is terminated when the producer is stopped. + // This is necessary as it lets ffmpeg exit gracefully. + p.query.Add("quitstdin", "q") + p.ID = core.NewID() p.FormatName = "ffmpeg" p.Medias = []*core.Media{ diff --git a/internal/gopro/gopro.go b/internal/gopro/gopro.go index ee5780494..a84c0fc03 100644 --- a/internal/gopro/gopro.go +++ b/internal/gopro/gopro.go @@ -3,7 +3,7 @@ package gopro import ( "net/http" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/gopro" diff --git a/internal/hass/api.go b/internal/hass/api.go index 9f110fc8b..21f83d32c 100644 --- a/internal/hass/api.go +++ b/internal/hass/api.go @@ -7,7 +7,7 @@ import ( "net/http" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/internal/webrtc" ) @@ -47,7 +47,7 @@ func apiStream(w http.ResponseWriter, r *http.Request) { name := r.RequestURI[8 : 8+i] stream := streams.Get(name) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } diff --git a/internal/hass/hass.go b/internal/hass/hass.go index 99c636926..73471f038 100644 --- a/internal/hass/hass.go +++ b/internal/hass/hass.go @@ -10,7 +10,7 @@ import ( "strings" "sync" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/roborock" "github.com/AlexxIT/go2rtc/internal/streams" diff --git a/internal/hls/hls.go b/internal/hls/hls.go index 5c136450d..381bb1ea9 100644 --- a/internal/hls/hls.go +++ b/internal/hls/hls.go @@ -5,7 +5,7 @@ import ( "sync" "time" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/api/ws" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/streams" @@ -52,7 +52,7 @@ func handlerStream(w http.ResponseWriter, r *http.Request) { src := r.URL.Query().Get("src") stream := streams.Get(src) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } diff --git a/internal/hls/ws.go b/internal/hls/ws.go index 00eedfe2a..5b97de8c1 100644 --- a/internal/hls/ws.go +++ b/internal/hls/ws.go @@ -4,7 +4,6 @@ import ( "errors" "time" - "github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/api/ws" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/mp4" @@ -13,7 +12,7 @@ import ( func handlerWSHLS(tr *ws.Transport, msg *ws.Message) error { stream, _ := streams.GetOrPatch(tr.Request.URL.Query()) if stream == nil { - return errors.New(api.StreamNotFound) + return errors.New(streams.StreamNotFound) } codecs := msg.String() diff --git a/internal/homekit/api.go b/internal/homekit/api.go index 885a40fa0..cc0824fcd 100644 --- a/internal/homekit/api.go +++ b/internal/homekit/api.go @@ -8,7 +8,7 @@ import ( "net/url" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/hap" @@ -151,7 +151,7 @@ func apiPair(id, url string) error { func apiUnpair(id string) error { stream := streams.Get(id) if stream == nil { - return errors.New(api.StreamNotFound) + return errors.New(streams.StreamNotFound) } rawURL := findHomeKitURL(stream.Sources()) diff --git a/internal/homekit/homekit.go b/internal/homekit/homekit.go index 59b84b3ba..2025eb230 100644 --- a/internal/homekit/homekit.go +++ b/internal/homekit/homekit.go @@ -5,7 +5,7 @@ import ( "net/http" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/srtp" "github.com/AlexxIT/go2rtc/internal/streams" diff --git a/internal/http/http.go b/internal/http/http.go index 4b0560c1a..35259073e 100644 --- a/internal/http/http.go +++ b/internal/http/http.go @@ -7,7 +7,7 @@ import ( "net/url" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/hls" @@ -114,7 +114,7 @@ func apiStream(w http.ResponseWriter, r *http.Request) { dst := r.URL.Query().Get("dst") stream := streams.Get(dst) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } diff --git a/internal/mjpeg/mjpeg.go b/internal/mjpeg/mjpeg.go index e9f973aa4..b7d005779 100644 --- a/internal/mjpeg/mjpeg.go +++ b/internal/mjpeg/mjpeg.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/api/ws" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/ffmpeg" @@ -40,7 +40,7 @@ func handlerKeyframe(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() stream, _ := streams.GetOrPatch(query) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } @@ -139,7 +139,7 @@ func outputMjpeg(w http.ResponseWriter, r *http.Request) { src := r.URL.Query().Get("src") stream := streams.Get(src) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } @@ -174,7 +174,7 @@ func inputMjpeg(w http.ResponseWriter, r *http.Request) { dst := r.URL.Query().Get("dst") stream := streams.Get(dst) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } @@ -193,7 +193,7 @@ func inputMjpeg(w http.ResponseWriter, r *http.Request) { func handlerWS(tr *ws.Transport, _ *ws.Message) error { stream, _ := streams.GetOrPatch(tr.Request.URL.Query()) if stream == nil { - return errors.New(api.StreamNotFound) + return errors.New(streams.StreamNotFound) } cons := mjpeg.NewConsumer() @@ -219,7 +219,7 @@ func apiStreamY4M(w http.ResponseWriter, r *http.Request) { src := r.URL.Query().Get("src") stream := streams.Get(src) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } diff --git a/internal/mp4/mp4.go b/internal/mp4/mp4.go index d0a6d9717..195cc16da 100644 --- a/internal/mp4/mp4.go +++ b/internal/mp4/mp4.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/api/ws" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/streams" @@ -43,7 +43,7 @@ func handlerKeyframe(w http.ResponseWriter, r *http.Request) { src := query.Get("src") stream := streams.Get(src) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } @@ -93,7 +93,7 @@ func handlerMP4(w http.ResponseWriter, r *http.Request) { stream, _ := streams.GetOrPatch(query) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } diff --git a/internal/mp4/ws.go b/internal/mp4/ws.go index c1afac244..f628fb68b 100644 --- a/internal/mp4/ws.go +++ b/internal/mp4/ws.go @@ -3,7 +3,6 @@ package mp4 import ( "errors" - "github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/api/ws" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" @@ -13,7 +12,7 @@ import ( func handlerWSMSE(tr *ws.Transport, msg *ws.Message) error { stream, _ := streams.GetOrPatch(tr.Request.URL.Query()) if stream == nil { - return errors.New(api.StreamNotFound) + return errors.New(streams.StreamNotFound) } var medias []*core.Media @@ -45,7 +44,7 @@ func handlerWSMSE(tr *ws.Transport, msg *ws.Message) error { func handlerWSMP4(tr *ws.Transport, msg *ws.Message) error { stream, _ := streams.GetOrPatch(tr.Request.URL.Query()) if stream == nil { - return errors.New(api.StreamNotFound) + return errors.New(streams.StreamNotFound) } var medias []*core.Media diff --git a/internal/mpeg/mpeg.go b/internal/mpeg/mpeg.go index 0a55299d7..c750149ba 100644 --- a/internal/mpeg/mpeg.go +++ b/internal/mpeg/mpeg.go @@ -3,7 +3,7 @@ package mpeg import ( "net/http" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/aac" "github.com/AlexxIT/go2rtc/pkg/mpegts" @@ -26,7 +26,7 @@ func outputMpegTS(w http.ResponseWriter, r *http.Request) { src := r.URL.Query().Get("src") stream := streams.Get(src) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } @@ -49,7 +49,7 @@ func inputMpegTS(w http.ResponseWriter, r *http.Request) { dst := r.URL.Query().Get("dst") stream := streams.Get(dst) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } @@ -72,7 +72,7 @@ func apiStreamAAC(w http.ResponseWriter, r *http.Request) { src := r.URL.Query().Get("src") stream := streams.Get(src) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } diff --git a/internal/nest/init.go b/internal/nest/init.go index 8289af73e..36ddad804 100644 --- a/internal/nest/init.go +++ b/internal/nest/init.go @@ -4,7 +4,7 @@ import ( "net/http" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/nest" diff --git a/internal/onvif/onvif.go b/internal/onvif/onvif.go index c305b706e..7caee0051 100644 --- a/internal/onvif/onvif.go +++ b/internal/onvif/onvif.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/rtsp" "github.com/AlexxIT/go2rtc/internal/streams" diff --git a/internal/ring/ring.go b/internal/ring/ring.go index 7fdb284f2..26ee78cfd 100644 --- a/internal/ring/ring.go +++ b/internal/ring/ring.go @@ -6,7 +6,7 @@ import ( "fmt" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/ring" diff --git a/internal/roborock/roborock.go b/internal/roborock/roborock.go index 32a436d86..71c0db4ef 100644 --- a/internal/roborock/roborock.go +++ b/internal/roborock/roborock.go @@ -4,7 +4,7 @@ import ( "fmt" "net/http" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/roborock" diff --git a/internal/rtmp/rtmp.go b/internal/rtmp/rtmp.go index b3d7f9324..30bcd60a1 100644 --- a/internal/rtmp/rtmp.go +++ b/internal/rtmp/rtmp.go @@ -6,7 +6,7 @@ import ( "net" "net/http" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" @@ -155,7 +155,7 @@ func outputFLV(w http.ResponseWriter, r *http.Request) { src := r.URL.Query().Get("src") stream := streams.Get(src) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } @@ -179,7 +179,7 @@ func inputFLV(w http.ResponseWriter, r *http.Request) { dst := r.URL.Query().Get("dst") stream := streams.Get(dst) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } diff --git a/internal/streams/add_consumer.go b/internal/streams/add_consumer.go index 7400ce6e2..a83c78c35 100644 --- a/internal/streams/add_consumer.go +++ b/internal/streams/add_consumer.go @@ -95,7 +95,7 @@ func (s *Stream) AddConsumer(cons core.Consumer) (err error) { // stop producers if they don't have readers if s.pending.Add(-1) == 0 { - s.stopProducers() + s.StopProducers() } if len(prodStarts) == 0 { diff --git a/internal/streams/api.go b/internal/streams/api.go index d6142eb02..4cc600d07 100644 --- a/internal/streams/api.go +++ b/internal/streams/api.go @@ -3,7 +3,7 @@ package streams import ( "net/http" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/creds" diff --git a/internal/streams/stream.go b/internal/streams/stream.go index 984c73edd..2f98cea63 100644 --- a/internal/streams/stream.go +++ b/internal/streams/stream.go @@ -8,6 +8,8 @@ import ( "github.com/AlexxIT/go2rtc/pkg/core" ) +const StreamNotFound = "stream not found" + type Stream struct { producers []*Producer consumers []core.Consumer @@ -73,7 +75,7 @@ func (s *Stream) RemoveConsumer(cons core.Consumer) { } s.mu.Unlock() - s.stopProducers() + s.StopProducers() } func (s *Stream) AddProducer(prod core.Producer) { @@ -94,7 +96,12 @@ func (s *Stream) RemoveProducer(prod core.Producer) { s.mu.Unlock() } -func (s *Stream) stopProducers() { +func (s *Stream) StopProducers(force ...bool) { + // Default force=false, only stop producers without active tracks + // If force=true, stop all producers regardless of active tracks + if len(force) == 0 { + force = append(force, false) + } if s.pending.Load() > 0 { log.Trace().Msg("[streams] skip stop pending producer") return @@ -103,14 +110,16 @@ func (s *Stream) stopProducers() { s.mu.Lock() producers: for _, producer := range s.producers { - for _, track := range producer.receivers { - if len(track.Senders()) > 0 { - continue producers + if !force[0] { + for _, track := range producer.receivers { + if len(track.Senders()) > 0 { + continue producers + } } - } - for _, track := range producer.senders { - if len(track.Senders()) > 0 { - continue producers + for _, track := range producer.senders { + if len(track.Senders()) > 0 { + continue producers + } } } producer.stop() diff --git a/internal/streams/streams.go b/internal/streams/streams.go index f3b8df03c..2a0df91b2 100644 --- a/internal/streams/streams.go +++ b/internal/streams/streams.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "github.com/rs/zerolog" ) @@ -174,3 +174,11 @@ func GetAllSources() map[string][]string { streamsMu.Unlock() return sources } + +func StopAll() { + streamsMu.Lock() + for _, stream := range streams { + stream.StopProducers(true) + } + streamsMu.Unlock() +} diff --git a/internal/tuya/tuya.go b/internal/tuya/tuya.go index 9dcf27212..38bc00b2d 100644 --- a/internal/tuya/tuya.go +++ b/internal/tuya/tuya.go @@ -9,7 +9,7 @@ import ( "net/url" "strconv" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/tuya" diff --git a/internal/v4l2/v4l2_linux.go b/internal/v4l2/v4l2_linux.go index 0bb05473e..522a88f5d 100644 --- a/internal/v4l2/v4l2_linux.go +++ b/internal/v4l2/v4l2_linux.go @@ -9,7 +9,7 @@ import ( "os" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/v4l2" diff --git a/internal/webrtc/server.go b/internal/webrtc/server.go index 48bd53802..1dc3e8f59 100644 --- a/internal/webrtc/server.go +++ b/internal/webrtc/server.go @@ -9,7 +9,6 @@ import ( "strings" "time" - "github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/webrtc" @@ -66,7 +65,7 @@ func outputWebRTC(w http.ResponseWriter, r *http.Request) { u := r.URL.Query().Get("src") stream := streams.Get(u) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } @@ -167,7 +166,7 @@ func inputWebRTC(w http.ResponseWriter, r *http.Request) { dst := r.URL.Query().Get("dst") stream := streams.Get(dst) if stream == nil { - http.Error(w, api.StreamNotFound, http.StatusNotFound) + http.Error(w, streams.StreamNotFound, http.StatusNotFound) return } diff --git a/internal/webrtc/webrtc.go b/internal/webrtc/webrtc.go index 2a5b4ad66..20dbc56d2 100644 --- a/internal/webrtc/webrtc.go +++ b/internal/webrtc/webrtc.go @@ -5,7 +5,7 @@ import ( "net" "strings" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/api/ws" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/streams" @@ -130,7 +130,7 @@ func asyncHandler(tr *ws.Transport, msg *ws.Message) (err error) { } if stream == nil { - return errors.New(api.StreamNotFound) + return errors.New(streams.StreamNotFound) } var offer struct { diff --git a/internal/webtorrent/init.go b/internal/webtorrent/init.go index b1c25c767..791a2d83b 100644 --- a/internal/webtorrent/init.go +++ b/internal/webtorrent/init.go @@ -6,7 +6,7 @@ import ( "net/http" "net/url" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/internal/webrtc" @@ -45,7 +45,7 @@ func Init() { Exchange: func(src, offer string) (answer string, err error) { stream := streams.Get(src) if stream == nil { - return "", errors.New(api.StreamNotFound) + return "", errors.New(streams.StreamNotFound) } return webrtc.ExchangeSDP(stream, offer, "webtorrent", "") }, diff --git a/internal/wyze/wyze.go b/internal/wyze/wyze.go index 982a16edf..6be4ee44f 100644 --- a/internal/wyze/wyze.go +++ b/internal/wyze/wyze.go @@ -6,7 +6,7 @@ import ( "net/http" "net/url" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" diff --git a/internal/xiaomi/xiaomi.go b/internal/xiaomi/xiaomi.go index 1e578420c..a307c6f52 100644 --- a/internal/xiaomi/xiaomi.go +++ b/internal/xiaomi/xiaomi.go @@ -10,7 +10,7 @@ import ( "strings" "sync" - "github.com/AlexxIT/go2rtc/internal/api" + api "github.com/AlexxIT/go2rtc/internal/api/server" "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core"