Files
onvif-go/imaging.go
T
2025-10-30 00:50:27 +00:00

354 lines
12 KiB
Go

package onvif
import (
"context"
"encoding/xml"
"fmt"
"github.com/0x524A/go-onvif/soap"
)
// Imaging service namespace
const imagingNamespace = "http://www.onvif.org/ver20/imaging/wsdl"
// GetImagingSettings retrieves imaging settings for a video source
func (c *Client) GetImagingSettings(ctx context.Context, videoSourceToken string) (*ImagingSettings, error) {
endpoint := c.imagingEndpoint
if endpoint == "" {
endpoint = c.endpoint
}
type GetImagingSettings struct {
XMLName xml.Name `xml:"timg:GetImagingSettings"`
Xmlns string `xml:"xmlns:timg,attr"`
VideoSourceToken string `xml:"timg:VideoSourceToken"`
}
type GetImagingSettingsResponse struct {
XMLName xml.Name `xml:"GetImagingSettingsResponse"`
ImagingSettings struct {
BacklightCompensation *struct {
Mode string `xml:"Mode"`
Level float64 `xml:"Level"`
} `xml:"BacklightCompensation"`
Brightness *float64 `xml:"Brightness"`
ColorSaturation *float64 `xml:"ColorSaturation"`
Contrast *float64 `xml:"Contrast"`
Exposure *struct {
Mode string `xml:"Mode"`
Priority string `xml:"Priority"`
MinExposureTime float64 `xml:"MinExposureTime"`
MaxExposureTime float64 `xml:"MaxExposureTime"`
MinGain float64 `xml:"MinGain"`
MaxGain float64 `xml:"MaxGain"`
MinIris float64 `xml:"MinIris"`
MaxIris float64 `xml:"MaxIris"`
ExposureTime float64 `xml:"ExposureTime"`
Gain float64 `xml:"Gain"`
Iris float64 `xml:"Iris"`
} `xml:"Exposure"`
Focus *struct {
AutoFocusMode string `xml:"AutoFocusMode"`
DefaultSpeed float64 `xml:"DefaultSpeed"`
NearLimit float64 `xml:"NearLimit"`
FarLimit float64 `xml:"FarLimit"`
} `xml:"Focus"`
IrCutFilter *string `xml:"IrCutFilter"`
Sharpness *float64 `xml:"Sharpness"`
WideDynamicRange *struct {
Mode string `xml:"Mode"`
Level float64 `xml:"Level"`
} `xml:"WideDynamicRange"`
WhiteBalance *struct {
Mode string `xml:"Mode"`
CrGain float64 `xml:"CrGain"`
CbGain float64 `xml:"CbGain"`
} `xml:"WhiteBalance"`
} `xml:"ImagingSettings"`
}
req := GetImagingSettings{
Xmlns: imagingNamespace,
VideoSourceToken: videoSourceToken,
}
var resp GetImagingSettingsResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetImagingSettings failed: %w", err)
}
settings := &ImagingSettings{
Brightness: resp.ImagingSettings.Brightness,
ColorSaturation: resp.ImagingSettings.ColorSaturation,
Contrast: resp.ImagingSettings.Contrast,
IrCutFilter: resp.ImagingSettings.IrCutFilter,
Sharpness: resp.ImagingSettings.Sharpness,
}
if resp.ImagingSettings.BacklightCompensation != nil {
settings.BacklightCompensation = &BacklightCompensation{
Mode: resp.ImagingSettings.BacklightCompensation.Mode,
Level: resp.ImagingSettings.BacklightCompensation.Level,
}
}
if resp.ImagingSettings.Exposure != nil {
settings.Exposure = &Exposure{
Mode: resp.ImagingSettings.Exposure.Mode,
Priority: resp.ImagingSettings.Exposure.Priority,
MinExposureTime: resp.ImagingSettings.Exposure.MinExposureTime,
MaxExposureTime: resp.ImagingSettings.Exposure.MaxExposureTime,
MinGain: resp.ImagingSettings.Exposure.MinGain,
MaxGain: resp.ImagingSettings.Exposure.MaxGain,
MinIris: resp.ImagingSettings.Exposure.MinIris,
MaxIris: resp.ImagingSettings.Exposure.MaxIris,
ExposureTime: resp.ImagingSettings.Exposure.ExposureTime,
Gain: resp.ImagingSettings.Exposure.Gain,
Iris: resp.ImagingSettings.Exposure.Iris,
}
}
if resp.ImagingSettings.Focus != nil {
settings.Focus = &FocusConfiguration{
AutoFocusMode: resp.ImagingSettings.Focus.AutoFocusMode,
DefaultSpeed: resp.ImagingSettings.Focus.DefaultSpeed,
NearLimit: resp.ImagingSettings.Focus.NearLimit,
FarLimit: resp.ImagingSettings.Focus.FarLimit,
}
}
if resp.ImagingSettings.WideDynamicRange != nil {
settings.WideDynamicRange = &WideDynamicRange{
Mode: resp.ImagingSettings.WideDynamicRange.Mode,
Level: resp.ImagingSettings.WideDynamicRange.Level,
}
}
if resp.ImagingSettings.WhiteBalance != nil {
settings.WhiteBalance = &WhiteBalance{
Mode: resp.ImagingSettings.WhiteBalance.Mode,
CrGain: resp.ImagingSettings.WhiteBalance.CrGain,
CbGain: resp.ImagingSettings.WhiteBalance.CbGain,
}
}
return settings, nil
}
// SetImagingSettings sets imaging settings for a video source
func (c *Client) SetImagingSettings(ctx context.Context, videoSourceToken string, settings *ImagingSettings, forcePersistence bool) error {
endpoint := c.imagingEndpoint
if endpoint == "" {
endpoint = c.endpoint
}
type SetImagingSettings struct {
XMLName xml.Name `xml:"timg:SetImagingSettings"`
Xmlns string `xml:"xmlns:timg,attr"`
VideoSourceToken string `xml:"timg:VideoSourceToken"`
ImagingSettings struct {
BacklightCompensation *struct {
Mode string `xml:"Mode"`
Level float64 `xml:"Level"`
} `xml:"BacklightCompensation,omitempty"`
Brightness *float64 `xml:"Brightness,omitempty"`
ColorSaturation *float64 `xml:"ColorSaturation,omitempty"`
Contrast *float64 `xml:"Contrast,omitempty"`
Exposure *struct {
Mode string `xml:"Mode"`
Priority string `xml:"Priority,omitempty"`
MinExposureTime float64 `xml:"MinExposureTime,omitempty"`
MaxExposureTime float64 `xml:"MaxExposureTime,omitempty"`
MinGain float64 `xml:"MinGain,omitempty"`
MaxGain float64 `xml:"MaxGain,omitempty"`
MinIris float64 `xml:"MinIris,omitempty"`
MaxIris float64 `xml:"MaxIris,omitempty"`
ExposureTime float64 `xml:"ExposureTime,omitempty"`
Gain float64 `xml:"Gain,omitempty"`
Iris float64 `xml:"Iris,omitempty"`
} `xml:"Exposure,omitempty"`
Focus *struct {
AutoFocusMode string `xml:"AutoFocusMode"`
DefaultSpeed float64 `xml:"DefaultSpeed,omitempty"`
NearLimit float64 `xml:"NearLimit,omitempty"`
FarLimit float64 `xml:"FarLimit,omitempty"`
} `xml:"Focus,omitempty"`
IrCutFilter *string `xml:"IrCutFilter,omitempty"`
Sharpness *float64 `xml:"Sharpness,omitempty"`
WideDynamicRange *struct {
Mode string `xml:"Mode"`
Level float64 `xml:"Level,omitempty"`
} `xml:"WideDynamicRange,omitempty"`
WhiteBalance *struct {
Mode string `xml:"Mode"`
CrGain float64 `xml:"CrGain,omitempty"`
CbGain float64 `xml:"CbGain,omitempty"`
} `xml:"WhiteBalance,omitempty"`
} `xml:"timg:ImagingSettings"`
ForcePersistence bool `xml:"timg:ForcePersistence"`
}
req := SetImagingSettings{
Xmlns: imagingNamespace,
VideoSourceToken: videoSourceToken,
ForcePersistence: forcePersistence,
}
// Map settings
if settings.BacklightCompensation != nil {
req.ImagingSettings.BacklightCompensation = &struct {
Mode string `xml:"Mode"`
Level float64 `xml:"Level"`
}{
Mode: settings.BacklightCompensation.Mode,
Level: settings.BacklightCompensation.Level,
}
}
req.ImagingSettings.Brightness = settings.Brightness
req.ImagingSettings.ColorSaturation = settings.ColorSaturation
req.ImagingSettings.Contrast = settings.Contrast
req.ImagingSettings.IrCutFilter = settings.IrCutFilter
req.ImagingSettings.Sharpness = settings.Sharpness
if settings.Exposure != nil {
req.ImagingSettings.Exposure = &struct {
Mode string `xml:"Mode"`
Priority string `xml:"Priority,omitempty"`
MinExposureTime float64 `xml:"MinExposureTime,omitempty"`
MaxExposureTime float64 `xml:"MaxExposureTime,omitempty"`
MinGain float64 `xml:"MinGain,omitempty"`
MaxGain float64 `xml:"MaxGain,omitempty"`
MinIris float64 `xml:"MinIris,omitempty"`
MaxIris float64 `xml:"MaxIris,omitempty"`
ExposureTime float64 `xml:"ExposureTime,omitempty"`
Gain float64 `xml:"Gain,omitempty"`
Iris float64 `xml:"Iris,omitempty"`
}{
Mode: settings.Exposure.Mode,
Priority: settings.Exposure.Priority,
MinExposureTime: settings.Exposure.MinExposureTime,
MaxExposureTime: settings.Exposure.MaxExposureTime,
MinGain: settings.Exposure.MinGain,
MaxGain: settings.Exposure.MaxGain,
MinIris: settings.Exposure.MinIris,
MaxIris: settings.Exposure.MaxIris,
ExposureTime: settings.Exposure.ExposureTime,
Gain: settings.Exposure.Gain,
Iris: settings.Exposure.Iris,
}
}
if settings.Focus != nil {
req.ImagingSettings.Focus = &struct {
AutoFocusMode string `xml:"AutoFocusMode"`
DefaultSpeed float64 `xml:"DefaultSpeed,omitempty"`
NearLimit float64 `xml:"NearLimit,omitempty"`
FarLimit float64 `xml:"FarLimit,omitempty"`
}{
AutoFocusMode: settings.Focus.AutoFocusMode,
DefaultSpeed: settings.Focus.DefaultSpeed,
NearLimit: settings.Focus.NearLimit,
FarLimit: settings.Focus.FarLimit,
}
}
if settings.WideDynamicRange != nil {
req.ImagingSettings.WideDynamicRange = &struct {
Mode string `xml:"Mode"`
Level float64 `xml:"Level,omitempty"`
}{
Mode: settings.WideDynamicRange.Mode,
Level: settings.WideDynamicRange.Level,
}
}
if settings.WhiteBalance != nil {
req.ImagingSettings.WhiteBalance = &struct {
Mode string `xml:"Mode"`
CrGain float64 `xml:"CrGain,omitempty"`
CbGain float64 `xml:"CbGain,omitempty"`
}{
Mode: settings.WhiteBalance.Mode,
CrGain: settings.WhiteBalance.CrGain,
CbGain: settings.WhiteBalance.CbGain,
}
}
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, nil); err != nil {
return fmt.Errorf("SetImagingSettings failed: %w", err)
}
return nil
}
// Move performs a focus move operation
func (c *Client) Move(ctx context.Context, videoSourceToken string, focus *FocusMove) error {
endpoint := c.imagingEndpoint
if endpoint == "" {
endpoint = c.endpoint
}
type Move struct {
XMLName xml.Name `xml:"timg:Move"`
Xmlns string `xml:"xmlns:timg,attr"`
VideoSourceToken string `xml:"timg:VideoSourceToken"`
Focus *struct {
Absolute *struct {
Position float64 `xml:"Position"`
Speed float64 `xml:"Speed,omitempty"`
} `xml:"Absolute,omitempty"`
Relative *struct {
Distance float64 `xml:"Distance"`
Speed float64 `xml:"Speed,omitempty"`
} `xml:"Relative,omitempty"`
Continuous *struct {
Speed float64 `xml:"Speed"`
} `xml:"Continuous,omitempty"`
} `xml:"timg:Focus"`
}
req := Move{
Xmlns: imagingNamespace,
VideoSourceToken: videoSourceToken,
}
if focus != nil {
req.Focus = &struct {
Absolute *struct {
Position float64 `xml:"Position"`
Speed float64 `xml:"Speed,omitempty"`
} `xml:"Absolute,omitempty"`
Relative *struct {
Distance float64 `xml:"Distance"`
Speed float64 `xml:"Speed,omitempty"`
} `xml:"Relative,omitempty"`
Continuous *struct {
Speed float64 `xml:"Speed"`
} `xml:"Continuous,omitempty"`
}{}
// Implementation would add specific focus move types here
}
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, nil); err != nil {
return fmt.Errorf("Move failed: %w", err)
}
return nil
}
// FocusMove represents a focus move operation (placeholder for focus move types)
type FocusMove struct {
// Can be extended with Absolute, Relative, Continuous move types
}