diff --git a/cmd/onvif/init.go b/cmd/onvif/init.go
index 928b77d4c..ae8101fa6 100644
--- a/cmd/onvif/init.go
+++ b/cmd/onvif/init.go
@@ -1,6 +1,13 @@
package onvif
import (
+ "io"
+ "net"
+ "net/http"
+ "os"
+ "strconv"
+ "time"
+
"github.com/AlexxIT/go2rtc/cmd/api"
"github.com/AlexxIT/go2rtc/cmd/app"
"github.com/AlexxIT/go2rtc/cmd/rtsp"
@@ -8,12 +15,6 @@ import (
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/onvif"
"github.com/rs/zerolog"
- "io"
- "net"
- "net/http"
- "os"
- "strconv"
- "time"
)
func Init() {
@@ -132,7 +133,7 @@ func apiOnvif(w http.ResponseWriter, r *http.Request) {
for _, host := range hosts {
items = append(items, api.Stream{
Name: host,
- URL: "onvif://user:pass@" + host,
+ URL: "onvif://" + host,
})
}
} else {
@@ -155,18 +156,25 @@ func apiOnvif(w http.ResponseWriter, r *http.Request) {
}
for i, token := range tokens {
+
+ streamuri, err := client.GetStreamUri(token)
+ if err != nil || streamuri == "" {
+ continue
+ }
items = append(items, api.Stream{
Name: name + " stream" + strconv.Itoa(i),
URL: src + "?subtype=" + token,
})
- }
-
- if len(tokens) > 0 && client.HasSnapshots() {
+ snapshoturi, err := client.GetSnapshotUri(token)
+ if err != nil || snapshoturi == "" {
+ continue
+ }
items = append(items, api.Stream{
- Name: name + " snapshot",
+ Name: name + " snapshot" + strconv.Itoa(i),
URL: src + "?subtype=" + tokens[0] + "&snapshot",
})
}
+
}
api.ResponseStreams(w, items)
diff --git a/go.mod b/go.mod
index beae7f799..6f31fbb01 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/AlexxIT/go2rtc
go 1.20
require (
+ github.com/antchfx/xmlquery v1.3.15
github.com/brutella/hap v0.0.17
github.com/deepch/vdk v0.0.19
github.com/gorilla/websocket v1.5.0
@@ -24,9 +25,11 @@ require (
)
require (
+ github.com/antchfx/xpath v1.2.4 // indirect
github.com/brutella/dnssd v1.2.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-chi/chi v1.5.4 // indirect
+ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
@@ -45,9 +48,9 @@ require (
github.com/xiam/to v0.0.0-20200126224905-d60d31e03561 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/mod v0.9.0 // indirect
- golang.org/x/net v0.8.0 // indirect
- golang.org/x/sys v0.6.0 // indirect
- golang.org/x/text v0.8.0 // indirect
+ golang.org/x/net v0.9.0 // indirect
+ golang.org/x/sys v0.7.0 // indirect
+ golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.7.0 // indirect
)
diff --git a/go.sum b/go.sum
index 20a2e4ee3..c4d2a0bab 100644
--- a/go.sum
+++ b/go.sum
@@ -2,6 +2,11 @@ github.com/AlexxIT/hap v0.0.15-0.20221108133010-d8a45b7a7045 h1:xJf3FxQJReJSDyYX
github.com/AlexxIT/hap v0.0.15-0.20221108133010-d8a45b7a7045/go.mod h1:QNA3sm16zE5uUyC8+E/gNkMvQWjqQLuxQKkU5PMi8N4=
github.com/AlexxIT/vdk v0.0.18-0.20221108193131-6168555b4f92 h1:cIeYMGaAirSZnrKRDTb5VgZDDYqPLhYiczElMg4sQW0=
github.com/AlexxIT/vdk v0.0.18-0.20221108193131-6168555b4f92/go.mod h1:7ydHfSkflMZxBXfWR79dMjrT54xzvLxnPaByOa9Jpzg=
+github.com/antchfx/xmlquery v1.3.15 h1:aJConNMi1sMha5G8YJoAIF5P+H+qG1L73bSItWHo8Tw=
+github.com/antchfx/xmlquery v1.3.15/go.mod h1:zMDv5tIGjOxY/JCNNinnle7V/EwthZ5IT8eeCGJKRWA=
+github.com/antchfx/xpath v1.2.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
+github.com/antchfx/xpath v1.2.4 h1:dW1HB/JxKvGtJ9WyVGJ0sIoEcqftV3SqIstujI+B9XY=
+github.com/antchfx/xpath v1.2.4/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/brutella/dnssd v1.2.3/go.mod h1:JoW2sJUrmVIef25G6lrLj7HS6Xdwh6q8WUIvMkkBYXs=
github.com/brutella/dnssd v1.2.5 h1:b8syhho41/5ikw3X2X4baR9NWEBSlpZnfQgujsv7bk4=
github.com/brutella/dnssd v1.2.5/go.mod h1:JoW2sJUrmVIef25G6lrLj7HS6Xdwh6q8WUIvMkkBYXs=
@@ -15,6 +20,8 @@ github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -157,8 +164,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -189,8 +196,8 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -203,8 +210,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
diff --git a/pkg/onvif/client.go b/pkg/onvif/client.go
index 77157d0b1..905428333 100644
--- a/pkg/onvif/client.go
+++ b/pkg/onvif/client.go
@@ -5,18 +5,22 @@ import (
"crypto/sha1"
"encoding/base64"
"errors"
+ "fmt"
"github.com/AlexxIT/go2rtc/pkg/core"
- "html"
+ "github.com/antchfx/xmlquery"
+
"io"
"net/http"
"net/url"
"regexp"
- "strings"
"time"
)
type Client struct {
- url *url.URL
+ url *url.URL
+ media_service string
+ image_service string
+ device_service string
}
func NewClient(rawURL string) (*Client, error) {
@@ -24,7 +28,8 @@ func NewClient(rawURL string) (*Client, error) {
if err != nil {
return nil, err
}
- return &Client{url: u}, nil
+ client := Client{url: u, device_service: "onvif/device_service"}
+ return &client, nil
}
func (c *Client) GetURI() (string, error) {
@@ -49,14 +54,11 @@ func (c *Client) GetURI() (string, error) {
getUri = c.GetSnapshotUri
}
- b, err := getUri(token)
+ uri, err := getUri(token)
if err != nil {
return "", err
}
- uri := FindTagValue(b, "Uri")
- uri = html.UnescapeString(uri)
-
u, err := url.Parse(uri)
if err != nil {
return "", err
@@ -94,81 +96,113 @@ func (c *Client) GetProfilesTokens() ([]string, error) {
return tokens, nil
}
-func (c *Client) HasSnapshots() bool {
- b, err := c.GetServiceCapabilities()
+func (c *Client) GetCapabilities() ([]byte, error) {
+ response, err := c.Request(
+ `
+ All
+`, c.device_service,
+ )
+ doc, err := xmlquery.Parse(bytes.NewReader(response))
if err != nil {
- return false
+ return nil, err
}
- return strings.Contains(string(b), `SnapshotUri="true"`)
-}
-func (c *Client) GetCapabilities() ([]byte, error) {
- return c.Request(
- `
- All
-`,
- )
+ c.media_service = xmlquery.FindOne(doc, "//tt:Media/tt:XAddr").InnerText()
+ c.image_service = xmlquery.FindOne(doc, "//tt:Imaging/tt:XAddr").InnerText()
+ return response, err
}
func (c *Client) GetNetworkInterfaces() ([]byte, error) {
- return c.Request(``)
+ return c.Request(``, c.device_service)
}
func (c *Client) GetDeviceInformation() ([]byte, error) {
- return c.Request(``)
+ return c.Request(``, c.device_service)
}
func (c *Client) GetProfiles() ([]byte, error) {
- return c.Request(``)
+ return c.Request(``, c.media_service)
}
-func (c *Client) GetStreamUri(token string) ([]byte, error) {
- return c.Request(
+func (c *Client) GetStreamUri(token string) (string, error) {
+ fmt.Println("Query Stream URI with token " + token)
+ response, err := c.Request(
`
RTP-Unicast
RTSP
- ` + token + `
-`)
+ `+token+`
+`, c.media_service)
+ if err != nil {
+ return "", err
+ }
+ doc, err := xmlquery.Parse(bytes.NewReader(response))
+ if err != nil {
+ return "", err
+ }
+
+ stream_url, err := xmlquery.Query(doc, "//trt:MediaUri/tt:Uri")
+ if err != nil {
+ fmt.Printf("Error: %v", err)
+ return "", err
+ }
+ fmt.Println("Stream URI: " + stream_url.InnerText())
+ return stream_url.InnerText(), err
}
-func (c *Client) GetSnapshotUri(token string) ([]byte, error) {
- return c.Request(
- `
- ` + token + `
-`)
+func (c *Client) GetSnapshotUri(token string) (string, error) {
+ fmt.Println("Query Snaphot URI with token " + token)
+ response, err := c.Request(
+ `
+ `+token+`
+`, c.media_service)
+ if err != nil {
+ return "", err
+ }
+ doc, err := xmlquery.Parse(bytes.NewReader(response))
+ if err != nil {
+ return "", err
+ }
+
+ stream_url, err := xmlquery.Query(doc, "//trt:MediaUri/tt:Uri")
+ if err != nil {
+ fmt.Printf("Error: %v", err)
+ return "", err
+ }
+ fmt.Println("Snaphot URI: " + stream_url.InnerText())
+ return stream_url.InnerText(), err
}
func (c *Client) GetSystemDateAndTime() ([]byte, error) {
return c.Request(
- ``,
+ ``, c.device_service,
)
}
func (c *Client) GetServiceCapabilities() ([]byte, error) {
return c.Request(
- ``,
+ ``, c.device_service,
)
}
func (c *Client) SystemReboot() ([]byte, error) {
return c.Request(
- ``,
+ ``, c.device_service,
)
}
func (c *Client) GetServices() ([]byte, error) {
return c.Request(`
true
-`)
+`, c.device_service)
}
func (c *Client) GetScopes() ([]byte, error) {
- return c.Request(``)
+ return c.Request(``, c.device_service)
}
-func (c *Client) Request(body string) ([]byte, error) {
+func (c *Client) Request(body string, path string) ([]byte, error) {
buf := bytes.NewBuffer(nil)
buf.WriteString(
``,
@@ -198,7 +232,7 @@ func (c *Client) Request(body string) ([]byte, error) {
client := &http.Client{Timeout: time.Second * 5000}
res, err := client.Post(
- "http://"+c.url.Host+"/onvif/",
+ "http://"+c.url.Host+"/"+path,
`application/soap+xml;charset=utf-8`,
buf,
)
diff --git a/pkg/onvif/helpers.go b/pkg/onvif/helpers.go
index dd12e6bc3..3d45ea88b 100644
--- a/pkg/onvif/helpers.go
+++ b/pkg/onvif/helpers.go
@@ -30,20 +30,22 @@ func DiscoveryStreamingHosts() ([]string, error) {
return nil, err
}
- msg := `
-
-
- http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe
- uuid:` + UUID() + `
- urn:schemas-xmlsoap-org:ws:2005:04:discovery
-
-
-
- tds:Device
- onvif://www.onvif.org/Profile/Streaming
-
-
-`
+ msg := `
+
+ urn:uuid:` + UUID() + `
+ urn:schemas-xmlsoap-org:ws:2005:04:discovery
+ http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe
+
+
+
+
+
+
+
+`
addr := &net.UDPAddr{
IP: net.IP{239, 255, 255, 250},
diff --git a/www/add.html b/www/add.html
index 476c65c56..2bcfbb755 100644
--- a/www/add.html
+++ b/www/add.html
@@ -204,6 +204,15 @@
await getStreams(url.toString(), 'onvif-table')
})
+
+ // Add event listener to the table src cell
+ document.getElementById('onvif-table').addEventListener('click', ev => {
+ const target = ev.target;
+ if (target.tagName === 'TD' && target.cellIndex === 1) { // Check if the clicked cell is in the "Source" column
+ const inputBox = document.querySelector('#onvif-form input[name="src"]');
+ inputBox.value = target.textContent;
+ }
+ });