package server import ( "fmt" "time" "github.com/0x524a/onvif-go" ) const ( defaultPort = 8080 defaultTimeoutSec = 30 defaultWidth = 1920 defaultHeight = 1080 defaultFramerate = 30 defaultQuality = 80 defaultBitrate = 4096 maxPan = 180 maxTilt = 90 defaultPTZSpeed = 0.5 mediumWidth = 1280 mediumHeight = 720 mediumQuality = 75 highQuality = 85 mediumBitrate = 2048 lowFramerate = 25 highBitrate = 6144 maxZoom = 3 lowPTZSpeed = 0.3 presetZoom = 2 ) // Config represents the ONVIF server configuration. type Config struct { // Server settings Host string // Bind address (e.g., "0.0.0.0") Port int // Server port (default: 8080) BasePath string // Base path for services (default: "/onvif") Timeout time.Duration // Request timeout // Device information DeviceInfo DeviceInfo // Authentication Username string Password string // Camera profiles (supports multi-lens cameras) Profiles []ProfileConfig // Capabilities SupportPTZ bool SupportImaging bool SupportEvents bool } // DeviceInfo contains device identification information. type DeviceInfo struct { Manufacturer string Model string FirmwareVersion string SerialNumber string HardwareID string } // ProfileConfig represents a camera profile configuration. type ProfileConfig struct { Token string // Profile token (unique identifier) Name string // Profile name VideoSource VideoSourceConfig // Video source configuration AudioSource *AudioSourceConfig // Audio source configuration (optional) VideoEncoder VideoEncoderConfig // Video encoder configuration AudioEncoder *AudioEncoderConfig // Audio encoder configuration (optional) PTZ *PTZConfig // PTZ configuration (optional) Snapshot SnapshotConfig // Snapshot configuration } // VideoSourceConfig represents video source configuration. type VideoSourceConfig struct { Token string // Video source token Name string // Video source name Resolution Resolution Framerate int Bounds Bounds } // AudioSourceConfig represents audio source configuration. type AudioSourceConfig struct { Token string // Audio source token Name string // Audio source name SampleRate int // Sample rate in Hz (e.g., 8000, 16000, 48000) Bitrate int // Bitrate in kbps } // VideoEncoderConfig represents video encoder configuration. type VideoEncoderConfig struct { Encoding string // JPEG, H264, H265, MPEG4 Resolution Resolution // Video resolution Quality float64 // Quality (0-100) Framerate int // Frames per second Bitrate int // Bitrate in kbps GovLength int // GOP length } // AudioEncoderConfig represents audio encoder configuration. type AudioEncoderConfig struct { Encoding string // G711, G726, AAC Bitrate int // Bitrate in kbps SampleRate int // Sample rate in Hz } // PTZConfig represents PTZ configuration. type PTZConfig struct { NodeToken string // PTZ node token PanRange Range // Pan range in degrees TiltRange Range // Tilt range in degrees ZoomRange Range // Zoom range DefaultSpeed PTZSpeed // Default speed SupportsContinuous bool // Supports continuous move SupportsAbsolute bool // Supports absolute move SupportsRelative bool // Supports relative move Presets []Preset // Predefined presets } // SnapshotConfig represents snapshot configuration. type SnapshotConfig struct { Enabled bool // Whether snapshots are supported Resolution Resolution // Snapshot resolution Quality float64 // JPEG quality (0-100) } // Resolution represents video resolution. type Resolution struct { Width int Height int } // Bounds represents video bounds. type Bounds struct { X int Y int Width int Height int } // Range represents a numeric range. type Range struct { Min float64 Max float64 } // PTZSpeed represents PTZ movement speed. type PTZSpeed struct { Pan float64 // Pan speed (-1.0 to 1.0) Tilt float64 // Tilt speed (-1.0 to 1.0) Zoom float64 // Zoom speed (-1.0 to 1.0) } // Preset represents a PTZ preset position. type Preset struct { Token string // Preset token Name string // Preset name Position PTZPosition // Position } // PTZPosition represents PTZ position. type PTZPosition struct { Pan float64 // Pan position Tilt float64 // Tilt position Zoom float64 // Zoom position } // StreamConfig represents an RTSP stream configuration. type StreamConfig struct { ProfileToken string // Associated profile token RTSPPath string // RTSP path (e.g., "/stream1") StreamURI string // Full RTSP URI } // Server represents the ONVIF server. type Server struct { config *Config streams map[string]*StreamConfig // Profile token -> stream config ptzState map[string]*PTZState // Profile token -> PTZ state imagingState map[string]*ImagingState // Video source token -> imaging state systemTime time.Time } // PTZState represents the current PTZ state. type PTZState struct { Position PTZPosition Moving bool PanMoving bool TiltMoving bool ZoomMoving bool LastUpdate time.Time } // ImagingState represents the current imaging settings state. type ImagingState struct { Brightness float64 Contrast float64 Saturation float64 Sharpness float64 BacklightComp BacklightCompensation Exposure ExposureSettings Focus FocusSettings WhiteBalance WhiteBalanceSettings WideDynamicRange WDRSettings IrCutFilter string // ON, OFF, AUTO } // BacklightCompensation represents backlight compensation settings. type BacklightCompensation struct { Mode string // OFF, ON Level float64 // 0-100 } // ExposureSettings represents exposure settings. type ExposureSettings struct { Mode string // AUTO, MANUAL Priority string // LowNoise, FrameRate MinExposure float64 MaxExposure float64 MinGain float64 MaxGain float64 ExposureTime float64 Gain float64 } // FocusSettings represents focus settings. type FocusSettings struct { AutoFocusMode string // AUTO, MANUAL DefaultSpeed float64 NearLimit float64 FarLimit float64 CurrentPos float64 } // WhiteBalanceSettings represents white balance settings. type WhiteBalanceSettings struct { Mode string // AUTO, MANUAL CrGain float64 CbGain float64 } // WDRSettings represents wide dynamic range settings. type WDRSettings struct { Mode string // OFF, ON Level float64 // 0-100 } // DefaultConfig returns a default server configuration with a multi-lens camera setup. // //nolint:funlen // DefaultConfig has many statements due to comprehensive default configuration func DefaultConfig() *Config { return &Config{ Host: "0.0.0.0", Port: defaultPort, BasePath: "/onvif", Timeout: defaultTimeoutSec * time.Second, DeviceInfo: DeviceInfo{ Manufacturer: "onvif-go", Model: "Virtual Multi-Lens Camera", FirmwareVersion: "1.0.0", SerialNumber: "SN-12345678", HardwareID: "HW-87654321", }, Username: "admin", Password: "admin", SupportPTZ: true, SupportImaging: true, SupportEvents: false, Profiles: []ProfileConfig{ { Token: "profile_0", Name: "Main Camera - High Quality", VideoSource: VideoSourceConfig{ Token: "video_source_0", Name: "Main Camera", Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, Framerate: defaultFramerate, Bounds: Bounds{X: 0, Y: 0, Width: defaultWidth, Height: defaultHeight}, }, VideoEncoder: VideoEncoderConfig{ Encoding: "H264", Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, Quality: defaultQuality, Framerate: defaultFramerate, Bitrate: defaultBitrate, GovLength: defaultFramerate, }, PTZ: &PTZConfig{ NodeToken: "ptz_node_0", PanRange: Range{Min: -maxPan, Max: maxPan}, TiltRange: Range{Min: -maxTilt, Max: maxTilt}, ZoomRange: Range{Min: 0, Max: 1}, DefaultSpeed: PTZSpeed{ Pan: defaultPTZSpeed, Tilt: defaultPTZSpeed, Zoom: defaultPTZSpeed, }, SupportsContinuous: true, SupportsAbsolute: true, SupportsRelative: true, Presets: []Preset{ {Token: "preset_0", Name: "Home", Position: PTZPosition{Pan: 0, Tilt: 0, Zoom: 0}}, { Token: "preset_1", Name: "Entrance", Position: PTZPosition{Pan: -45, Tilt: -10, Zoom: defaultPTZSpeed}, }, }, }, Snapshot: SnapshotConfig{ Enabled: true, Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, Quality: highQuality, }, }, { Token: "profile_1", Name: "Wide Angle Camera", VideoSource: VideoSourceConfig{ Token: "video_source_1", Name: "Wide Angle Camera", Resolution: Resolution{Width: mediumWidth, Height: mediumHeight}, Framerate: defaultFramerate, Bounds: Bounds{X: 0, Y: 0, Width: mediumWidth, Height: mediumHeight}, }, VideoEncoder: VideoEncoderConfig{ Encoding: "H264", Resolution: Resolution{Width: mediumWidth, Height: mediumHeight}, Quality: mediumQuality, Framerate: defaultFramerate, Bitrate: mediumBitrate, GovLength: defaultFramerate, }, Snapshot: SnapshotConfig{ Enabled: true, Resolution: Resolution{Width: mediumWidth, Height: mediumHeight}, Quality: defaultQuality, }, }, { Token: "profile_2", Name: "Telephoto Camera", VideoSource: VideoSourceConfig{ Token: "video_source_2", Name: "Telephoto Camera", Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, Framerate: lowFramerate, Bounds: Bounds{X: 0, Y: 0, Width: defaultWidth, Height: defaultHeight}, }, VideoEncoder: VideoEncoderConfig{ Encoding: "H264", Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, Quality: highQuality, Framerate: lowFramerate, Bitrate: highBitrate, GovLength: lowFramerate, }, PTZ: &PTZConfig{ NodeToken: "ptz_node_2", PanRange: Range{Min: -maxPan, Max: maxPan}, TiltRange: Range{Min: -maxTilt, Max: maxTilt}, ZoomRange: Range{Min: 0, Max: maxZoom}, DefaultSpeed: PTZSpeed{ Pan: lowPTZSpeed, Tilt: lowPTZSpeed, Zoom: lowPTZSpeed, }, SupportsContinuous: true, SupportsAbsolute: true, SupportsRelative: true, Presets: []Preset{ {Token: "preset_2_0", Name: "Home", Position: PTZPosition{Pan: 0, Tilt: 0, Zoom: 0}}, { Token: "preset_2_1", Name: "Zoom In", Position: PTZPosition{Pan: 0, Tilt: 0, Zoom: presetZoom}, }, }, }, Snapshot: SnapshotConfig{ Enabled: true, Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, Quality: highQuality, }, }, }, } } // ServiceEndpoints returns the service endpoint URLs. func (c *Config) ServiceEndpoints(host string) map[string]string { if host == "" { host = c.Host if host == "0.0.0.0" || host == "" { host = "localhost" } } var baseURL string const httpPort = 80 if c.Port == httpPort { baseURL = "http://" + host + c.BasePath } else { // Import fmt at the top to use Sprintf baseURL = fmt.Sprintf("http://%s:%d%s", host, c.Port, c.BasePath) } endpoints := map[string]string{ "device": baseURL + "/device_service", "media": baseURL + "/media_service", "imaging": baseURL + "/imaging_service", } if c.SupportPTZ { endpoints["ptz"] = baseURL + "/ptz_service" } if c.SupportEvents { endpoints["events"] = baseURL + "/events_service" } return endpoints } // ToONVIFProfile converts a ProfileConfig to an ONVIF Profile. func (p *ProfileConfig) ToONVIFProfile() *onvif.Profile { profile := &onvif.Profile{ Token: p.Token, Name: p.Name, VideoSourceConfiguration: &onvif.VideoSourceConfiguration{ Token: p.VideoSource.Token, Name: p.VideoSource.Name, SourceToken: p.VideoSource.Token, Bounds: &onvif.IntRectangle{ X: p.VideoSource.Bounds.X, Y: p.VideoSource.Bounds.Y, Width: p.VideoSource.Bounds.Width, Height: p.VideoSource.Bounds.Height, }, }, VideoEncoderConfiguration: &onvif.VideoEncoderConfiguration{ Token: p.Token + "_encoder", Name: p.Name + " Encoder", Encoding: p.VideoEncoder.Encoding, Resolution: &onvif.VideoResolution{ Width: p.VideoEncoder.Resolution.Width, Height: p.VideoEncoder.Resolution.Height, }, Quality: p.VideoEncoder.Quality, RateControl: &onvif.VideoRateControl{ FrameRateLimit: p.VideoEncoder.Framerate, BitrateLimit: p.VideoEncoder.Bitrate, }, }, } if p.PTZ != nil { profile.PTZConfiguration = &onvif.PTZConfiguration{ Token: p.PTZ.NodeToken, Name: p.Name + " PTZ", NodeToken: p.PTZ.NodeToken, } } return profile }