diff --git a/internal/onvif/init.go b/internal/onvif/init.go
index a4f5ad22..01b33702 100644
--- a/internal/onvif/init.go
+++ b/internal/onvif/init.go
@@ -11,6 +11,7 @@ import (
"io"
"net"
"net/http"
+ "net/url"
"os"
"strconv"
"time"
@@ -123,17 +124,32 @@ func apiOnvif(w http.ResponseWriter, r *http.Request) {
var items []api.Stream
if src == "" {
- hosts, err := onvif.DiscoveryStreamingHosts()
+ urls, err := onvif.DiscoveryStreamingURLs()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- for _, host := range hosts {
- items = append(items, api.Stream{
- Name: host,
- URL: "onvif://user:pass@" + host,
- })
+ for _, rawURL := range urls {
+ u, err := url.Parse(rawURL)
+ if err != nil {
+ log.Warn().Str("url", rawURL).Msg("[onvif] broken")
+ continue
+ }
+
+ if u.Scheme != "http" {
+ log.Warn().Str("url", rawURL).Msg("[onvif] unsupported")
+ continue
+ }
+
+ u.Scheme = "onvif"
+ u.User = url.UserPassword("user", "pass")
+
+ if u.Path == onvif.PathDevice {
+ u.Path = ""
+ }
+
+ items = append(items, api.Stream{Name: u.Host, URL: u.String()})
}
} else {
client, err := onvif.NewClient(src)
diff --git a/pkg/onvif/client.go b/pkg/onvif/client.go
index a4b6413f..d090baf3 100644
--- a/pkg/onvif/client.go
+++ b/pkg/onvif/client.go
@@ -17,6 +17,10 @@ import (
type Client struct {
url *url.URL
+
+ deviceURL string
+ mediaURL string
+ imaginURL string
}
func NewClient(rawURL string) (*Client, error) {
@@ -24,7 +28,25 @@ func NewClient(rawURL string) (*Client, error) {
if err != nil {
return nil, err
}
- return &Client{url: u}, nil
+
+ baseURL := "http://" + u.Host
+
+ client := &Client{url: u}
+ if u.Path == "" {
+ client.deviceURL = baseURL + PathDevice
+ } else {
+ client.deviceURL = baseURL + u.Path
+ }
+
+ b, err := client.GetCapabilities()
+ if err != nil {
+ return nil, err
+ }
+
+ client.mediaURL = FindTagValue(b, "Media.+?XAddr")
+ client.imaginURL = FindTagValue(b, "Imaging.+?XAddr")
+
+ return client, nil
}
func (c *Client) GetURI() (string, error) {
@@ -39,7 +61,7 @@ func (c *Client) GetURI() (string, error) {
return "", err
}
if i >= len(tokens) {
- return "", errors.New("wrong subtype")
+ return "", errors.New("onvif: wrong subtype")
}
token = tokens[i]
}
@@ -104,7 +126,7 @@ func (c *Client) HasSnapshots() bool {
func (c *Client) GetCapabilities() ([]byte, error) {
return c.Request(
- PathDevice,
+ c.deviceURL,
`
All
`,
@@ -113,25 +135,25 @@ func (c *Client) GetCapabilities() ([]byte, error) {
func (c *Client) GetNetworkInterfaces() ([]byte, error) {
return c.Request(
- PathDevice, ``,
+ c.deviceURL, ``,
)
}
func (c *Client) GetDeviceInformation() ([]byte, error) {
return c.Request(
- PathDevice, ``,
+ c.deviceURL, ``,
)
}
func (c *Client) GetProfiles() ([]byte, error) {
return c.Request(
- PathMedia, ``,
+ c.mediaURL, ``,
)
}
func (c *Client) GetStreamUri(token string) ([]byte, error) {
return c.Request(
- PathMedia,
+ c.mediaURL,
`
RTP-Unicast
@@ -144,8 +166,8 @@ func (c *Client) GetStreamUri(token string) ([]byte, error) {
func (c *Client) GetSnapshotUri(token string) ([]byte, error) {
return c.Request(
- PathMedia,
- `
+ c.imaginURL,
+ `
`+token+`
`,
)
@@ -153,26 +175,26 @@ func (c *Client) GetSnapshotUri(token string) ([]byte, error) {
func (c *Client) GetSystemDateAndTime() ([]byte, error) {
return c.Request(
- PathDevice, ``,
+ c.deviceURL, ``,
)
}
func (c *Client) GetServiceCapabilities() ([]byte, error) {
// some cameras answer GetServiceCapabilities for media only for path = "/onvif/media"
return c.Request(
- PathMedia, ``,
+ c.mediaURL, ``,
)
}
func (c *Client) SystemReboot() ([]byte, error) {
return c.Request(
- PathDevice, ``,
+ c.deviceURL, ``,
)
}
func (c *Client) GetServices() ([]byte, error) {
return c.Request(
- PathDevice, `
+ c.deviceURL, `
true
`,
)
@@ -180,11 +202,15 @@ func (c *Client) GetServices() ([]byte, error) {
func (c *Client) GetScopes() ([]byte, error) {
return c.Request(
- PathDevice, ``,
+ c.deviceURL, ``,
)
}
-func (c *Client) Request(path, body string) ([]byte, error) {
+func (c *Client) Request(url, body string) ([]byte, error) {
+ if url == "" {
+ return nil, errors.New("onvif: unsupported service")
+ }
+
buf := bytes.NewBuffer(nil)
buf.WriteString(
``,
@@ -213,11 +239,7 @@ func (c *Client) Request(path, body string) ([]byte, error) {
buf.WriteString(`` + body + ``)
client := &http.Client{Timeout: time.Second * 5000}
- res, err := client.Post(
- "http://"+c.url.Host+path,
- `application/soap+xml;charset=utf-8`,
- buf,
- )
+ res, err := client.Post(url, `application/soap+xml;charset=utf-8`, buf)
if err != nil {
return nil, err
}
@@ -226,7 +248,7 @@ func (c *Client) Request(path, body string) ([]byte, error) {
b, err := io.ReadAll(res.Body)
if err == nil && res.StatusCode != http.StatusOK {
- err = errors.New(res.Status)
+ err = errors.New("onvif: " + res.Status + " for " + url)
}
return b, err
diff --git a/pkg/onvif/helpers.go b/pkg/onvif/helpers.go
index 714bc0fc..c5451c42 100644
--- a/pkg/onvif/helpers.go
+++ b/pkg/onvif/helpers.go
@@ -3,7 +3,6 @@ package onvif
import (
"github.com/AlexxIT/go2rtc/pkg/core"
"net"
- "net/url"
"regexp"
"strconv"
"strings"
@@ -12,11 +11,10 @@ import (
const (
PathDevice = "/onvif/device_service"
- PathMedia = "/onvif/media_service"
)
func FindTagValue(b []byte, tag string) string {
- re := regexp.MustCompile(tag + `[^>]*>([^<]+)`)
+ re := regexp.MustCompile(`<[^/>]*` + tag + `[^>]*>([^<]+)`)
m := re.FindSubmatch(b)
if len(m) != 2 {
return ""
@@ -30,7 +28,7 @@ func UUID() string {
return s[:8] + "-" + s[8:12] + "-" + s[12:16] + "-" + s[16:20] + "-" + s[20:]
}
-func DiscoveryStreamingHosts() ([]string, error) {
+func DiscoveryStreamingURLs() ([]string, error) {
conn, err := net.ListenUDP("udp4", nil)
if err != nil {
return nil, err
@@ -66,7 +64,7 @@ func DiscoveryStreamingHosts() ([]string, error) {
return nil, err
}
- var hosts []string
+ var urls []string
b := make([]byte, 8192)
for {
@@ -75,37 +73,28 @@ func DiscoveryStreamingHosts() ([]string, error) {
break
}
+ //log.Printf("[onvif] discovery response addr=%s:\n%s", addr, b[:n])
+
// ignore printers, etc
if !strings.Contains(string(b[:n]), "onvif") {
continue
}
- //log.Printf("[onvif] discovery response:\n%s", b[:n])
-
- rawURL := FindTagValue(b[:n], "XAddrs")
- if rawURL == "" {
- continue
- }
-
- u, err := url.Parse(rawURL)
- if err != nil {
- continue
- }
-
- if u.Scheme != "http" {
+ url := FindTagValue(b[:n], "XAddrs")
+ if url == "" {
continue
}
// fix some buggy cameras
// http://0.0.0.0:8080/onvif/device_service
- if strings.HasPrefix(u.Host, "0.0.0.0") {
- u.Host = addr.IP.String() + u.Host[7:]
+ if strings.HasPrefix(url, "http://0.0.0.0") {
+ url = "http://" + addr.IP.String() + url[14:]
}
- hosts = append(hosts, u.Host)
+ urls = append(urls, url)
}
- return hosts, nil
+ return urls, nil
}
func atoi(s string) int {
diff --git a/www/add.html b/www/add.html
index 476c65c5..fc8a7c46 100644
--- a/www/add.html
+++ b/www/add.html
@@ -60,6 +60,7 @@