diff --git a/internal/onvif/onvif.go b/internal/onvif/onvif.go index 8d80398a..65f8599a 100644 --- a/internal/onvif/onvif.go +++ b/internal/onvif/onvif.go @@ -166,21 +166,21 @@ func apiOnvif(w http.ResponseWriter, r *http.Request) { var items []*api.Source if src == "" { - urls, err := onvif.DiscoveryStreamingURLs() + devices, err := onvif.DiscoveryStreamingDevices() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - for _, rawURL := range urls { - u, err := url.Parse(rawURL) + for _, device := range devices { + u, err := url.Parse(device.URL) if err != nil { - log.Warn().Str("url", rawURL).Msg("[onvif] broken") + log.Warn().Str("url", device.URL).Msg("[onvif] broken") continue } if u.Scheme != "http" { - log.Warn().Str("url", rawURL).Msg("[onvif] unsupported") + log.Warn().Str("url", device.URL).Msg("[onvif] unsupported") continue } @@ -191,7 +191,11 @@ func apiOnvif(w http.ResponseWriter, r *http.Request) { u.Path = "" } - items = append(items, &api.Source{Name: u.Host, URL: u.String()}) + items = append(items, &api.Source{ + Name: u.Host, + URL: u.String(), + Info: device.Name + " " + device.Hardware, + }) } } else { client, err := onvif.NewClient(src) diff --git a/pkg/onvif/helpers.go b/pkg/onvif/helpers.go index 893beb00..8fac9ac4 100644 --- a/pkg/onvif/helpers.go +++ b/pkg/onvif/helpers.go @@ -12,6 +12,12 @@ import ( "github.com/AlexxIT/go2rtc/pkg/core" ) +type DiscoveryDevice struct { + URL string + Name string + Hardware string +} + func FindTagValue(b []byte, tag string) string { re := regexp.MustCompile(`(?s)<(?:\w+:)?` + tag + `\b[^>]*>([^<]+)`) m := re.FindSubmatch(b) @@ -27,7 +33,8 @@ func UUID() string { return s[:8] + "-" + s[8:12] + "-" + s[12:16] + "-" + s[16:20] + "-" + s[20:] } -func DiscoveryStreamingURLs() ([]string, error) { +// DiscoveryStreamingDevices return list of tuple (onvif_url, name, hardware) +func DiscoveryStreamingDevices() ([]DiscoveryDevice, error) { conn, err := net.ListenUDP("udp4", nil) if err != nil { return nil, err @@ -61,11 +68,9 @@ func DiscoveryStreamingURLs() ([]string, error) { return nil, err } - if err = conn.SetReadDeadline(time.Now().Add(time.Second * 3)); err != nil { - return nil, err - } + _ = conn.SetReadDeadline(time.Now().Add(5 * time.Second)) - var urls []string + var devices []DiscoveryDevice b := make([]byte, 8192) for { @@ -81,21 +86,35 @@ func DiscoveryStreamingURLs() ([]string, error) { continue } - url := FindTagValue(b[:n], "XAddrs") - if url == "" { + device := DiscoveryDevice{ + URL: FindTagValue(b[:n], "XAddrs"), + } + + if device.URL == "" { continue } // fix some buggy cameras // http://0.0.0.0:8080/onvif/device_service - if strings.HasPrefix(url, "http://0.0.0.0") { - url = "http://" + addr.IP.String() + url[14:] + if s, ok := strings.CutPrefix(device.URL, "http://0.0.0.0"); ok { + device.URL = "http://" + addr.IP.String() + s } - urls = append(urls, url) + // try to find the camera name and model (hardware) + scopes := FindTagValue(b[:n], "Scopes") + device.Name = findScope(scopes, "onvif://www.onvif.org/name/") + device.Hardware = findScope(scopes, "onvif://www.onvif.org/hardware/") + + devices = append(devices, device) } - return urls, nil + return devices, nil +} + +func findScope(s, prefix string) string { + s = core.Between(s, prefix, " ") + s, _ = url.QueryUnescape(s) + return s } func atoi(s string) int {