goonvif/device.go

289 lines
7.9 KiB
Go
Raw Normal View History

2024-10-30 18:51:51 +08:00
package goonvif
import (
"context"
"errors"
"net"
"net/http"
"strings"
2024-10-31 18:22:47 +08:00
"time"
2024-10-30 18:51:51 +08:00
2024-10-31 18:22:47 +08:00
"git.pyer.club/kingecg/goonvif/onvif"
device "git.pyer.club/kingecg/goonvif/onvif/device"
"git.pyer.club/kingecg/goonvif/onvif/media"
sdk "git.pyer.club/kingecg/goonvif/onvif/sdk/device"
media_sdk "git.pyer.club/kingecg/goonvif/onvif/sdk/media"
wsdiscovery "git.pyer.club/kingecg/goonvif/onvif/ws-discovery"
"git.pyer.club/kingecg/goonvif/onvif/xsd"
onvifmodel "git.pyer.club/kingecg/goonvif/onvif/xsd/onvif"
"git.pyer.club/kingecg/goonvif/replay"
replay_sdk "git.pyer.club/kingecg/goonvif/sdk/replay"
2024-10-30 18:51:51 +08:00
search_sdk "git.pyer.club/kingecg/goonvif/sdk/search"
"git.pyer.club/kingecg/goonvif/search"
"github.com/beevik/etree"
)
type Device struct {
onvif.DeviceParams
2024-10-31 18:22:47 +08:00
device *onvif.Device
ctx context.Context
mediaCapablities *media.Capabilities
2024-10-30 18:51:51 +08:00
}
var ErrAuthRequired = errors.New("auth required")
func NewDevice(params onvif.DeviceParams) *Device {
return &Device{DeviceParams: params, ctx: context.Background()}
}
func (d *Device) check() error {
if d.device != nil {
return nil
}
if d.Username == "" || d.Password == "" {
return ErrAuthRequired
}
if d.HttpClient == nil {
d.HttpClient = new(http.Client)
}
device, err := onvif.NewDevice(d.DeviceParams)
if err != nil {
return err
}
d.device = device
return nil
}
func (d *Device) GetCapabilities() (interface{}, error) {
err := d.check()
if err != nil {
return nil, err
}
cap := device.GetCapabilities{Category: "All"}
getCapabilitiesResponse, err := sdk.Call_GetCapabilities(d.ctx, d.device, cap)
if err != nil {
return nil, err
}
r := getCapabilitiesResponse.Capabilities
return r, err
}
func (d *Device) GetRecordingSummary() (search.RecordingSummary, error) {
err := d.check()
if err != nil {
return search.RecordingSummary{}, err
}
resp, rerr := search_sdk.Call_GetRecordingSummary(d.ctx, d.device, search.GetRecordingSummary{})
if rerr != nil {
return search.RecordingSummary{}, rerr
}
return resp.Summary, nil
}
func (d *Device) GetEndpoints() (map[string]string, error) {
err := d.check()
if err != nil {
return nil, err
}
return d.device.GetServices(), nil
}
2024-10-31 18:22:47 +08:00
func (d *Device) FindRecording(start time.Time, end time.Time) ([]string, error) {
err := d.check()
if err != nil {
return nil, err
}
// recordFilterTemplate := "not (boolean(//EarliestRecording > %s) or boolean(//LatestRecording < %s) "
// filter := fmt.Sprintf(recordFilterTemplate, end.Format("2006-01-02T15:04:05Z"), start.Format("2006-01-02T15:04:05Z"))
fr, ferr := search_sdk.Call_FindRecordings(d.ctx, d.device, search.FindRecordings{})
if ferr != nil {
return nil, ferr
}
searchToken := fr.SearchToken
resultlist := make([]search.RecordingInformation, 0)
for {
gr, gerr := search_sdk.Call_GetRecordingSearchResults(d.ctx, d.device, search.GetRecordingSearchResults{SearchToken: searchToken})
if gerr != nil {
return nil, gerr
}
if len(gr.ResultList.RecordingInformation) > 0 {
resultlist = append(resultlist, gr.ResultList.RecordingInformation...)
}
if gr.ResultList.SearchState == "Completed" {
break
}
}
recordingTokes := make([]string, 0)
for _, r := range resultlist {
rstartStr := string(xsd.DateTime(r.EarliestRecording))
rendStr := string(xsd.DateTime(r.LatestRecording))
rstart, _ := time.Parse("2006-01-02T15:04:05Z", rstartStr)
rend, _ := time.Parse("2006-01-02T15:04:05Z", rendStr)
if rstart.After(end) || rend.Before(start) {
continue
}
recordingTokes = append(recordingTokes, string(r.RecordingToken))
}
return recordingTokes, nil
}
func (d *Device) GetReplayUriTcp(token string) (string, error) {
return d.GetReplayUri(token, TcpUnicastStreamSetup)
}
func (d *Device) GetReplayUriUdpUnicast(token string) (string, error) {
return d.GetReplayUri(token, UdpUnicastStreamSetup)
}
func (d *Device) GetReplayUriUdpMulticast(token string) (string, error) {
return d.GetReplayUri(token, RtspMulticastStreamSetup)
}
func (d *Device) GetMediaCapablities() (media.Capabilities, error) {
err := d.check()
if err != nil {
return media.Capabilities{}, err
}
if d.mediaCapablities != nil {
return *d.mediaCapablities, nil
}
resp, err := media_sdk.Call_GetServiceCapabilities(d.ctx, d.device, media.GetServiceCapabilities{})
if err != nil {
return media.Capabilities{}, err
}
d.mediaCapablities = &resp.Capabilities
return resp.Capabilities, nil
}
// GetReplayUri 获取设备回放的URI。
// 参数:
//
// token: 用于标识回放的令牌。
// transport: 传输协议UDP=RTP/UDP, RTSP=RTP/RTSP/TCP or HTTP=RTP/RTSP/HTTP/TCP。
// multiCast: 布尔值,指示是否使用多播。
//
// 返回值:
//
// string: 回放的URI。
// error: 错误信息,如果执行操作时发生错误。
func (d *Device) GetReplayUri(token string, stream onvifmodel.StreamSetup) (string, error) {
// 检查设备状态
err := d.check()
if err != nil {
return "", err
}
// 调用SDK方法获取回放URI
resp, err := replay_sdk.Call_GetReplayUri(d.ctx, d.device, replay.GetReplayUri{StreamSetup: stream, RecordingToken: onvifmodel.ReferenceToken(token)})
if err != nil {
return "", err
}
// 返回获取到的URI
return string(resp.Uri), nil
}
func (d *Device) GetStreamUri(stream onvifmodel.StreamSetup, profileToken string) (string, error) {
err := d.check()
if err != nil {
return "", err
}
getStreamUri := media.GetStreamUri{StreamSetup: stream}
if profileToken != "" {
getStreamUri.ProfileToken = onvifmodel.ReferenceToken(profileToken)
}
resp, rerr := media_sdk.Call_GetStreamUri(d.ctx, d.device, getStreamUri)
// resp, rerr := media_sdk.Call_GetStreamUri(d.ctx, d.device, media.GetStreamUri{})
return string(resp.MediaUri.Uri), rerr
}
func (d *Device) GetStreamUriTcp(profileToken string) (string, error) {
return d.GetStreamUri(TcpUnicastStreamSetup, profileToken)
}
func (d *Device) GetStreamUriUdp(profileToken string) (string, error) {
return d.GetStreamUri(UdpUnicastStreamSetup, profileToken)
}
func (d *Device) GetSnapshotUri(profileToken string) (string, error) {
return "", nil
}
func (d *Device) GetProfiles() ([]onvifmodel.Profile, error) {
err := d.check()
if err != nil {
return nil, err
}
resp, err := media_sdk.Call_GetProfiles(d.ctx, d.device, media.GetProfiles{})
if err != nil {
return nil, err
}
return resp.Profiles, nil
}
2024-10-30 18:51:51 +08:00
func GetAvailableDevicesAtSpecificEthernetInterface(interfaceName string) ([]Device, error) {
// Call a ws-discovery Probe Message to Discover NVT type Devices
devices, err := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:" + onvif.NVT.String()}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"})
if err != nil {
return nil, err
}
nvtDevicesSeen := make(map[string]bool)
nvtDevices := make([]Device, 0)
for _, j := range devices {
doc := etree.NewDocument()
if err := doc.ReadFromString(j); err != nil {
return nil, err
}
for _, xaddr := range doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/XAddrs") {
xaddr := strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2]
if !nvtDevicesSeen[xaddr] {
dev := NewDevice(onvif.DeviceParams{Xaddr: strings.Split(xaddr, " ")[0]})
nvtDevicesSeen[xaddr] = true
nvtDevices = append(nvtDevices, *dev)
}
}
}
return nvtDevices, nil
}
func Discovery() ([]Device, error) {
ifaces, err := listLocalNetworkInterfaces()
if err != nil {
return nil, err
}
devices := make([]Device, 0)
for _, iface := range ifaces {
idevices, err := GetAvailableDevicesAtSpecificEthernetInterface(iface)
if err != nil {
continue
}
devices = append(devices, idevices...)
}
return devices, nil
}
func listLocalNetworkInterfaces() ([]string, error) {
interfaces, err := net.Interfaces()
if err != nil {
return nil, err
}
var ifaceNames []string
for _, iface := range interfaces {
// 判断是否为局域网
if (iface.Flags&net.FlagUp) == 0 || (iface.Flags&net.FlagRunning) == 0 || (iface.Flags&net.FlagLoopback) != 0 {
continue
}
ifaceNames = append(ifaceNames, iface.Name)
}
return ifaceNames, nil
}