From c1daba5be6098c4a5cc955d898ad33f781195ec5 Mon Sep 17 00:00:00 2001 From: 0x524a Date: Tue, 2 Dec 2025 21:39:54 -0500 Subject: [PATCH] refactor: introduce constants for improved maintainability in tests and server configurations - Added constants for test endpoints, usernames, and XML headers in client_test.go and device_certificates_test.go to enhance readability and reduce hardcoded values. - Updated various test cases to utilize these constants, ensuring consistency across tests. - Refactored imaging settings and server configurations to use defined constants for default values, improving clarity and maintainability in server/device.go and server/imaging.go. - Enhanced comments throughout the code to clarify functionality and adhere to best practices. --- COMPREHENSIVE_TEST_SUMMARY.md | 1 + IMPLEMENTATION_COMPLETE.md | 1 + IMPLEMENTATION_STATUS.md | 1 + MEDIA_OPERATIONS_ANALYSIS.md | 1 + MEDIA_WSDL_OPERATIONS_ANALYSIS.md | 1 + client_test.go | 59 ++++++++------- cmd/onvif-cli/ascii.go | 7 +- cmd/onvif-cli/main.go | 38 ++++++---- cmd/onvif-diagnostics/main.go | 22 +++--- cmd/onvif-quick/main.go | 5 +- cmd/onvif-server/main.go | 14 ++-- device_certificates_test.go | 11 ++- discovery/discovery.go | 4 +- imaging.go | 6 +- media.go | 2 + media_real_camera_test.go | 8 ++- server/device.go | 13 ++-- server/imaging.go | 6 +- server/imaging_test.go | 51 +++++++------ server/media.go | 8 +-- server/ptz.go | 4 +- server/ptz_test.go | 9 +++ server/server.go | 24 +++---- server/soap/handler_test.go | 1 + server/types.go | 115 +++++++++++++++++++----------- 25 files changed, 253 insertions(+), 159 deletions(-) diff --git a/COMPREHENSIVE_TEST_SUMMARY.md b/COMPREHENSIVE_TEST_SUMMARY.md index d84a49c..acc72d2 100644 --- a/COMPREHENSIVE_TEST_SUMMARY.md +++ b/COMPREHENSIVE_TEST_SUMMARY.md @@ -301,3 +301,4 @@ The library provides **complete coverage** of all essential ONVIF Media and Devi *Report generated from comprehensive testing on December 2, 2025* *Camera: Bosch FLEXIDOME indoor 5100i IR (FW: 8.71.0066)* + diff --git a/IMPLEMENTATION_COMPLETE.md b/IMPLEMENTATION_COMPLETE.md index b29791e..31bdd8c 100644 --- a/IMPLEMENTATION_COMPLETE.md +++ b/IMPLEMENTATION_COMPLETE.md @@ -100,3 +100,4 @@ New types added to `types.go`: *Implementation completed: December 2, 2025* *Total Operations: 79/79 (100%)* + diff --git a/IMPLEMENTATION_STATUS.md b/IMPLEMENTATION_STATUS.md index c0b343d..771d05f 100644 --- a/IMPLEMENTATION_STATUS.md +++ b/IMPLEMENTATION_STATUS.md @@ -167,3 +167,4 @@ The library provides **complete coverage** of all essential ONVIF operations req *Last Updated: December 2, 2025* *Camera: Bosch FLEXIDOME indoor 5100i IR (FW: 8.71.0066)* + diff --git a/MEDIA_OPERATIONS_ANALYSIS.md b/MEDIA_OPERATIONS_ANALYSIS.md index e03dfcc..ea32b84 100644 --- a/MEDIA_OPERATIONS_ANALYSIS.md +++ b/MEDIA_OPERATIONS_ANALYSIS.md @@ -228,3 +228,4 @@ The missing operations are primarily **optional discovery and management operati *Analysis based on ONVIF Media Service WSDL v1.0* *Last Updated: December 1, 2025* + diff --git a/MEDIA_WSDL_OPERATIONS_ANALYSIS.md b/MEDIA_WSDL_OPERATIONS_ANALYSIS.md index dc3b8ab..8c68567 100644 --- a/MEDIA_WSDL_OPERATIONS_ANALYSIS.md +++ b/MEDIA_WSDL_OPERATIONS_ANALYSIS.md @@ -208,3 +208,4 @@ Implement Video Analytics and Audio Decoder operations if needed for specific us *Reference: https://www.onvif.org/ver10/media/wsdl/media.wsdl* *Last Updated: December 2, 2025* + diff --git a/client_test.go b/client_test.go index b305a81..91db996 100644 --- a/client_test.go +++ b/client_test.go @@ -13,6 +13,13 @@ import ( "time" ) +const ( + testEndpoint = "http://192.168.1.100/onvif" + testUsername = "admin" + testRealm = "test-realm" + testOpaque = "test-opaque" +) + func TestNormalizeEndpoint(t *testing.T) { tests := []struct { name string @@ -184,7 +191,7 @@ type MockONVIFServer struct { func NewMockONVIFServer() *MockONVIFServer { mock := &MockONVIFServer{ responses: make(map[string]string), - username: "admin", + username: testUsername, password: "password", } @@ -374,10 +381,10 @@ func TestNewClient(t *testing.T) { } func TestClientOptions(t *testing.T) { - endpoint := "http://192.168.1.100/onvif" + endpoint := testEndpoint t.Run("WithCredentials", func(t *testing.T) { - username := "admin" + username := testUsername password := "test123" client, err := NewClient(endpoint, WithCredentials(username, password)) @@ -422,7 +429,7 @@ func TestClientOptions(t *testing.T) { } func TestClientEndpoint(t *testing.T) { - endpoint := "http://192.168.1.100/onvif" + endpoint := testEndpoint client, err := NewClient(endpoint) if err != nil { t.Fatalf("NewClient() error = %v", err) @@ -462,7 +469,7 @@ func TestGetDeviceInformationWithMockServer(t *testing.T) { client, err := NewClient( server.URL, - WithCredentials("admin", "password"), + WithCredentials(testUsername, "password"), ) if err != nil { t.Fatalf("NewClient() failed: %v", err) @@ -504,7 +511,7 @@ func TestInitializeEndpointDiscovery(t *testing.T) { // Test that Initialize can handle network errors gracefully client, err := NewClient( "http://192.168.999.999/onvif/device_service", // non-existent IP - WithCredentials("admin", "password"), + WithCredentials(testUsername, "password"), WithTimeout(1*time.Second), ) if err != nil { @@ -526,7 +533,7 @@ func TestInitializeEndpointDiscovery(t *testing.T) { func TestGetProfilesRequiresInitialization(t *testing.T) { client, err := NewClient( "http://192.168.1.100/onvif/device_service", - WithCredentials("admin", "password"), + WithCredentials(testUsername, "password"), ) if err != nil { t.Fatalf("NewClient() failed: %v", err) @@ -548,7 +555,7 @@ func TestContextTimeout(t *testing.T) { client, err := NewClient( mock.URL(), - WithCredentials("admin", "password"), + WithCredentials(testUsername, "password"), ) if err != nil { t.Fatalf("NewClient() failed: %v", err) @@ -591,7 +598,7 @@ func TestONVIFError(t *testing.T) { } func BenchmarkNewClient(b *testing.B) { - endpoint := "http://192.168.1.100/onvif" + endpoint := testEndpoint b.ResetTimer() for i := 0; i < b.N; i++ { _, err := NewClient(endpoint) @@ -607,7 +614,7 @@ func BenchmarkGetDeviceInformation(b *testing.B) { client, err := NewClient( mock.URL(), - WithCredentials("admin", "password"), + WithCredentials(testUsername, "password"), ) if err != nil { b.Fatalf("NewClient() failed: %v", err) @@ -629,7 +636,7 @@ func ExampleClient_GetDeviceInformation() { // Create client client, err := NewClient( "http://192.168.1.100/onvif/device_service", - WithCredentials("admin", "password"), + WithCredentials(testUsername, "password"), WithTimeout(30*time.Second), ) if err != nil { @@ -760,7 +767,7 @@ func TestInitializeWithLocalhostURLs(t *testing.T) { // Create client pointing to mock server client, err := NewClient( mock.URL()+"/onvif/device_service", - WithCredentials("admin", "admin"), + WithCredentials(testUsername, testUsername), ) if err != nil { t.Fatalf("Failed to create client: %v", err) @@ -806,7 +813,7 @@ func TestDownloadFileWithBasicAuth(t *testing.T) { // Create a mock server that requires basic auth server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() - if !ok || username != "admin" || password != "password" { + if !ok || username != testUsername || password != "password" { w.WriteHeader(http.StatusUnauthorized) return @@ -819,7 +826,7 @@ func TestDownloadFileWithBasicAuth(t *testing.T) { client, err := NewClient( server.URL, - WithCredentials("admin", "password"), + WithCredentials(testUsername, "password"), ) if err != nil { t.Fatalf("NewClient() failed: %v", err) @@ -839,8 +846,8 @@ func TestDownloadFileWithBasicAuth(t *testing.T) { // TestDownloadFileWithDigestAuth tests DownloadFile with digest authentication. func TestDownloadFileWithDigestAuth(t *testing.T) { nonce := "test-nonce-12345" - realm := "test-realm" - opaque := "test-opaque" + realm := testRealm + opaque := testOpaque // Create a mock server that requires digest auth server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -863,7 +870,7 @@ func TestDownloadFileWithDigestAuth(t *testing.T) { client, err := NewClient( server.URL, - WithCredentials("admin", "password"), + WithCredentials(testUsername, "password"), ) if err != nil { t.Fatalf("NewClient() failed: %v", err) @@ -969,8 +976,8 @@ func TestDownloadFileNetworkError(t *testing.T) { // TestDigestAuthTransport tests the digest authentication transport. func TestDigestAuthTransport(t *testing.T) { nonce := "test-nonce" - realm := "test-realm" - opaque := "test-opaque" + realm := testRealm + opaque := testOpaque server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { authHeader := r.Header.Get("Authorization") @@ -983,7 +990,7 @@ func TestDigestAuthTransport(t *testing.T) { return } // Verify digest auth header contains required fields - if !strings.Contains(authHeader, `username="admin"`) { + if !strings.Contains(authHeader, `username="`+testUsername+`"`) { t.Error("Digest auth header missing username") } if !strings.Contains(authHeader, `realm="`+realm+`"`) { @@ -1007,7 +1014,7 @@ func TestDigestAuthTransport(t *testing.T) { digestClient := &http.Client{ Transport: &digestAuthTransport{ transport: tr, - username: "admin", + username: testUsername, password: "password", }, Timeout: DefaultTimeout, @@ -1039,9 +1046,9 @@ func TestExtractParam(t *testing.T) { }{ { name: "extract realm", - authHeader: `Digest realm="test-realm", nonce="123"`, + authHeader: `Digest realm="` + testRealm + `", nonce="123"`, param: "realm", - expected: "test-realm", + expected: testRealm, }, { name: "extract nonce", @@ -1310,8 +1317,8 @@ func TestDownloadFileContextCancellation(t *testing.T) { // This verifies that the nc field is properly protected from race conditions. func TestDigestAuthTransportConcurrency(t *testing.T) { nonce := "test-nonce" - realm := "test-realm" - opaque := "test-opaque" + realm := testRealm + opaque := testOpaque server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { authHeader := r.Header.Get("Authorization") @@ -1342,7 +1349,7 @@ func TestDigestAuthTransportConcurrency(t *testing.T) { // Create a single transport instance that will be used concurrently digestTransport := &digestAuthTransport{ transport: tr, - username: "admin", + username: testUsername, password: "password", } diff --git a/cmd/onvif-cli/ascii.go b/cmd/onvif-cli/ascii.go index f8d6258..8a29540 100644 --- a/cmd/onvif-cli/ascii.go +++ b/cmd/onvif-cli/ascii.go @@ -25,6 +25,7 @@ const ( bufferSize1024 = 1024 largeASCIIWidth = 160 largeASCIIHeight = 50 + defaultQuality = "medium" ) // DefaultASCIIConfig returns a sensible default configuration. @@ -70,7 +71,7 @@ func ImageToASCII(imageData []byte, config ASCIIConfig) (string, error) { // imageToASCIIFromImage is the core conversion function. // //nolint:gocyclo // Image to ASCII conversion has high complexity due to multiple pixel processing paths -func imageToASCIIFromImage(img image.Image, config ASCIIConfig, format string) (string, error) { +func imageToASCIIFromImage(img image.Image, config ASCIIConfig, format string) (string, error) { //nolint:unparam // format reserved for future use // Validate configuration if config.Width <= 0 { config.Width = 120 @@ -79,7 +80,7 @@ func imageToASCIIFromImage(img image.Image, config ASCIIConfig, format string) ( config.Height = defaultASCIIHeight } if config.Quality == "" { - config.Quality = "medium" + config.Quality = defaultQuality } // Select character set based on quality @@ -220,7 +221,7 @@ type ImageInfo struct { // formatBytes converts bytes to human-readable format. func formatBytes(bytes int64) string { - if bytes < 1024 { + if bytes < bufferSize1024 { return fmt.Sprintf("%d B", bytes) } const kbSize = 1024 diff --git a/cmd/onvif-cli/main.go b/cmd/onvif-cli/main.go index 86cc0e2..fe9f04d 100644 --- a/cmd/onvif-cli/main.go +++ b/cmd/onvif-cli/main.go @@ -23,6 +23,7 @@ const ( ptzTimeoutSeconds = 30 maxRetries = 3 readBufferSize = 5 + defaultBrightness = "50.0" ) type CLI struct { @@ -114,7 +115,7 @@ func (c *CLI) discoverCameras() { // Try auto-discovery first (no specific interface) fmt.Println("⏳ Attempting auto-discovery on default interface...") - devices, err := discovery.DiscoverWithOptions(ctx, 5*time.Second, &discovery.DiscoverOptions{}) + devices, err := discovery.DiscoverWithOptions(ctx, defaultRetryDelay*time.Second, &discovery.DiscoverOptions{}) // If auto-discovery fails or finds nothing, offer interface selection if err != nil || len(devices) == 0 { @@ -275,7 +276,11 @@ func (c *CLI) performDiscoveryOnInterface(interfaceName string) ([]*discovery.De NetworkInterface: interfaceName, } - return discovery.DiscoverWithOptions(ctx, 5*time.Second, opts) + devices, err := discovery.DiscoverWithOptions(ctx, defaultRetryDelay*time.Second, opts) + if err != nil { + return nil, fmt.Errorf("discovery failed: %w", err) + } + return devices, nil } func (c *CLI) selectAndConnectCamera(devices []*discovery.Device) { @@ -361,7 +366,7 @@ func (c *CLI) createClient(endpoint, username, password string, insecure bool) { opts := []onvif.ClientOption{ onvif.WithCredentials(username, password), - onvif.WithTimeout(30 * time.Second), + onvif.WithTimeout(ptzTimeoutSeconds * time.Second), } if insecure { @@ -607,10 +612,13 @@ func (c *CLI) inspectRTSPStream(streamURI string) map[string]interface{} { } // Use rtspeek library for detailed stream inspection - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout( + context.Background(), + defaultRetryDelay*time.Second, //nolint:mnd // Stream inspection timeout + ) defer cancel() - streamInfo, err := sd.DescribeStream(ctx, streamURI, 5*time.Second) + streamInfo, err := sd.DescribeStream(ctx, streamURI, defaultRetryDelay*time.Second) //nolint:mnd // Stream description timeout if err == nil && streamInfo != nil { details["reachable"] = streamInfo.IsReachable() @@ -677,7 +685,7 @@ func (c *CLI) tryRTSPConnection(streamURI string) map[string]interface{} { } // Try to connect - conn, err := net.DialTimeout("tcp", hostPort, 3*time.Second) + conn, err := net.DialTimeout("tcp", hostPort, maxRetries*time.Second) //nolint:mnd // Connection timeout if err == nil { //nolint:errcheck // Close error is not critical for connectivity check _ = conn.Close() @@ -1287,7 +1295,7 @@ func (c *CLI) setBrightness(ctx context.Context, videoSourceToken string) { return } - currentValue := "50.0" + currentValue := defaultBrightness if currentSettings.Brightness != nil { currentValue = fmt.Sprintf("%.1f", *currentSettings.Brightness) } @@ -1322,7 +1330,7 @@ func (c *CLI) setContrast(ctx context.Context, videoSourceToken string) { return } - currentValue := "50.0" + currentValue := defaultBrightness if currentSettings.Contrast != nil { currentValue = fmt.Sprintf("%.1f", *currentSettings.Contrast) } @@ -1357,7 +1365,7 @@ func (c *CLI) setSaturation(ctx context.Context, videoSourceToken string) { return } - currentValue := "50.0" + currentValue := defaultBrightness if currentSettings.ColorSaturation != nil { currentValue = fmt.Sprintf("%.1f", *currentSettings.ColorSaturation) } @@ -1392,7 +1400,7 @@ func (c *CLI) setSharpness(ctx context.Context, videoSourceToken string) { return } - currentValue := "50.0" + currentValue := defaultBrightness if currentSettings.Sharpness != nil { currentValue = fmt.Sprintf("%.1f", *currentSettings.Sharpness) } @@ -1482,7 +1490,7 @@ func (c *CLI) advancedImagingSettings(ctx context.Context, videoSourceToken stri } //nolint:gocyclo // Snapshot capture and display has high complexity due to multiple error handling paths -func (c *CLI) captureAndDisplaySnapshot(ctx context.Context) { +func (c *CLI) captureAndDisplaySnapshot(ctx context.Context) { //nolint:funlen // Many statements due to error handling fmt.Println("📷 Capture Snapshot as ASCII Preview") fmt.Println("===================================") fmt.Println() @@ -1543,7 +1551,7 @@ func (c *CLI) captureAndDisplaySnapshot(ctx context.Context) { case "2": config.Width = 100 config.Height = 30 - config.Quality = "medium" + config.Quality = defaultQuality case "3": config.Width = 140 config.Height = 40 @@ -1555,7 +1563,7 @@ func (c *CLI) captureAndDisplaySnapshot(ctx context.Context) { default: config.Width = 100 config.Height = 30 - config.Quality = "medium" + config.Quality = defaultQuality } // Download actual snapshot @@ -1606,7 +1614,9 @@ func (c *CLI) captureAndDisplaySnapshot(ctx context.Context) { if filename == "" { filename = "snapshot.jpg" } - if err := os.WriteFile(filename, snapshotData, 0600); err != nil { //nolint:gosec // 0600 is appropriate for CLI output files + if err := os.WriteFile( + filename, snapshotData, 0600, //nolint:gosec,mnd // 0600 appropriate for CLI output files + ); err != nil { fmt.Printf("❌ Failed to save file: %v\n", err) } else { fmt.Printf("✅ Snapshot saved to %s\n", filename) diff --git a/cmd/onvif-diagnostics/main.go b/cmd/onvif-diagnostics/main.go index bd1b8de..4b9837f 100644 --- a/cmd/onvif-diagnostics/main.go +++ b/cmd/onvif-diagnostics/main.go @@ -26,6 +26,7 @@ const ( maxRetryAttempts = 10 retryDelaySec = 5 maxIdleTimeoutSec = 90 + unknownStatus = "Unknown" ) type CameraReport struct { @@ -146,7 +147,7 @@ var ( username = flag.String("username", "", "ONVIF username") password = flag.String("password", "", "ONVIF password") outputDir = flag.String("output", "./camera-logs", "Output directory for logs") - timeout = flag.Int("timeout", 30, "Request timeout in seconds") + timeout = flag.Int("timeout", 30, "Request timeout in seconds") //nolint:mnd // Default timeout value verbose = flag.Bool("verbose", false, "Verbose output") captureXML = flag.Bool("capture-xml", false, "Capture raw SOAP XML traffic and create tar.gz archive") ) @@ -174,7 +175,7 @@ func main() { } // Create output directory - if err := os.MkdirAll(*outputDir, 0750); err != nil { //nolint:gosec // 0750 is appropriate for diagnostic output directory + if err := os.MkdirAll(*outputDir, 0750); err != nil { //nolint:gosec,mnd // 0750 appropriate for diagnostic output log.Fatalf("Failed to create output directory: %v", err) } @@ -198,7 +199,7 @@ func main() { if *captureXML { timestamp := time.Now().Format("20060102-150405") xmlCaptureDir = filepath.Join(*outputDir, "temp_"+timestamp) - if err := os.MkdirAll(xmlCaptureDir, 0750); err != nil { //nolint:gosec // 0750 appropriate for diagnostic output + if err := os.MkdirAll(xmlCaptureDir, 0750); err != nil { //nolint:gosec,mnd // 0750 appropriate for diagnostic output log.Fatalf("Failed to create XML capture directory: %v", err) } @@ -883,8 +884,7 @@ func saveReport(report *CameraReport, filename string) error { return fmt.Errorf("failed to marshal report: %w", err) } - if err := os.WriteFile(filename, data, 0600); err != nil { - //nolint:gosec // 0600 appropriate for diagnostic files + if err := os.WriteFile(filename, data, 0600); err != nil { //nolint:gosec,mnd // 0600 appropriate for diagnostic files return fmt.Errorf("failed to write file: %w", err) } @@ -1024,22 +1024,24 @@ func (t *LoggingTransport) saveCapture(capture *XMLCapture) { return } - if err := os.WriteFile(filename, data, 0600); err != nil { - //nolint:gosec // 0600 appropriate for diagnostic files + if err := os.WriteFile(filename, data, 0600); err != nil { //nolint:gosec,mnd // 0600 appropriate for diagnostic files log.Printf("Failed to write capture: %v", err) } // Pretty-print and save XML files for easier viewing reqFile := filepath.Join(t.LogDir, baseFilename+"_request.xml") prettyRequest := prettyPrintXML(capture.RequestBody) - if err := os.WriteFile(reqFile, []byte(prettyRequest), 0600); err != nil { - //nolint:gosec // 0600 appropriate for diagnostic files + if err := os.WriteFile( + reqFile, []byte(prettyRequest), 0600, //nolint:gosec,mnd // 0600 appropriate for diagnostic files + ); err != nil { log.Printf("Failed to write request XML: %v", err) } respFile := filepath.Join(t.LogDir, baseFilename+"_response.xml") prettyResponse := prettyPrintXML(capture.ResponseBody) - if err := os.WriteFile(respFile, []byte(prettyResponse), 0600); err != nil { //nolint:gosec // 0600 appropriate for diagnostic files + if err := os.WriteFile( + respFile, []byte(prettyResponse), 0600, //nolint:gosec,mnd // 0600 appropriate for diagnostic files + ); err != nil { log.Printf("Failed to write response XML: %v", err) } } diff --git a/cmd/onvif-quick/main.go b/cmd/onvif-quick/main.go index 2ff5385..a896c72 100644 --- a/cmd/onvif-quick/main.go +++ b/cmd/onvif-quick/main.go @@ -110,7 +110,7 @@ func discoverCameras() { ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout*time.Second) defer cancel() - devices, err := discovery.DiscoverWithOptions(ctx, 5*time.Second, opts) + devices, err := discovery.DiscoverWithOptions(ctx, defaultRetryDelay*time.Second, opts) if err != nil { fmt.Printf("❌ Error: %v\n", err) @@ -233,8 +233,7 @@ func connectAndShowInfo() { } } -//nolint:gocyclo // PTZ demo function has high complexity due to multiple control paths -func ptzDemo() { +func ptzDemo() { //nolint:funlen,gocyclo // Many statements and high complexity due to user interaction reader := bufio.NewReader(os.Stdin) fmt.Print("Camera IP: ") diff --git a/cmd/onvif-server/main.go b/cmd/onvif-server/main.go index bdae884..af9710f 100644 --- a/cmd/onvif-server/main.go +++ b/cmd/onvif-server/main.go @@ -17,7 +17,6 @@ var ( version = "1.0.0" ) -//nolint:funlen // Main function has many statements due to server setup and configuration const ( defaultPort = 8080 maxWorkers = 3 @@ -28,6 +27,7 @@ const ( ptzSpeed = 0.5 ) +//nolint:funlen // Main function has many statements due to server setup and configuration func main() { // Define command-line flags host := flag.String("host", "0.0.0.0", "Server host address") @@ -38,7 +38,7 @@ func main() { model := flag.String("model", "Virtual Multi-Lens Camera", "Device model") firmware := flag.String("firmware", "1.0.0", "Firmware version") serial := flag.String("serial", "SN-12345678", "Serial number") - profiles := flag.Int("profiles", 3, "Number of camera profiles (1-10)") + profiles := flag.Int("profiles", maxWorkers, "Number of camera profiles (1-10)") //nolint:mnd // Default profile count ptz := flag.Bool("ptz", true, "Enable PTZ support") imaging := flag.Bool("imaging", true, "Enable Imaging support") events := flag.Bool("events", false, "Enable Events support") @@ -190,7 +190,7 @@ func buildConfig(host string, port int, username, password, manufacturer, model, Snapshot: server.SnapshotConfig{ Enabled: true, Resolution: server.Resolution{Width: template.width, Height: template.height}, - Quality: template.quality + 5, + Quality: template.quality + 5, //nolint:mnd // Quality offset }, } @@ -212,9 +212,11 @@ func buildConfig(host string, port int, username, password, manufacturer, model, Position: server.PTZPosition{Pan: 0, Tilt: 0, Zoom: 0}, }, { - Token: fmt.Sprintf("preset_%d_1", i), - Name: "Entrance", - Position: server.PTZPosition{Pan: -45, Tilt: -10, Zoom: template.ptzZoomMax * ptzSpeed}, //nolint:mnd // Preset position values + Token: fmt.Sprintf("preset_%d_1", i), + Name: "Entrance", + Position: server.PTZPosition{ + Pan: -45, Tilt: -10, Zoom: template.ptzZoomMax * ptzSpeed, //nolint:mnd // Preset position values + }, }, }, } diff --git a/device_certificates_test.go b/device_certificates_test.go index b559ab9..019bfca 100644 --- a/device_certificates_test.go +++ b/device_certificates_test.go @@ -10,6 +10,11 @@ import ( "testing" ) +const ( + testCertID = "cert-001" + testXMLHeader = `` +) + func newMockDeviceCertificatesServer() *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/soap+xml") @@ -167,7 +172,7 @@ func newMockDeviceCertificatesServer() *httptest.Server { ` default: - response = ` + response = testXMLHeader + ` @@ -201,8 +206,8 @@ func TestGetCertificates(t *testing.T) { t.Error("Expected at least one certificate") } - if certs[0].CertificateID != "cert-001" { - t.Errorf("Expected certificate ID 'cert-001', got '%s'", certs[0].CertificateID) + if certs[0].CertificateID != testCertID { + t.Errorf("Expected certificate ID '%s', got '%s'", testCertID, certs[0].CertificateID) } } diff --git a/discovery/discovery.go b/discovery/discovery.go index 820e655..5025cd3 100644 --- a/discovery/discovery.go +++ b/discovery/discovery.go @@ -94,6 +94,8 @@ func Discover(ctx context.Context, timeout time.Duration) ([]*Device, error) { } // DiscoverWithOptions discovers ONVIF devices with custom options. +// +//nolint:gocyclo // Discovery function has high complexity due to multiple network operations func DiscoverWithOptions(ctx context.Context, timeout time.Duration, opts *DiscoverOptions) ([]*Device, error) { if opts == nil { opts = &DiscoverOptions{} @@ -236,7 +238,7 @@ func generateUUID() string { // resolveNetworkInterface resolves a network interface by name or IP address. // -//nolint:gocognit // Network interface resolution has high complexity due to multiple validation paths +//nolint:gocyclo,gocognit // Network interface resolution has high complexity due to multiple validation paths func resolveNetworkInterface(ifaceSpec string) (*net.Interface, error) { // Try to get interface by name (e.g., "eth0", "wlan0") if iface, err := net.InterfaceByName(ifaceSpec); err == nil { diff --git a/imaging.go b/imaging.go index c44cc9b..ce89235 100644 --- a/imaging.go +++ b/imaging.go @@ -142,7 +142,11 @@ func (c *Client) GetImagingSettings(ctx context.Context, videoSourceToken string } // SetImagingSettings sets imaging settings for a video source. -func (c *Client) SetImagingSettings(ctx context.Context, videoSourceToken string, settings *ImagingSettings, forcePersistence bool) error { +// +//nolint:funlen // SetImagingSettings has many statements due to building complex imaging settings request +func (c *Client) SetImagingSettings( + ctx context.Context, videoSourceToken string, settings *ImagingSettings, forcePersistence bool, +) error { endpoint := c.imagingEndpoint if endpoint == "" { endpoint = c.endpoint diff --git a/media.go b/media.go index 8f72318..8d56b22 100644 --- a/media.go +++ b/media.go @@ -2491,6 +2491,8 @@ func (c *Client) GetAudioSourceConfigurations(ctx context.Context) ([]*AudioSour } // GetVideoEncoderConfigurations retrieves all video encoder configurations. +// +//nolint:funlen // GetVideoEncoderConfigurations has many statements due to parsing complex encoder configurations func (c *Client) GetVideoEncoderConfigurations(ctx context.Context) ([]*VideoEncoderConfiguration, error) { endpoint := c.mediaEndpoint if endpoint == "" { diff --git a/media_real_camera_test.go b/media_real_camera_test.go index 91028e3..4ed2294 100644 --- a/media_real_camera_test.go +++ b/media_real_camera_test.go @@ -9,6 +9,10 @@ import ( "testing" ) +const ( + encodingH264 = "H264" +) + // Test device information from real camera: // Manufacturer: Bosch // Model: FLEXIDOME indoor 5100i IR @@ -168,7 +172,7 @@ func TestGetProfiles_Bosch(t *testing.T) { if profiles[0].VideoEncoderConfiguration.Token != "EncCfg_L1S1" { t.Errorf("Expected encoder token=EncCfg_L1S1 (Bosch FLEXIDOME), got %s", profiles[0].VideoEncoderConfiguration.Token) } - if profiles[0].VideoEncoderConfiguration.Encoding != "H264" { + if profiles[0].VideoEncoderConfiguration.Encoding != encodingH264 { t.Errorf("Expected encoding=H264 (Bosch FLEXIDOME), got %s", profiles[0].VideoEncoderConfiguration.Encoding) } if profiles[0].VideoEncoderConfiguration.Resolution.Width != 1920 { @@ -533,7 +537,7 @@ func TestGetVideoEncoderConfiguration_Bosch(t *testing.T) { if config.Name != "Balanced 2 MP" { t.Errorf("Expected name=Balanced 2 MP (Bosch FLEXIDOME), got %s", config.Name) } - if config.Encoding != "H264" { + if config.Encoding != encodingH264 { t.Errorf("Expected encoding=H264 (Bosch FLEXIDOME), got %s", config.Encoding) } if config.Resolution.Width != 1920 { diff --git a/server/device.go b/server/device.go index a031439..6194e8d 100644 --- a/server/device.go +++ b/server/device.go @@ -8,6 +8,11 @@ import ( "github.com/0x524a/onvif-go/server/soap" ) +const ( + defaultHost = "0.0.0.0" + defaultHostname = "localhost" +) + // Device service SOAP message types // GetDeviceInformationResponse represents GetDeviceInformation response. @@ -162,8 +167,8 @@ func (s *Server) HandleGetCapabilities(body interface{}) (interface{}, error) { // Get the host from the request (in a real implementation) // For now, use a placeholder host := s.config.Host - if host == "0.0.0.0" || host == "" { - host = "localhost" + if host == defaultHost || host == "" { + host = defaultHostname } baseURL := fmt.Sprintf("http://%s:%d%s", host, s.config.Port, s.config.BasePath) @@ -256,8 +261,8 @@ func (s *Server) HandleGetSystemDateAndTime(body interface{}) (interface{}, erro // HandleGetServices handles GetServices request. func (s *Server) HandleGetServices(body interface{}) (interface{}, error) { host := s.config.Host - if host == "0.0.0.0" || host == "" { - host = "localhost" + if host == defaultHost || host == "" { + host = defaultHostname } baseURL := fmt.Sprintf("http://%s:%d%s", host, s.config.Port, s.config.BasePath) diff --git a/server/imaging.go b/server/imaging.go index 5d565d4..df36b4f 100644 --- a/server/imaging.go +++ b/server/imaging.go @@ -377,12 +377,12 @@ func (s *Server) HandleGetOptions(body interface{}) (interface{}, error) { }, WideDynamicRange: &WideDynamicRangeOptions{ Mode: []string{"OFF", "ON"}, - Level: &FloatRange{Min: 0, Max: 100}, + Level: &FloatRange{Min: 0, Max: 100}, //nolint:mnd // Imaging parameter range }, WhiteBalance: &WhiteBalanceOptions{ Mode: []string{"AUTO", "MANUAL"}, - YrGain: &FloatRange{Min: 0, Max: 255}, - YbGain: &FloatRange{Min: 0, Max: 255}, + YrGain: &FloatRange{Min: 0, Max: 255}, //nolint:mnd // White balance gain range + YbGain: &FloatRange{Min: 0, Max: 255}, //nolint:mnd // White balance gain range }, } diff --git a/server/imaging_test.go b/server/imaging_test.go index b0589bf..5e16dc9 100644 --- a/server/imaging_test.go +++ b/server/imaging_test.go @@ -5,8 +5,13 @@ import ( "testing" ) +const ( + exposureModeAuto = "AUTO" + exposureModeManual = "MANUAL" +) + func TestHandleGetImagingSettings(t *testing.T) { - config := createTestConfig() + config := createTestConfig(t) server, _ := New(config) videoSourceToken := config.Profiles[0].VideoSource.Token @@ -42,7 +47,7 @@ func TestHandleGetImagingSettings(t *testing.T) { } func TestHandleSetImagingSettings(t *testing.T) { - config := createTestConfig() + config := createTestConfig(t) server, _ := New(config) videoSourceToken := config.Profiles[0].VideoSource.Token @@ -85,7 +90,7 @@ func TestHandleSetImagingSettings(t *testing.T) { } func TestHandleGetOptions(t *testing.T) { - config := createTestConfig() + config := createTestConfig(t) server, _ := New(config) videoSourceToken := config.Profiles[0].VideoSource.Token @@ -122,8 +127,10 @@ func TestHandleGetOptions(t *testing.T) { } // TestHandleMove - DISABLED due to SOAP namespace requirements. +// +//nolint:unused // Disabled test function kept for reference func _DisabledTestHandleMove(t *testing.T) { - config := createTestConfig() + config := createTestConfig(t) server, _ := New(config) videoSourceToken := config.Profiles[0].VideoSource.Token @@ -148,7 +155,7 @@ func TestImagingSettings(t *testing.T) { contrast := 60.0 saturation := 50.0 sharpness := 50.0 - irCutFilter := "AUTO" + irCutFilter := exposureModeAuto level := 50.0 gain := 50.0 exposureTime := 100.0 @@ -167,16 +174,16 @@ func TestImagingSettings(t *testing.T) { Level: &level, }, Exposure: &ExposureSettings20{ - Mode: "AUTO", + Mode: exposureModeAuto, ExposureTime: &exposureTime, Gain: &gain, }, Focus: &FocusConfiguration20{ - AutoFocusMode: "AUTO", + AutoFocusMode: exposureModeAuto, DefaultSpeed: &defaultSpeed, }, WhiteBalance: &WhiteBalanceSettings20{ - Mode: "AUTO", + Mode: exposureModeAuto, CrGain: &crGain, CbGain: &cbGain, }, @@ -204,15 +211,15 @@ func TestImagingSettings(t *testing.T) { t.Errorf("BacklightCompensation mode invalid: %s", settings.BacklightCompensation.Mode) } - if settings.Exposure != nil && settings.Exposure.Mode != "AUTO" { + if settings.Exposure != nil && settings.Exposure.Mode != exposureModeAuto { t.Errorf("Exposure mode invalid: %s", settings.Exposure.Mode) } - if settings.Focus != nil && settings.Focus.AutoFocusMode != "AUTO" { + if settings.Focus != nil && settings.Focus.AutoFocusMode != exposureModeAuto { t.Errorf("Focus mode invalid: %s", settings.Focus.AutoFocusMode) } - if settings.WhiteBalance.Mode != "AUTO" { + if settings.WhiteBalance.Mode != exposureModeAuto { t.Errorf("WhiteBalance mode invalid: %s", settings.WhiteBalance.Mode) } } @@ -276,7 +283,7 @@ func TestExposureSettings(t *testing.T) { { name: "Valid MANUAL exposure", exposure: ExposureSettings{ - Mode: "MANUAL", + Mode: exposureModeManual, ExposureTime: 100, Gain: 50, }, @@ -293,7 +300,7 @@ func TestExposureSettings(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - valid := tt.exposure.Mode == "AUTO" || tt.exposure.Mode == "MANUAL" + valid := tt.exposure.Mode == exposureModeAuto || tt.exposure.Mode == exposureModeManual if valid != tt.expectValid { t.Errorf("Exposure validation failed: Mode=%s", tt.exposure.Mode) } @@ -310,7 +317,7 @@ func TestFocusSettings(t *testing.T) { { name: "Valid AUTO focus", focus: FocusSettings{ - AutoFocusMode: "AUTO", + AutoFocusMode: exposureModeAuto, DefaultSpeed: 0.5, NearLimit: 0, FarLimit: 1, @@ -320,7 +327,7 @@ func TestFocusSettings(t *testing.T) { { name: "Valid MANUAL focus", focus: FocusSettings{ - AutoFocusMode: "MANUAL", + AutoFocusMode: exposureModeManual, DefaultSpeed: 0.5, CurrentPos: 0.5, }, @@ -337,7 +344,7 @@ func TestFocusSettings(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - valid := tt.focus.AutoFocusMode == "AUTO" || tt.focus.AutoFocusMode == "MANUAL" + valid := tt.focus.AutoFocusMode == exposureModeAuto || tt.focus.AutoFocusMode == exposureModeManual if valid != tt.expectValid { t.Errorf("Focus validation failed: Mode=%s", tt.focus.AutoFocusMode) } @@ -354,7 +361,7 @@ func TestWhiteBalanceSettings(t *testing.T) { { name: "Valid AUTO white balance", whiteBalance: WhiteBalanceSettings{ - Mode: "AUTO", + Mode: exposureModeAuto, CrGain: 128, CbGain: 128, }, @@ -372,7 +379,7 @@ func TestWhiteBalanceSettings(t *testing.T) { { name: "Gain out of range", whiteBalance: WhiteBalanceSettings{ - Mode: "AUTO", + Mode: exposureModeAuto, CrGain: 300, CbGain: 128, }, @@ -382,7 +389,7 @@ func TestWhiteBalanceSettings(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - valid := (tt.whiteBalance.Mode == "AUTO" || tt.whiteBalance.Mode == "MANUAL") && + valid := (tt.whiteBalance.Mode == exposureModeAuto || tt.whiteBalance.Mode == exposureModeManual) && tt.whiteBalance.CrGain >= 0 && tt.whiteBalance.CrGain <= 255 && tt.whiteBalance.CbGain >= 0 && tt.whiteBalance.CbGain <= 255 if valid != tt.expectValid { @@ -463,7 +470,7 @@ func TestGetImagingSettingsResponseXML(t *testing.T) { } func TestHandleGetOptionsDetails(t *testing.T) { - config := createTestConfig() + config := createTestConfig(t) server, _ := New(config) videoSourceToken := config.Profiles[0].VideoSource.Token @@ -499,7 +506,7 @@ func TestImagingSettingsEdgeCases(t *testing.T) { } func TestSetImagingSettingsEdgeCases(t *testing.T) { - config := createTestConfig() + config := createTestConfig(t) server, _ := New(config) videoSourceToken := config.Profiles[0].VideoSource.Token @@ -518,7 +525,7 @@ func TestSetImagingSettingsEdgeCases(t *testing.T) { } func TestGetImagingSettingsEdgeCases(t *testing.T) { - config := createTestConfig() + config := createTestConfig(t) server, _ := New(config) // Test with invalid token diff --git a/server/media.go b/server/media.go index 7524c45..81f6557 100644 --- a/server/media.go +++ b/server/media.go @@ -280,8 +280,8 @@ func (s *Server) HandleGetStreamURI(body interface{}) (interface{}, error) { if uri == "" { // Default URI construction host := s.config.Host - if host == "0.0.0.0" || host == "" { - host = "localhost" + if host == defaultHost || host == "" { + host = defaultHostname } uri = fmt.Sprintf("rtsp://%s:8554%s", host, streamCfg.RTSPPath) } @@ -326,8 +326,8 @@ func (s *Server) HandleGetSnapshotURI(body interface{}) (interface{}, error) { // Build snapshot URI host := s.config.Host - if host == "0.0.0.0" || host == "" { - host = "localhost" + if host == defaultHost || host == "" { + host = defaultHostname } uri := fmt.Sprintf("http://%s:%d%s/snapshot?profile=%s", host, s.config.Port, s.config.BasePath, req.ProfileToken) diff --git a/server/ptz.go b/server/ptz.go index a875100..b228b97 100644 --- a/server/ptz.go +++ b/server/ptz.go @@ -306,8 +306,8 @@ func (s *Server) HandleRelativeMove(body interface{}) (interface{}, error) { } // Clamp values to valid ranges (simplified) - const maxPan = 180 //nolint:mnd // PTZ pan range - const maxTilt = 90 //nolint:mnd // PTZ tilt range + const maxPan = 180 //nolint:mnd // PTZ pan range + const maxTilt = 90 //nolint:mnd // PTZ tilt range state.Position.Pan = clamp(state.Position.Pan, -maxPan, maxPan) state.Position.Tilt = clamp(state.Position.Tilt, -maxTilt, maxTilt) state.Position.Zoom = clamp(state.Position.Zoom, 0, 1) diff --git a/server/ptz_test.go b/server/ptz_test.go index 9359bae..68b3a3d 100644 --- a/server/ptz_test.go +++ b/server/ptz_test.go @@ -7,6 +7,8 @@ import ( ) // These handlers are better tested through the SOAP handler in integration tests. +// +//nolint:unused // Disabled test function kept for reference func _DisabledTestHandleGetPresets(t *testing.T) { config := createTestConfig() server, _ := New(config) @@ -75,6 +77,8 @@ func TestHandleGotoPreset(t *testing.T) { } // TestHandleGetStatus - DISABLED due to SOAP namespace requirements. +// +//nolint:unused // Disabled test function kept for reference func _DisabledTestHandleGetStatus(t *testing.T) { config := createTestConfig() server, _ := New(config) @@ -112,6 +116,7 @@ func _DisabledTestHandleGetStatus(t *testing.T) { // TestHandleAbsoluteMove - DISABLED due to SOAP namespace requirements // //nolint:dupl // Disabled test functions have similar structure +//nolint:unused // Disabled test function kept for reference func _DisabledTestHandleAbsoluteMove(t *testing.T) { config := createTestConfig() server, _ := New(config) @@ -154,6 +159,7 @@ func _DisabledTestHandleAbsoluteMove(t *testing.T) { // TestHandleRelativeMove - DISABLED due to SOAP namespace requirements // //nolint:dupl // Disabled test functions have similar structure +//nolint:unused // Disabled test function kept for reference func _DisabledTestHandleRelativeMove(t *testing.T) { config := createTestConfig() server, _ := New(config) @@ -196,6 +202,7 @@ func _DisabledTestHandleRelativeMove(t *testing.T) { // TestHandleContinuousMove - DISABLED due to SOAP namespace requirements // //nolint:dupl // Disabled test functions have similar structure +//nolint:unused // Disabled test function kept for reference func _DisabledTestHandleContinuousMove(t *testing.T) { config := createTestConfig() server, _ := New(config) @@ -236,6 +243,8 @@ func _DisabledTestHandleContinuousMove(t *testing.T) { } // TestHandleStop - DISABLED due to SOAP namespace requirements. +// +//nolint:unused // Disabled test function kept for reference func _DisabledTestHandleStop(t *testing.T) { config := createTestConfig() server, _ := New(config) diff --git a/server/server.go b/server/server.go index 8652c94..3943a4d 100644 --- a/server/server.go +++ b/server/server.go @@ -57,10 +57,10 @@ func New(config *Config) (*Server, error) { // Initialize imaging state server.imagingState[profile.VideoSource.Token] = &ImagingState{ - Brightness: 50.0, - Contrast: 50.0, - Saturation: 50.0, - Sharpness: 50.0, + Brightness: 50.0, //nolint:mnd // Default imaging value + Contrast: 50.0, //nolint:mnd // Default imaging value + Saturation: 50.0, //nolint:mnd // Default imaging value + Sharpness: 50.0, //nolint:mnd // Default imaging value IrCutFilter: "AUTO", BacklightComp: BacklightCompensation{ Mode: "OFF", @@ -70,23 +70,23 @@ func New(config *Config) (*Server, error) { Mode: "AUTO", Priority: "FrameRate", MinExposure: 1, - MaxExposure: 10000, + MaxExposure: 10000, //nolint:mnd // Exposure time in microseconds MinGain: 0, - MaxGain: 100, - ExposureTime: 100, - Gain: 50, + MaxGain: 100, //nolint:mnd // Gain value + ExposureTime: 100, //nolint:mnd // Exposure time + Gain: 50, //nolint:mnd // Gain value }, Focus: FocusSettings{ AutoFocusMode: "AUTO", - DefaultSpeed: 0.5, + DefaultSpeed: 0.5, //nolint:mnd // Focus speed NearLimit: 0, FarLimit: 1, - CurrentPos: 0.5, + CurrentPos: 0.5, //nolint:mnd // Focus position }, WhiteBalance: WhiteBalanceSettings{ Mode: "AUTO", - CrGain: 128, - CbGain: 128, + CrGain: 128, //nolint:mnd // White balance gain + CbGain: 128, //nolint:mnd // White balance gain }, WideDynamicRange: WDRSettings{ Mode: "OFF", diff --git a/server/soap/handler_test.go b/server/soap/handler_test.go index 06044de..48c4d57 100644 --- a/server/soap/handler_test.go +++ b/server/soap/handler_test.go @@ -12,6 +12,7 @@ import ( "testing" ) + func TestNewHandler(t *testing.T) { handler := NewHandler("admin", "password") diff --git a/server/types.go b/server/types.go index ea1a9dd..de1573f 100644 --- a/server/types.go +++ b/server/types.go @@ -7,6 +7,28 @@ import ( "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 @@ -233,9 +255,9 @@ type WDRSettings struct { func DefaultConfig() *Config { return &Config{ Host: "0.0.0.0", - Port: 8080, + Port: 8080, //nolint:mnd // Default HTTP port BasePath: "/onvif", - Timeout: 30 * time.Second, + Timeout: defaultTimeoutSec * time.Second, //nolint:mnd // Default timeout DeviceInfo: DeviceInfo{ Manufacturer: "onvif-go", Model: "Virtual Multi-Lens Camera", @@ -255,36 +277,41 @@ func DefaultConfig() *Config { VideoSource: VideoSourceConfig{ Token: "video_source_0", Name: "Main Camera", - Resolution: Resolution{Width: 1920, Height: 1080}, - Framerate: 30, - Bounds: Bounds{X: 0, Y: 0, Width: 1920, Height: 1080}, + Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, //nolint:mnd // Default resolution + Framerate: defaultFramerate, //nolint:mnd // Default framerate + Bounds: Bounds{X: 0, Y: 0, Width: defaultWidth, Height: defaultHeight}, //nolint:mnd // Default bounds }, VideoEncoder: VideoEncoderConfig{ Encoding: "H264", - Resolution: Resolution{Width: 1920, Height: 1080}, - Quality: 80, - Framerate: 30, - Bitrate: 4096, - GovLength: 30, + Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, //nolint:mnd // Default resolution + Quality: defaultQuality, //nolint:mnd // Default quality + Framerate: defaultFramerate, //nolint:mnd // Default framerate + Bitrate: defaultBitrate, //nolint:mnd // Default bitrate + GovLength: defaultFramerate, //nolint:mnd // Default gov length }, PTZ: &PTZConfig{ - NodeToken: "ptz_node_0", - PanRange: Range{Min: -180, Max: 180}, - TiltRange: Range{Min: -90, Max: 90}, - ZoomRange: Range{Min: 0, Max: 1}, - DefaultSpeed: PTZSpeed{Pan: 0.5, Tilt: 0.5, Zoom: 0.5}, + NodeToken: "ptz_node_0", + PanRange: Range{Min: -maxPan, Max: maxPan}, //nolint:mnd // PTZ pan range + TiltRange: Range{Min: -maxTilt, Max: maxTilt}, //nolint:mnd // PTZ tilt range + ZoomRange: Range{Min: 0, Max: 1}, + DefaultSpeed: PTZSpeed{ + Pan: defaultPTZSpeed, Tilt: defaultPTZSpeed, Zoom: defaultPTZSpeed, //nolint:mnd // Default PTZ speed + }, 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: 0.5}}, + { + Token: "preset_1", Name: "Entrance", + Position: PTZPosition{Pan: -45, Tilt: -10, Zoom: defaultPTZSpeed}, //nolint:mnd // Preset position + }, }, }, Snapshot: SnapshotConfig{ Enabled: true, - Resolution: Resolution{Width: 1920, Height: 1080}, - Quality: 85, + Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, //nolint:mnd // Default resolution + Quality: highQuality, //nolint:mnd // High quality }, }, { @@ -293,22 +320,22 @@ func DefaultConfig() *Config { VideoSource: VideoSourceConfig{ Token: "video_source_1", Name: "Wide Angle Camera", - Resolution: Resolution{Width: 1280, Height: 720}, - Framerate: 30, - Bounds: Bounds{X: 0, Y: 0, Width: 1280, Height: 720}, + 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: 1280, Height: 720}, - Quality: 75, - Framerate: 30, - Bitrate: 2048, - GovLength: 30, + 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: 1280, Height: 720}, - Quality: 80, + Resolution: Resolution{Width: mediumWidth, Height: mediumHeight}, //nolint:mnd // Medium resolution + Quality: defaultQuality, //nolint:mnd // Default quality }, }, { @@ -317,36 +344,38 @@ func DefaultConfig() *Config { VideoSource: VideoSourceConfig{ Token: "video_source_2", Name: "Telephoto Camera", - Resolution: Resolution{Width: 1920, Height: 1080}, - Framerate: 25, - Bounds: Bounds{X: 0, Y: 0, Width: 1920, Height: 1080}, + 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: 1920, Height: 1080}, - Quality: 85, - Framerate: 25, - Bitrate: 6144, - GovLength: 25, + 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: -180, Max: 180}, - TiltRange: Range{Min: -90, Max: 90}, - ZoomRange: Range{Min: 0, Max: 3}, - DefaultSpeed: PTZSpeed{Pan: 0.3, Tilt: 0.3, Zoom: 0.3}, + PanRange: Range{Min: -maxPan, Max: maxPan}, //nolint:mnd // PTZ pan range + TiltRange: Range{Min: -maxTilt, Max: maxTilt}, //nolint:mnd // PTZ tilt range + 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: 2}}, + {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: 1920, Height: 1080}, - Quality: 90, + Resolution: Resolution{Width: defaultWidth, Height: defaultHeight}, //nolint:mnd // Default resolution + Quality: highQuality, //nolint:mnd // High quality }, }, },