02f79ea7a7
- Updated comments to improve clarity and adhere to best practices in ascii.go, main.go, and diagnostics. - Removed unnecessary linter directives for improved readability in imaging.go and ptz.go. - Reformatted function signatures and added helper calls in tests for consistency and clarity. - Enhanced error handling and logging consistency in various server files, ensuring better maintainability.
463 lines
14 KiB
Go
463 lines
14 KiB
Go
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/0x524a/onvif-go"
|
|
)
|
|
|
|
const (
|
|
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.
|
|
func DefaultConfig() *Config { //nolint:funlen // DefaultConfig has many statements due to comprehensive default configuration
|
|
return &Config{
|
|
Host: "0.0.0.0",
|
|
Port: 8080, //nolint:mnd // Default HTTP port
|
|
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}, //nolint:mnd // Preset position
|
|
},
|
|
},
|
|
},
|
|
Snapshot: SnapshotConfig{
|
|
Enabled: true,
|
|
Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, //nolint:mnd // Default resolution
|
|
Quality: highQuality, //nolint:mnd // High quality
|
|
},
|
|
},
|
|
{
|
|
Token: "profile_1",
|
|
Name: "Wide Angle Camera",
|
|
VideoSource: VideoSourceConfig{
|
|
Token: "video_source_1",
|
|
Name: "Wide Angle Camera",
|
|
Resolution: Resolution{Width: mediumWidth, Height: mediumHeight}, //nolint:mnd // Medium resolution
|
|
Framerate: defaultFramerate, //nolint:mnd // Default framerate
|
|
Bounds: Bounds{X: 0, Y: 0, Width: mediumWidth, Height: mediumHeight}, //nolint:mnd // Medium bounds
|
|
},
|
|
VideoEncoder: VideoEncoderConfig{
|
|
Encoding: "H264",
|
|
Resolution: Resolution{Width: mediumWidth, Height: mediumHeight}, //nolint:mnd // Medium resolution
|
|
Quality: mediumQuality, //nolint:mnd // Medium quality
|
|
Framerate: defaultFramerate, //nolint:mnd // Default framerate
|
|
Bitrate: mediumBitrate, //nolint:mnd // Medium bitrate
|
|
GovLength: defaultFramerate, //nolint:mnd // Default gov length
|
|
},
|
|
Snapshot: SnapshotConfig{
|
|
Enabled: true,
|
|
Resolution: Resolution{Width: mediumWidth, Height: mediumHeight}, //nolint:mnd // Medium resolution
|
|
Quality: defaultQuality, //nolint:mnd // Default quality
|
|
},
|
|
},
|
|
{
|
|
Token: "profile_2",
|
|
Name: "Telephoto Camera",
|
|
VideoSource: VideoSourceConfig{
|
|
Token: "video_source_2",
|
|
Name: "Telephoto Camera",
|
|
Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, //nolint:mnd // Default resolution
|
|
Framerate: lowFramerate, //nolint:mnd // Low framerate
|
|
Bounds: Bounds{X: 0, Y: 0, Width: defaultWidth, Height: defaultHeight}, //nolint:mnd // Default bounds
|
|
},
|
|
VideoEncoder: VideoEncoderConfig{
|
|
Encoding: "H264",
|
|
Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, //nolint:mnd // Default resolution
|
|
Quality: highQuality, //nolint:mnd // High quality
|
|
Framerate: lowFramerate, //nolint:mnd // Low framerate
|
|
Bitrate: highBitrate, //nolint:mnd // High bitrate
|
|
GovLength: lowFramerate, //nolint:mnd // Low framerate
|
|
},
|
|
PTZ: &PTZConfig{
|
|
NodeToken: "ptz_node_2",
|
|
PanRange: Range{Min: -maxPan, Max: maxPan},
|
|
TiltRange: Range{Min: -maxTilt, Max: maxTilt},
|
|
ZoomRange: Range{Min: 0, Max: maxZoom}, //nolint:mnd // Max zoom
|
|
DefaultSpeed: PTZSpeed{
|
|
Pan: lowPTZSpeed, Tilt: lowPTZSpeed, Zoom: lowPTZSpeed, //nolint:mnd // Low PTZ speed
|
|
},
|
|
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}, //nolint:mnd // Preset zoom
|
|
},
|
|
},
|
|
},
|
|
Snapshot: SnapshotConfig{
|
|
Enabled: true,
|
|
Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, //nolint:mnd // Default resolution
|
|
Quality: highQuality, //nolint:mnd // High quality
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// 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 //nolint:mnd // Standard HTTP port
|
|
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
|
|
}
|