Refactor build process and improve documentation
- Enhanced Makefile to support building for multiple platforms with versioning and improved output. - Added build-release.sh script for local binary releases, including checksum generation and archive creation. - Updated error handling and comments in the build process for clarity. - Ensured all binaries are built with versioning information included.
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Release Binaries
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# Linux
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
- goos: linux
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
|
||||
# Windows
|
||||
- goos: windows
|
||||
goarch: amd64
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
|
||||
# macOS
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
|
||||
- name: Get version
|
||||
id: version
|
||||
run: |
|
||||
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build binaries
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
CGO_ENABLED: 0
|
||||
run: |
|
||||
VERSION=${{ steps.version.outputs.VERSION }}
|
||||
LDFLAGS="-s -w -X main.Version=${VERSION} -X main.Commit=${{ steps.version.outputs.SHORT_SHA }}"
|
||||
|
||||
# Set file extension for Windows
|
||||
EXT=""
|
||||
if [ "${{ matrix.goos }}" = "windows" ]; then
|
||||
EXT=".exe"
|
||||
fi
|
||||
|
||||
# Build all CLI tools
|
||||
mkdir -p dist
|
||||
|
||||
echo "Building onvif-cli..."
|
||||
go build -ldflags="${LDFLAGS}" -o "dist/onvif-cli-${{ matrix.goos }}-${{ matrix.goarch }}${EXT}" ./cmd/onvif-cli
|
||||
|
||||
echo "Building onvif-quick..."
|
||||
go build -ldflags="${LDFLAGS}" -o "dist/onvif-quick-${{ matrix.goos }}-${{ matrix.goarch }}${EXT}" ./cmd/onvif-quick
|
||||
|
||||
echo "Building onvif-server..."
|
||||
go build -ldflags="${LDFLAGS}" -o "dist/onvif-server-${{ matrix.goos }}-${{ matrix.goarch }}${EXT}" ./cmd/onvif-server
|
||||
|
||||
echo "Building onvif-diagnostics..."
|
||||
go build -ldflags="${LDFLAGS}" -o "dist/onvif-diagnostics-${{ matrix.goos }}-${{ matrix.goarch }}${EXT}" ./cmd/onvif-diagnostics
|
||||
|
||||
- name: Create archive
|
||||
run: |
|
||||
VERSION=${{ steps.version.outputs.VERSION }}
|
||||
PLATFORM="${{ matrix.goos }}-${{ matrix.goarch }}"
|
||||
ARCHIVE_NAME="go-onvif-${VERSION}-${PLATFORM}"
|
||||
|
||||
mkdir -p releases
|
||||
|
||||
if [ "${{ matrix.goos }}" = "windows" ]; then
|
||||
# Create ZIP for Windows
|
||||
cd dist
|
||||
zip -j "../releases/${ARCHIVE_NAME}.zip" *-${{ matrix.goos }}-${{ matrix.goarch }}.exe ../README.md ../LICENSE
|
||||
cd ..
|
||||
else
|
||||
# Create tar.gz for Unix-like systems
|
||||
cd dist
|
||||
tar czf "../releases/${ARCHIVE_NAME}.tar.gz" *-${{ matrix.goos }}-${{ matrix.goarch }} -C .. README.md LICENSE
|
||||
cd ..
|
||||
fi
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
cd releases
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum * > checksums-${{ matrix.goos }}-${{ matrix.goarch }}.txt
|
||||
else
|
||||
shasum -a 256 * > checksums-${{ matrix.goos }}-${{ matrix.goarch }}.txt
|
||||
fi
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: releases/*
|
||||
retention-days: 5
|
||||
|
||||
release:
|
||||
name: Create GitHub Release
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: all-releases
|
||||
pattern: release-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Generate combined checksums
|
||||
run: |
|
||||
cd all-releases
|
||||
# Combine all checksum files
|
||||
cat checksums-*.txt > checksums.txt
|
||||
# Remove individual checksum files
|
||||
rm checksums-*.txt
|
||||
|
||||
- name: Get version and changelog
|
||||
id: version
|
||||
run: |
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
|
||||
|
||||
# Generate changelog from commits since last tag
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
||||
if [ -n "$PREV_TAG" ]; then
|
||||
echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
|
||||
git log --pretty=format:"- %s (%h)" ${PREV_TAG}..HEAD >> $GITHUB_OUTPUT
|
||||
echo "" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: all-releases/*
|
||||
draft: false
|
||||
prerelease: ${{ contains(github.ref, '-rc') || contains(github.ref, '-beta') || contains(github.ref, '-alpha') }}
|
||||
generate_release_notes: true
|
||||
body: |
|
||||
## Release ${{ steps.version.outputs.VERSION }}
|
||||
|
||||
### Installation
|
||||
|
||||
Download the appropriate binary for your platform below.
|
||||
|
||||
#### Linux/macOS
|
||||
```bash
|
||||
# Download and extract
|
||||
wget https://github.com/${{ github.repository }}/releases/download/${{ steps.version.outputs.VERSION }}/go-onvif-${{ steps.version.outputs.VERSION }}-linux-amd64.tar.gz
|
||||
tar xzf go-onvif-${{ steps.version.outputs.VERSION }}-linux-amd64.tar.gz
|
||||
|
||||
# Make executable and move to PATH
|
||||
chmod +x onvif-cli-linux-amd64
|
||||
sudo mv onvif-cli-linux-amd64 /usr/local/bin/onvif-cli
|
||||
```
|
||||
|
||||
#### Windows
|
||||
Download the `.zip` file for your architecture and extract it.
|
||||
|
||||
#### Go Library
|
||||
```bash
|
||||
go get github.com/${{ github.repository }}@${{ steps.version.outputs.VERSION }}
|
||||
```
|
||||
|
||||
### Checksums
|
||||
|
||||
SHA256 checksums are available in `checksums.txt`
|
||||
|
||||
### Changes
|
||||
|
||||
${{ steps.version.outputs.CHANGELOG }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
docker:
|
||||
name: Build and Push Docker Image
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
continue-on-error: true
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get version
|
||||
id: version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}:latest
|
||||
ghcr.io/${{ github.repository }}:${{ steps.version.outputs.VERSION }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
# Building and Releasing go-onvif
|
||||
|
||||
This document describes how to build binaries for multiple platforms and create releases.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Build for Your Current Platform
|
||||
|
||||
```bash
|
||||
make build-cli
|
||||
```
|
||||
|
||||
This builds all CLI tools for your current OS/architecture in the `bin/` directory.
|
||||
|
||||
### Build for All Platforms
|
||||
|
||||
```bash
|
||||
make build-all
|
||||
```
|
||||
|
||||
This creates binaries for:
|
||||
- **Linux**: amd64, arm64, arm (32-bit)
|
||||
- **Windows**: amd64, arm64
|
||||
- **macOS**: amd64 (Intel), arm64 (Apple Silicon)
|
||||
|
||||
Binaries are output to `bin/` directory.
|
||||
|
||||
### Create Release Archives
|
||||
|
||||
```bash
|
||||
make release
|
||||
```
|
||||
|
||||
This:
|
||||
1. Builds for all platforms
|
||||
2. Creates `.tar.gz` archives (Linux/macOS) and `.zip` files (Windows)
|
||||
3. Generates SHA256 checksums
|
||||
4. Places everything in `releases/` directory
|
||||
|
||||
## Manual Building
|
||||
|
||||
### Using the Build Script
|
||||
|
||||
```bash
|
||||
# Build with automatic version detection
|
||||
./build-release.sh
|
||||
|
||||
# Build with specific version
|
||||
./build-release.sh v1.0.1
|
||||
```
|
||||
|
||||
### Using Go Directly
|
||||
|
||||
```bash
|
||||
# Set platform and architecture
|
||||
export GOOS=linux
|
||||
export GOARCH=amd64
|
||||
|
||||
# Build a specific tool
|
||||
go build -o bin/onvif-cli-linux-amd64 ./cmd/onvif-cli
|
||||
```
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
| OS | Architecture | Binary Suffix | Notes |
|
||||
|---------|-------------|------------------------|----------------------------|
|
||||
| Linux | amd64 | `linux-amd64` | 64-bit Intel/AMD |
|
||||
| Linux | arm64 | `linux-arm64` | 64-bit ARM (Raspberry Pi 4)|
|
||||
| Linux | arm | `linux-arm` | 32-bit ARM (Raspberry Pi 3)|
|
||||
| Windows | amd64 | `windows-amd64.exe` | 64-bit Windows |
|
||||
| Windows | arm64 | `windows-arm64.exe` | ARM Windows (Surface Pro X)|
|
||||
| macOS | amd64 | `darwin-amd64` | Intel Macs |
|
||||
| macOS | arm64 | `darwin-arm64` | Apple Silicon (M1/M2/M3) |
|
||||
|
||||
## CLI Tools
|
||||
|
||||
The following binaries are built:
|
||||
|
||||
1. **onvif-cli** - Comprehensive ONVIF client with full feature set
|
||||
2. **onvif-quick** - Quick tool for common operations
|
||||
3. **onvif-server** - ONVIF mock server for testing
|
||||
4. **onvif-diagnostics** - Diagnostic and debugging tools
|
||||
|
||||
## Automated Releases via GitHub Actions
|
||||
|
||||
Releases are automatically created when you push a tag:
|
||||
|
||||
```bash
|
||||
# Create and push a new version tag
|
||||
git tag -a v1.0.1 -m "Release version 1.0.1"
|
||||
git push origin v1.0.1
|
||||
```
|
||||
|
||||
The GitHub Actions workflow will:
|
||||
1. Build binaries for all platforms
|
||||
2. Create release archives
|
||||
3. Generate checksums
|
||||
4. Create a GitHub release with all artifacts
|
||||
5. Build and push Docker images (multi-arch)
|
||||
|
||||
### Release Workflow Features
|
||||
|
||||
- ✅ Builds for 7 platform/architecture combinations
|
||||
- ✅ Creates compressed archives (`.tar.gz` and `.zip`)
|
||||
- ✅ Generates SHA256 checksums for verification
|
||||
- ✅ Auto-generates release notes from commits
|
||||
- ✅ Supports pre-releases (tags with `-rc`, `-beta`, `-alpha`)
|
||||
- ✅ Builds multi-architecture Docker images
|
||||
- ✅ Pushes to GitHub Container Registry
|
||||
|
||||
## Docker Images
|
||||
|
||||
Docker images are automatically built for:
|
||||
- `linux/amd64`
|
||||
- `linux/arm64`
|
||||
- `linux/arm/v7`
|
||||
|
||||
Available at:
|
||||
```
|
||||
ghcr.io/0x524a/go-onvif:latest
|
||||
ghcr.io/0x524a/go-onvif:v1.0.0
|
||||
```
|
||||
|
||||
## Manual GitHub Release
|
||||
|
||||
If you prefer to create releases manually:
|
||||
|
||||
```bash
|
||||
# Build release archives
|
||||
make release
|
||||
|
||||
# Create GitHub release using gh CLI
|
||||
gh release create v1.0.1 releases/* \
|
||||
--title "Release v1.0.1" \
|
||||
--notes "Release notes here"
|
||||
```
|
||||
|
||||
## Version Numbering
|
||||
|
||||
Follow [Semantic Versioning](https://semver.org/):
|
||||
|
||||
- `v1.0.0` - Major release (breaking changes)
|
||||
- `v1.1.0` - Minor release (new features, backward compatible)
|
||||
- `v1.1.1` - Patch release (bug fixes)
|
||||
- `v1.0.0-rc1` - Release candidate
|
||||
- `v1.0.0-beta1` - Beta release
|
||||
- `v1.0.0-alpha1` - Alpha release
|
||||
|
||||
## Build Flags
|
||||
|
||||
The build process uses the following flags:
|
||||
|
||||
```bash
|
||||
-ldflags="-s -w -X main.Version=<version> -X main.Commit=<sha>"
|
||||
```
|
||||
|
||||
- `-s` - Omit symbol table (smaller binary)
|
||||
- `-w` - Omit DWARF debug info (smaller binary)
|
||||
- `-X main.Version` - Inject version string
|
||||
- `-X main.Commit` - Inject git commit SHA
|
||||
|
||||
## Size Optimization
|
||||
|
||||
Binaries are built with `CGO_ENABLED=0` and stripped flags, resulting in:
|
||||
- Smaller binary sizes
|
||||
- No external dependencies
|
||||
- Portable across systems
|
||||
|
||||
Typical sizes:
|
||||
- onvif-cli: ~10-15 MB
|
||||
- onvif-quick: ~8-12 MB
|
||||
- onvif-server: ~10-14 MB
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build Fails for Specific Platform
|
||||
|
||||
Some platforms may not be supported by all dependencies. Check:
|
||||
```bash
|
||||
go tool dist list # List all supported platforms
|
||||
```
|
||||
|
||||
### Large Binary Sizes
|
||||
|
||||
Ensure you're using the build flags:
|
||||
```bash
|
||||
go build -ldflags="-s -w" -o binary ./cmd/tool
|
||||
```
|
||||
|
||||
### Missing Dependencies
|
||||
|
||||
```bash
|
||||
go mod download
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
## Distribution
|
||||
|
||||
Once built, binaries can be distributed via:
|
||||
|
||||
1. **GitHub Releases** (automatic)
|
||||
2. **Package managers** (homebrew, apt, etc.)
|
||||
3. **Container registries** (Docker Hub, GHCR)
|
||||
4. **Direct download** from your server
|
||||
|
||||
## Verification
|
||||
|
||||
Users can verify downloads using checksums:
|
||||
|
||||
```bash
|
||||
# Download binary and checksum
|
||||
wget https://github.com/0x524A/go-onvif/releases/download/v1.0.0/go-onvif-v1.0.0-linux-amd64.tar.gz
|
||||
wget https://github.com/0x524A/go-onvif/releases/download/v1.0.0/checksums.txt
|
||||
|
||||
# Verify
|
||||
sha256sum -c checksums.txt --ignore-missing
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
After building:
|
||||
1. Test binaries on target platforms
|
||||
2. Update CHANGELOG.md with release notes
|
||||
3. Create GitHub release
|
||||
4. Announce on relevant channels
|
||||
5. Update documentation with new features
|
||||
@@ -1,706 +0,0 @@
|
||||
# ONVIF Camera Analysis Report
|
||||
|
||||
Generated: November 7, 2025
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Analysis of 5 ONVIF-compliant cameras from 3 manufacturers (REOLINK, AXIS, Bosch) reveals diverse implementations and capabilities. All cameras successfully responded to ONVIF commands with varying feature sets.
|
||||
|
||||
---
|
||||
|
||||
## Camera Inventory
|
||||
|
||||
### 1. REOLINK E1 Zoom
|
||||
- **Firmware**: v3.1.0.2649_23083101
|
||||
- **Serial**: 192168261
|
||||
- **IP**: 192.168.2.61:8000
|
||||
- **Type**: PTZ Indoor Camera
|
||||
- **Key Features**: PTZ support, dual stream, basic imaging
|
||||
|
||||
### 2. AXIS Q3819-PVE
|
||||
- **Firmware**: 10.12.153
|
||||
- **Serial**: B8A44F9DC7ED
|
||||
- **IP**: 192.168.2.190
|
||||
- **Type**: Panoramic Fixed Dome
|
||||
- **Key Features**: Ultra-wide 8192x1728 resolution, analytics, advanced imaging
|
||||
|
||||
### 3. AXIS P3818-PVE
|
||||
- **Firmware**: 11.9.60
|
||||
- **Serial**: B8A44FA04F26
|
||||
- **IP**: 192.168.2.82
|
||||
- **Type**: Panoramic Fixed Dome
|
||||
- **Key Features**: 5120x2560 resolution, analytics, dual encoding (H264/JPEG)
|
||||
|
||||
### 4. Bosch FLEXIDOME panoramic 5100i
|
||||
- **Firmware**: 9.00.0210
|
||||
- **Serial**: 404705923918060213
|
||||
- **IP**: 192.168.2.24
|
||||
- **Type**: 360° Panoramic Dome
|
||||
- **Key Features**: 16 profiles, dewarping, circular image (2112x2112)
|
||||
|
||||
### 5. Bosch FLEXIDOME IP starlight 8000i
|
||||
- **Firmware**: 7.70.0126
|
||||
- **Serial**: 044518807925140011
|
||||
- **IP**: 192.168.2.200
|
||||
- **Type**: Fixed Dome with Low-Light Performance
|
||||
- **Key Features**: Starlight imaging, I/O connectors, relay output
|
||||
|
||||
---
|
||||
|
||||
## Comparative Analysis
|
||||
|
||||
### Resolution Capabilities
|
||||
|
||||
| Camera | Max Resolution | Aspect Ratio | Primary Use Case |
|
||||
|--------|---------------|--------------|------------------|
|
||||
| REOLINK E1 Zoom | 2048x1536 | 4:3 | Standard surveillance |
|
||||
| AXIS Q3819-PVE | 8192x1728 | ~4.7:1 | 180° panoramic |
|
||||
| AXIS P3818-PVE | 5120x2560 | 2:1 | 180° panoramic |
|
||||
| Bosch panoramic 5100i | 2112x2112 | 1:1 | 360° fisheye |
|
||||
| Bosch starlight 8000i | 1536x864 | 16:9 | Low-light environments |
|
||||
|
||||
### Profile Count
|
||||
|
||||
| Camera | Total Profiles | Video Profiles | Notes |
|
||||
|--------|----------------|----------------|-------|
|
||||
| REOLINK E1 Zoom | 2 | 2 | MainStream + SubStream |
|
||||
| AXIS Q3819-PVE | 2 | 2 | H264 + JPEG |
|
||||
| AXIS P3818-PVE | 2 | 2 | H264 + JPEG |
|
||||
| Bosch panoramic 5100i | 16 | 9 valid | Includes metadata/audio profiles |
|
||||
| Bosch starlight 8000i | 3 | 3 | 2x H264 + 1x JPEG |
|
||||
|
||||
### ONVIF Service Support
|
||||
|
||||
| Service | REOLINK | AXIS Q3819 | AXIS P3818 | Bosch Panoramic | Bosch Starlight |
|
||||
|---------|---------|------------|------------|-----------------|-----------------|
|
||||
| Device | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Media | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Imaging | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Events | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Analytics | ✗ | ✓ | ✓ | ✓ | ✗ |
|
||||
| PTZ | ✓ | ✗ | ✗ | ✓ | ✗ |
|
||||
|
||||
### Video Encoding
|
||||
|
||||
| Camera | H264 | JPEG | MPEG4 | Notes |
|
||||
|--------|------|------|-------|-------|
|
||||
| REOLINK | ✓ | ✗ | ✗ | H264 only |
|
||||
| AXIS Q3819 | ✓ | ✓ | ✗ | Dual encoding |
|
||||
| AXIS P3818 | ✓ | ✓ | ✗ | Dual encoding |
|
||||
| Bosch Panoramic | ✓ | ✗ | ✗ | H264 only |
|
||||
| Bosch Starlight | ✓ | ✓ | ✗ | Dual encoding |
|
||||
|
||||
### Network Capabilities
|
||||
|
||||
| Feature | REOLINK | AXIS Q3819 | AXIS P3818 | Bosch Panoramic | Bosch Starlight |
|
||||
|---------|---------|------------|------------|-----------------|-----------------|
|
||||
| RTP Multicast | ✗ | ✓ | ✓ | ✓ | ✓ |
|
||||
| RTP/TCP | ✓ | ✓ | ✓ | ✗ | ✗ |
|
||||
| RTP/RTSP/TCP | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| IPv6 Support | ✗ | ✓ | ✓ | ✗ | ✗ |
|
||||
| TLS 1.2 | ✗ | ✓ | ✓ | ✓ | ✓ |
|
||||
|
||||
### Imaging Features
|
||||
|
||||
| Feature | REOLINK | AXIS Q3819 | AXIS P3818 | Bosch Panoramic | Bosch Starlight |
|
||||
|---------|---------|------------|------------|-----------------|-----------------|
|
||||
| Brightness Control | ✓ (128) | ✓ (50) | ✓ (50) | ✓ (127) | ✓ (128) |
|
||||
| Saturation Control | ✓ (128) | ✓ (50) | ✓ (50) | ✓ (127) | ✓ (128) |
|
||||
| Contrast Control | ✓ (128) | ✓ (50) | ✓ (50) | ✓ (127) | ✓ (128) |
|
||||
| Sharpness Control | ✓ (128) | ✓ (50) | ✓ (50) | ✗ | ✗ |
|
||||
| IrCutFilter | AUTO | AUTO | AUTO | ✗ | ✗ |
|
||||
| WDR | ✗ | ON | ON | ✗ | ✗ |
|
||||
| WhiteBalance | ✗ | AUTO | AUTO | ✗ | ✗ |
|
||||
| Exposure Control | ✗ | AUTO | AUTO | ✗ | ✗ |
|
||||
|
||||
### I/O and Security
|
||||
|
||||
| Feature | REOLINK | AXIS Q3819 | AXIS P3818 | Bosch Panoramic | Bosch Starlight |
|
||||
|---------|---------|------------|------------|-----------------|-----------------|
|
||||
| Input Connectors | 0 | 2 | 2 | 0 | 2 |
|
||||
| Relay Outputs | 0 | 0 | 0 | 0 | 1 |
|
||||
| IP Filter | ✗ | ✓ | ✓ | ✗ | ✗ |
|
||||
| TLS 1.1 | ✗ | ✓ | ✓ | ✗ | ✓ |
|
||||
| TLS 1.2 | ✗ | ✓ | ✓ | ✓ | ✓ |
|
||||
|
||||
---
|
||||
|
||||
## Manufacturer-Specific Findings
|
||||
|
||||
### REOLINK
|
||||
- **Strengths**:
|
||||
- Simple, straightforward ONVIF implementation
|
||||
- PTZ support with status reporting
|
||||
- Good value camera with basic features
|
||||
- **Limitations**:
|
||||
- Limited imaging controls (no WDR, exposure, focus)
|
||||
- Only H264 encoding (no JPEG profile)
|
||||
- No analytics support
|
||||
- Lower security features (no TLS)
|
||||
- **RTSP Pattern**: `rtsp://IP:554/` (main), `rtsp://IP:554/h264Preview_01_sub` (sub)
|
||||
- **Snapshot Pattern**: `http://IP:80/cgi-bin/api.cgi?cmd=onvifSnapPic&channel=0`
|
||||
|
||||
### AXIS
|
||||
- **Strengths**:
|
||||
- Excellent ONVIF compliance and feature richness
|
||||
- Ultra-high resolution panoramic cameras
|
||||
- Advanced imaging with WDR, exposure control, white balance
|
||||
- Strong security (TLS 1.1/1.2, IP filtering, access policy)
|
||||
- Analytics and rule-based event support
|
||||
- **Consistent Implementation**:
|
||||
- Both cameras share similar ONVIF structure
|
||||
- Dual H264/JPEG encoding profiles
|
||||
- Same URL patterns and capabilities
|
||||
- **RTSP Pattern**: `rtsp://IP/onvif-media/media.amp?profile=X&sessiontimeout=60&streamtype=unicast`
|
||||
- **Snapshot Pattern**: `http://IP/onvif-cgi/jpg/image.cgi?resolution=WxH&compression=30`
|
||||
- **Notable**: Q3819 has wider aspect ratio (8192x1728 vs 5120x2560)
|
||||
|
||||
### Bosch
|
||||
- **Strengths**:
|
||||
- Specialized cameras with unique features
|
||||
- Panoramic 5100i has comprehensive dewarping profiles
|
||||
- Starlight 8000i optimized for low-light
|
||||
- Good I/O options (starlight model has relay output)
|
||||
- **Quirks**:
|
||||
- Panoramic model has 16 profiles (many without video encoders)
|
||||
- Some profiles return "IncompleteConfiguration" errors
|
||||
- Less standardized RTSP URLs (tunnel-based)
|
||||
- **RTSP Pattern**: `rtsp://IP/rtsp_tunnel?p=X&line=Y&inst=Z` (various parameters)
|
||||
- **Snapshot Pattern**: `http://IP/snap.jpg?JpegCam=X`
|
||||
- **Notable**:
|
||||
- Panoramic uses circular (2112x2112) and dewarped (3072x1728) views
|
||||
- 3 profiles failed GetStreamURI with incomplete configuration
|
||||
|
||||
---
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
### Response Times (Average)
|
||||
|
||||
| Operation | REOLINK | AXIS Q3819 | AXIS P3818 | Bosch Panoramic | Bosch Starlight |
|
||||
|-----------|---------|------------|------------|-----------------|-----------------|
|
||||
| DeviceInfo | 117.7ms | 5.0ms | 4.9ms | 8.5ms | 7.9ms |
|
||||
| Capabilities | 85.6ms | 72.7ms | 69.3ms | 21.9ms | 27.1ms |
|
||||
| GetProfiles | 832.1ms | 70.9ms | 8.0ms | 706.2ms | 258.3ms |
|
||||
| GetStreamURI | ~129ms avg | ~20ms avg | ~4ms avg | ~11ms avg | ~10ms avg |
|
||||
| GetSnapshot | ~170ms avg | ~20ms avg | ~4ms avg | ~11ms avg | ~6ms avg |
|
||||
| Imaging | 111.8ms | 55.8ms | 67.2ms | 57.3ms | 14.8ms |
|
||||
|
||||
**Key Observations**:
|
||||
- AXIS cameras have fastest response times overall
|
||||
- REOLINK has higher latency (likely due to port 8000, may be proxy/gateway)
|
||||
- Bosch cameras have moderate, consistent response times
|
||||
- GetProfiles is slowest operation for most cameras
|
||||
|
||||
### Error Analysis
|
||||
|
||||
| Camera | Total Errors | Error Types |
|
||||
|--------|--------------|-------------|
|
||||
| REOLINK E1 Zoom | 0 | None |
|
||||
| AXIS Q3819-PVE | 0 | None |
|
||||
| AXIS P3818-PVE | 0 | None |
|
||||
| Bosch panoramic 5100i | 3 | GetStreamURI: IncompleteConfiguration (profiles 9,10,11) |
|
||||
| Bosch starlight 8000i | 0 | None |
|
||||
|
||||
**Bosch Panoramic Errors**: Profiles 9, 10, 11 have no VideoEncoderConfiguration, causing legitimate failures. These appear to be metadata-only or incomplete profiles.
|
||||
|
||||
---
|
||||
|
||||
## Stream URI Patterns
|
||||
|
||||
### REOLINK Pattern
|
||||
```
|
||||
rtsp://192.168.2.61:554/ # MainStream
|
||||
rtsp://192.168.2.61:554/h264Preview_01_sub # SubStream
|
||||
```
|
||||
|
||||
### AXIS Pattern
|
||||
```
|
||||
rtsp://IP/onvif-media/media.amp?profile=profile_1_h264&sessiontimeout=60&streamtype=unicast
|
||||
rtsp://IP/onvif-media/media.amp?profile=profile_1_jpeg&sessiontimeout=60&streamtype=unicast
|
||||
```
|
||||
|
||||
### Bosch Patterns
|
||||
|
||||
**Indoor 5100i IR** (from previous report):
|
||||
```
|
||||
rtsp://IP/rtsp_tunnel?p=0&line=1&inst=1&vcd=2
|
||||
```
|
||||
|
||||
**Panoramic 5100i**:
|
||||
```
|
||||
rtsp://192.168.2.24/rtsp_tunnel?p=0&line=3&inst=4 # E_PTZ view
|
||||
rtsp://192.168.2.24/rtsp_tunnel?p=1&line=2&inst=1 # Dewarped view
|
||||
rtsp://192.168.2.24/rtsp_tunnel?p=2&line=1&inst=4 # Full circle
|
||||
rtsp://192.168.2.24/rtsp_tunnel?von=0&aon=1&aud=1 # Audio only
|
||||
rtsp://192.168.2.24/rtsp_tunnel?von=0&vcd=2&line=1 # Metadata
|
||||
```
|
||||
|
||||
**Starlight 8000i**:
|
||||
```
|
||||
rtsp://192.168.2.200/rtsp_tunnel?p=0&h26x=4&vcd=2
|
||||
rtsp://192.168.2.200/rtsp_tunnel?p=1&inst=2&h26x=4
|
||||
rtsp://192.168.2.200/rtsp_tunnel?h26x=0 # JPEG
|
||||
```
|
||||
|
||||
**Parameter Meanings**:
|
||||
- `p`: Profile index
|
||||
- `line`: Video line/source (1=full, 2=dewarped, 3=ePTZ)
|
||||
- `inst`: Instance number
|
||||
- `vcd`: Video codec (2=metadata)
|
||||
- `h26x`: H.26x codec (0=JPEG, 4=H264)
|
||||
- `von`: Video on/off
|
||||
- `aon`: Audio on/off
|
||||
|
||||
---
|
||||
|
||||
## PTZ Capabilities
|
||||
|
||||
### REOLINK E1 Zoom (PTZ Enabled)
|
||||
- **PTZ Service**: http://192.168.2.61:8000/onvif/ptz_service
|
||||
- **Status**: Both profiles report IDLE for PanTilt and Zoom
|
||||
- **Presets**: 0 configured
|
||||
- **Configuration**: PTZ config present but with empty position spaces
|
||||
- **Notes**: PTZ capability exists but requires further testing for movement commands
|
||||
|
||||
### Bosch Panoramic 5100i (ePTZ)
|
||||
- **PTZ Service**: http://192.168.2.24/onvif/ptz_service
|
||||
- **Type**: Electronic PTZ (digital zoom/pan on panoramic image)
|
||||
- **Profile**: Dedicated ePTZ profile (token "0", 1920x1080)
|
||||
- **Notes**: Digital PTZ on dewarped 360° image, not mechanical movement
|
||||
|
||||
### Other Cameras
|
||||
- AXIS Q3819-PVE, P3818-PVE, Bosch starlight 8000i: No PTZ support
|
||||
|
||||
---
|
||||
|
||||
## Snapshot URI Patterns
|
||||
|
||||
| Manufacturer | Pattern | Authentication Required |
|
||||
|--------------|---------|------------------------|
|
||||
| REOLINK | `http://IP:80/cgi-bin/api.cgi?cmd=onvifSnapPic&channel=0` | Yes |
|
||||
| AXIS | `http://IP/onvif-cgi/jpg/image.cgi?resolution=WxH&compression=30` | Yes |
|
||||
| Bosch | `http://IP/snap.jpg?JpegCam=N` | Yes |
|
||||
|
||||
**InvalidAfterConnect/Reboot**:
|
||||
- REOLINK: InvalidAfterConnect=true, InvalidAfterReboot=true
|
||||
- AXIS: All false (persistent URIs)
|
||||
- Bosch: InvalidAfterReboot=true
|
||||
|
||||
---
|
||||
|
||||
## Bitrate and Frame Rate Analysis
|
||||
|
||||
### REOLINK E1 Zoom
|
||||
- **MainStream**: 1024 kbps @ 15fps (2048x1536)
|
||||
- **SubStream**: 512 kbps @ 15fps (640x480)
|
||||
- **Quality**: 0 (main), 2 (sub)
|
||||
|
||||
### AXIS Q3819-PVE
|
||||
- **H264**: Max bitrate @ 30fps (8192x1728)
|
||||
- **JPEG**: Max bitrate @ 30fps (8192x1728)
|
||||
- **Quality**: 70 for both
|
||||
- **Bitrate Limit**: 2147483647 (max int32 = unlimited)
|
||||
|
||||
### AXIS P3818-PVE
|
||||
- **H264**: Max bitrate @ 30fps (1920x960)
|
||||
- **JPEG**: Max bitrate @ 30fps (5120x2560)
|
||||
- **Quality**: 70 for both
|
||||
- **Bitrate Limit**: 2147483647 (unlimited)
|
||||
|
||||
### Bosch Panoramic 5100i
|
||||
- **Highest**: 13000 kbps @ 30fps (3072x1728 dewarped)
|
||||
- **Lowest**: 400 kbps @ 30fps (512x288)
|
||||
- **Standard**: 5200 kbps @ 30fps (1920x1080)
|
||||
- **Quality**: 50 across all profiles
|
||||
|
||||
### Bosch Starlight 8000i
|
||||
- **H264**: 1400 kbps @ 30fps (1536x864)
|
||||
- **JPEG**: 6000 kbps @ 1fps (1536x864)
|
||||
- **Quality**: 50 (H264), 70 (JPEG)
|
||||
|
||||
---
|
||||
|
||||
## Testing Recommendations
|
||||
|
||||
### Priority 1: Create Camera-Specific Tests
|
||||
|
||||
Each manufacturer has distinct patterns worthy of dedicated test files:
|
||||
|
||||
1. **reolink_e1_zoom_test.go**
|
||||
- Test PTZ status retrieval
|
||||
- Verify dual-stream profiles
|
||||
- Test CGI-based snapshot URLs
|
||||
- Validate 15fps frame rate limits
|
||||
|
||||
2. **axis_q3819_test.go**
|
||||
- Test ultra-wide resolution (8192x1728)
|
||||
- Verify analytics service
|
||||
- Test dual H264/JPEG encoding
|
||||
- Validate WDR and exposure settings
|
||||
- Test multicast support
|
||||
|
||||
3. **axis_p3818_test.go**
|
||||
- Test 5120x2560 panoramic resolution
|
||||
- Similar to Q3819 but different aspect ratio
|
||||
- Benchmark performance differences
|
||||
|
||||
4. **bosch_panoramic_5100i_test.go**
|
||||
- Test circular (2112x2112) image profiles
|
||||
- Test dewarped profiles
|
||||
- Handle IncompleteConfiguration errors gracefully
|
||||
- Test metadata and audio-only profiles
|
||||
- Test 16 different profiles
|
||||
|
||||
5. **bosch_starlight_8000i_test.go**
|
||||
- Test low-light imaging capabilities
|
||||
- Test I/O connectors (2 inputs, 1 relay output)
|
||||
- Test JPEG motion (1fps) vs H264 (30fps)
|
||||
|
||||
### Priority 2: Cross-Manufacturer Tests
|
||||
|
||||
Create tests that verify common ONVIF compliance:
|
||||
|
||||
1. **stream_uri_compatibility_test.go**
|
||||
- Parse and validate different RTSP URL formats
|
||||
- Test RTSP connection to each pattern
|
||||
- Verify authentication handling
|
||||
|
||||
2. **imaging_settings_test.go**
|
||||
- Test brightness/contrast/saturation ranges
|
||||
- Test optional features (WDR, exposure, white balance)
|
||||
- Verify manufacturer-specific defaults
|
||||
|
||||
3. **profile_enumeration_test.go**
|
||||
- Test handling of 2-16 profiles
|
||||
- Verify profile names and tokens
|
||||
- Test resolution validation
|
||||
|
||||
### Priority 3: Edge Case Tests
|
||||
|
||||
1. **incomplete_profile_handling_test.go**
|
||||
- Test cameras with profiles lacking video encoders
|
||||
- Verify graceful error handling for IncompleteConfiguration
|
||||
- Test metadata-only and audio-only profiles
|
||||
|
||||
2. **performance_benchmark_test.go**
|
||||
- Benchmark GetProfiles (100ms to 800ms variation)
|
||||
- Test response time consistency
|
||||
- Measure concurrent request handling
|
||||
|
||||
---
|
||||
|
||||
## Code Patterns for Tests
|
||||
|
||||
### Example: Testing AXIS Cameras
|
||||
|
||||
```go
|
||||
func TestAXISQ3819PVE_UltraWideResolution(t *testing.T) {
|
||||
skipIfNoCamera(t)
|
||||
|
||||
client := createTestClient(t)
|
||||
profiles, err := client.GetProfiles()
|
||||
require.NoError(t, err)
|
||||
|
||||
// AXIS Q3819 should have H264 and JPEG profiles
|
||||
assert.Equal(t, 2, len(profiles))
|
||||
|
||||
// Find H264 profile
|
||||
var h264Profile *onvif.Profile
|
||||
for _, p := range profiles {
|
||||
if p.VideoEncoderConfiguration != nil &&
|
||||
p.VideoEncoderConfiguration.Encoding == "H264" {
|
||||
h264Profile = &p
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
require.NotNil(t, h264Profile, "H264 profile should exist")
|
||||
|
||||
// Verify ultra-wide resolution
|
||||
assert.Equal(t, 8192, h264Profile.VideoEncoderConfiguration.Resolution.Width)
|
||||
assert.Equal(t, 1728, h264Profile.VideoEncoderConfiguration.Resolution.Height)
|
||||
|
||||
// Verify 30fps
|
||||
assert.Equal(t, 30, h264Profile.VideoEncoderConfiguration.RateControl.FrameRateLimit)
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Testing Bosch Panoramic Profiles
|
||||
|
||||
```go
|
||||
func TestBoschPanoramic5100i_MultipleProfiles(t *testing.T) {
|
||||
skipIfNoCamera(t)
|
||||
|
||||
client := createTestClient(t)
|
||||
profiles, err := client.GetProfiles()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should have 16 profiles
|
||||
assert.Equal(t, 16, len(profiles))
|
||||
|
||||
// Count profiles with valid video encoders
|
||||
validVideoProfiles := 0
|
||||
for _, p := range profiles {
|
||||
if p.VideoEncoderConfiguration != nil {
|
||||
validVideoProfiles++
|
||||
}
|
||||
}
|
||||
|
||||
assert.Equal(t, 9, validVideoProfiles, "Should have 9 video profiles")
|
||||
|
||||
// Test that incomplete profiles fail gracefully
|
||||
for _, p := range profiles {
|
||||
uri, err := client.GetStreamURI(p.Token, "RTP-Unicast")
|
||||
|
||||
if p.VideoEncoderConfiguration != nil {
|
||||
// Valid profiles should succeed
|
||||
if err != nil {
|
||||
t.Logf("Profile %s failed: %v", p.Token, err)
|
||||
}
|
||||
} else {
|
||||
// Incomplete profiles should fail
|
||||
assert.Error(t, err, "Profile %s should fail (no video encoder)", p.Token)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Testing PTZ Status
|
||||
|
||||
```go
|
||||
func TestREOLINKE1Zoom_PTZStatus(t *testing.T) {
|
||||
skipIfNoCamera(t)
|
||||
|
||||
client := createTestClient(t)
|
||||
profiles, err := client.GetProfiles()
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, profile := range profiles {
|
||||
if profile.PTZConfiguration != nil {
|
||||
status, err := client.GetPTZStatus(profile.Token)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should report IDLE when not moving
|
||||
assert.NotNil(t, status.MoveStatus)
|
||||
assert.Contains(t, []string{"IDLE", "MOVING"}, status.MoveStatus.PanTilt)
|
||||
assert.Contains(t, []string{"IDLE", "MOVING"}, status.MoveStatus.Zoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Test Suite Structure
|
||||
|
||||
```
|
||||
tests/
|
||||
├── manufacturers/
|
||||
│ ├── reolink/
|
||||
│ │ └── e1_zoom_test.go
|
||||
│ ├── axis/
|
||||
│ │ ├── q3819_pve_test.go
|
||||
│ │ └── p3818_pve_test.go
|
||||
│ └── bosch/
|
||||
│ ├── flexidome_indoor_5100i_ir_test.go (existing)
|
||||
│ ├── flexidome_panoramic_5100i_test.go
|
||||
│ └── flexidome_starlight_8000i_test.go
|
||||
├── compliance/
|
||||
│ ├── stream_uri_test.go
|
||||
│ ├── imaging_test.go
|
||||
│ └── profile_test.go
|
||||
├── benchmarks/
|
||||
│ └── response_time_test.go
|
||||
└── edge_cases/
|
||||
├── incomplete_profiles_test.go
|
||||
└── error_handling_test.go
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Insights
|
||||
|
||||
### RTSP Tunnel Parameters (Bosch)
|
||||
|
||||
Bosch uses a proprietary `rtsp_tunnel` endpoint with various parameters:
|
||||
|
||||
- **p**: Profile index (0-15)
|
||||
- **line**: Video source line
|
||||
- 1 = Full image circle
|
||||
- 2 = Dewarped view mode
|
||||
- 3 = Electronic PTZ
|
||||
- **inst**: Stream instance (1-4, corresponds to bitrate tiers)
|
||||
- **h26x**: Codec selection
|
||||
- 0 = JPEG
|
||||
- 4 = H.264
|
||||
- **vcd**: Video coding
|
||||
- 2 = Metadata stream
|
||||
- **von**: Video on (0/1)
|
||||
- **aon**: Audio on (0/1)
|
||||
- **aud**: Audio stream identifier
|
||||
- **JpegCam**: Camera number for snapshots
|
||||
|
||||
### AXIS URL Parameters
|
||||
|
||||
- **profile**: Profile token
|
||||
- **sessiontimeout**: Session timeout in seconds
|
||||
- **streamtype**: unicast or multicast
|
||||
- **resolution**: Snapshot resolution (WxH)
|
||||
- **compression**: JPEG compression quality (0-100, lower = better)
|
||||
|
||||
### REOLINK CGI API
|
||||
|
||||
Uses proprietary CGI commands:
|
||||
- `cmd=onvifSnapPic`: Get ONVIF-compliant snapshot
|
||||
- `channel=0`: Camera channel
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Authentication
|
||||
All cameras require HTTP Digest Authentication for ONVIF requests.
|
||||
|
||||
### TLS Support
|
||||
|
||||
| Camera | TLS 1.1 | TLS 1.2 | Notes |
|
||||
|--------|---------|---------|-------|
|
||||
| REOLINK E1 Zoom | ✗ | ✗ | HTTP only |
|
||||
| AXIS Q3819-PVE | ✓ | ✓ | Full TLS support |
|
||||
| AXIS P3818-PVE | ✓ | ✓ | Full TLS support |
|
||||
| Bosch Panoramic 5100i | ✗ | ✓ | TLS 1.2 only |
|
||||
| Bosch Starlight 8000i | ✓ | ✓ | Full TLS support |
|
||||
|
||||
**Recommendation**: AXIS cameras provide the strongest security posture with IP filtering, access policy config, and TLS support.
|
||||
|
||||
### WS-Security
|
||||
All cameras support WS-Security UsernameToken with digest authentication, as evidenced by successful ONVIF communication.
|
||||
|
||||
---
|
||||
|
||||
## Compatibility Matrix
|
||||
|
||||
### ONVIF Profile Compliance
|
||||
|
||||
Based on feature analysis, likely ONVIF profile compliance:
|
||||
|
||||
| Camera | Profile S | Profile T | Profile G | Profile M |
|
||||
|--------|-----------|-----------|-----------|-----------|
|
||||
| REOLINK E1 Zoom | ✓ | ✓ (PTZ) | ✗ | ✗ |
|
||||
| AXIS Q3819-PVE | ✓ | ✗ | ✓ (Analytics) | ✓ (Metadata) |
|
||||
| AXIS P3818-PVE | ✓ | ✗ | ✓ (Analytics) | ✓ (Metadata) |
|
||||
| Bosch Panoramic 5100i | ✓ | ✓ (ePTZ) | ✓ (Analytics) | ✓ (Metadata) |
|
||||
| Bosch Starlight 8000i | ✓ | ✗ | ✗ | Partial |
|
||||
|
||||
**Profiles**:
|
||||
- **S**: Streaming (basic video)
|
||||
- **T**: PTZ control
|
||||
- **G**: Video analytics
|
||||
- **M**: Metadata streaming
|
||||
|
||||
---
|
||||
|
||||
## Conclusions
|
||||
|
||||
### Best Practices Discovered
|
||||
|
||||
1. **Profile Enumeration**: Always check VideoEncoderConfiguration before calling GetStreamURI
|
||||
2. **Error Handling**: Bosch cameras may return IncompleteConfiguration for metadata profiles
|
||||
3. **Response Times**: Expect 5-800ms for GetProfiles depending on camera complexity
|
||||
4. **URL Patterns**: Cannot assume consistent RTSP URL format across manufacturers
|
||||
5. **Imaging Defaults**: Manufacturers use different scales (0-255 vs 0-100 vs 0-128)
|
||||
|
||||
### Client Library Improvements Needed
|
||||
|
||||
1. **URL Parser**: Helper to parse and validate different RTSP URL formats
|
||||
2. **Profile Filter**: Method to filter profiles by capability (video, audio, metadata)
|
||||
3. **Retry Logic**: Handle transient errors and timeouts
|
||||
4. **TLS Support**: Enable HTTPS for cameras supporting TLS
|
||||
5. **Batch Operations**: Parallel GetStreamURI calls for cameras with many profiles
|
||||
|
||||
### Test Coverage Recommendations
|
||||
|
||||
Based on this analysis, create test files covering:
|
||||
|
||||
1. ✅ Bosch FLEXIDOME indoor 5100i IR (already exists)
|
||||
2. 🔲 REOLINK E1 Zoom (PTZ, dual stream)
|
||||
3. 🔲 AXIS Q3819-PVE (ultra-wide, analytics)
|
||||
4. 🔲 AXIS P3818-PVE (panoramic, analytics)
|
||||
5. 🔲 Bosch FLEXIDOME panoramic 5100i (16 profiles, dewarping)
|
||||
6. 🔲 Bosch FLEXIDOME IP starlight 8000i (low-light, I/O)
|
||||
|
||||
### Interoperability Score
|
||||
|
||||
Based on ONVIF compliance, feature richness, and ease of integration:
|
||||
|
||||
| Camera | Score | Rationale |
|
||||
|--------|-------|-----------|
|
||||
| AXIS P3818-PVE | 9.5/10 | Excellent compliance, fast, feature-rich |
|
||||
| AXIS Q3819-PVE | 9.5/10 | Same as P3818, ultra-wide resolution |
|
||||
| Bosch Starlight 8000i | 8.0/10 | Good compliance, moderate features |
|
||||
| Bosch Panoramic 5100i | 7.5/10 | Complex profile structure, some errors |
|
||||
| REOLINK E1 Zoom | 7.0/10 | Basic features, slower responses, limited imaging |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Create manufacturer-specific test files** for each camera model
|
||||
2. **Implement helper functions** for common patterns (URL parsing, profile filtering)
|
||||
3. **Add benchmark tests** to track performance regression
|
||||
4. **Document manufacturer quirks** in code comments
|
||||
5. **Create CI/CD pipeline** to test against real cameras (when available)
|
||||
6. **Expand coverage** for PTZ operations on REOLINK
|
||||
7. **Test analytics** on AXIS cameras
|
||||
8. **Validate TLS connections** on supported cameras
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Raw Data Summary
|
||||
|
||||
### REOLINK E1 Zoom
|
||||
- Profiles: 2
|
||||
- Stream URIs: 2/2 successful
|
||||
- Snapshot URIs: 2/2 successful
|
||||
- Video Encoders: 2/2 successful
|
||||
- Imaging Settings: 1/1 successful
|
||||
- PTZ Status: 2/2 successful (both IDLE)
|
||||
- PTZ Presets: 0
|
||||
- Total Errors: 0
|
||||
|
||||
### AXIS Q3819-PVE
|
||||
- Profiles: 2
|
||||
- Stream URIs: 2/2 successful
|
||||
- Snapshot URIs: 2/2 successful
|
||||
- Video Encoders: 2/2 successful
|
||||
- Imaging Settings: 1/1 successful
|
||||
- Total Errors: 0
|
||||
|
||||
### AXIS P3818-PVE
|
||||
- Profiles: 2
|
||||
- Stream URIs: 2/2 successful
|
||||
- Snapshot URIs: 2/2 successful
|
||||
- Video Encoders: 2/2 successful
|
||||
- Imaging Settings: 1/1 successful
|
||||
- Total Errors: 0
|
||||
|
||||
### Bosch FLEXIDOME panoramic 5100i
|
||||
- Profiles: 16
|
||||
- Stream URIs: 13/16 successful (3 IncompleteConfiguration errors)
|
||||
- Snapshot URIs: 16/16 successful
|
||||
- Video Encoders: 9/9 successful (only tested valid profiles)
|
||||
- Imaging Settings: 1/1 successful
|
||||
- Total Errors: 3 (expected for incomplete profiles)
|
||||
|
||||
### Bosch FLEXIDOME IP starlight 8000i
|
||||
- Profiles: 3
|
||||
- Stream URIs: 3/3 successful
|
||||
- Snapshot URIs: 3/3 successful
|
||||
- Video Encoders: 3/3 successful
|
||||
- Imaging Settings: 1/1 successful
|
||||
- Total Errors: 0
|
||||
|
||||
---
|
||||
|
||||
**End of Analysis Report**
|
||||
@@ -1,129 +0,0 @@
|
||||
# Linting Fixes - golangci-lint Issues Resolved
|
||||
|
||||
## Summary
|
||||
All 7 linting errors reported by golangci-lint have been successfully fixed.
|
||||
|
||||
## Issues Fixed
|
||||
|
||||
### 1. Unchecked Error Return: `rand.Read`
|
||||
**File:** `soap/soap.go:174`
|
||||
**Fix:** Added explicit error handling with comment explaining that `rand.Read` from `crypto/rand` always succeeds for valid buffer sizes.
|
||||
```go
|
||||
// Before
|
||||
rand.Read(nonceBytes)
|
||||
|
||||
// After
|
||||
_, _ = rand.Read(nonceBytes) // rand.Read always returns len(nonceBytes), nil
|
||||
```
|
||||
|
||||
### 2. Unchecked Error Return: `w.Write`
|
||||
**File:** `client_test.go:102`
|
||||
**Fix:** Added explicit error handling for `http.ResponseWriter.Write()` with explanatory comment.
|
||||
```go
|
||||
// Before
|
||||
w.Write([]byte(response))
|
||||
|
||||
// After
|
||||
_, _ = w.Write([]byte(response)) // Writing to ResponseWriter; error is handled by http package
|
||||
```
|
||||
|
||||
### 3-5. Unchecked Error Return: `client.Initialize`
|
||||
**Files:**
|
||||
- `cmd/onvif-quick/main.go:121`
|
||||
- `cmd/onvif-quick/main.go:164`
|
||||
- `cmd/onvif-quick/main.go:269`
|
||||
|
||||
**Fix:** Added explicit error ignoring with explanatory comments. Errors are caught in subsequent operations.
|
||||
```go
|
||||
// Before
|
||||
client.Initialize(ctx)
|
||||
|
||||
// After
|
||||
_ = client.Initialize(ctx) // Ignore initialization errors, we'll catch them on GetProfiles
|
||||
```
|
||||
|
||||
### 6. Unchecked Error Return: `client.Stop`
|
||||
**File:** `cmd/onvif-quick/main.go:226`
|
||||
**Fix:** Added explicit error handling for PTZ stop operation.
|
||||
```go
|
||||
// Before
|
||||
client.Stop(ctx, profileToken, true, false)
|
||||
|
||||
// After
|
||||
_ = client.Stop(ctx, profileToken, true, false) // Stop PTZ movement
|
||||
```
|
||||
|
||||
### 7. Unused Field: `deviceEndpoint`
|
||||
**File:** `client.go:21`
|
||||
**Fix:** Removed the unused field from the `Client` struct.
|
||||
```go
|
||||
// Before
|
||||
type Client struct {
|
||||
deviceEndpoint string
|
||||
mediaEndpoint string
|
||||
ptzEndpoint string
|
||||
imagingEndpoint string
|
||||
eventEndpoint string
|
||||
}
|
||||
|
||||
// After
|
||||
type Client struct {
|
||||
mediaEndpoint string
|
||||
ptzEndpoint string
|
||||
imagingEndpoint string
|
||||
eventEndpoint string
|
||||
}
|
||||
```
|
||||
|
||||
### 8-10. Unchecked Error Return: Deferred `Close()` calls
|
||||
**Files:**
|
||||
- `client_test.go:59` - `r.Body.Close()`
|
||||
- `discovery/discovery.go:81` - `conn.Close()`
|
||||
- `soap/soap.go:128` - `resp.Body.Close()`
|
||||
|
||||
**Fix:** Wrapped deferred close calls in anonymous functions to properly handle errors.
|
||||
```go
|
||||
// Before
|
||||
defer conn.Close()
|
||||
|
||||
// After
|
||||
defer func() { _ = conn.Close() }()
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
### Linting Results
|
||||
```bash
|
||||
$ golangci-lint run --timeout=5m
|
||||
0 issues.
|
||||
```
|
||||
|
||||
### Test Results
|
||||
All tests continue to pass:
|
||||
```bash
|
||||
$ go test -v ./...
|
||||
PASS
|
||||
ok github.com/0x524A/go-onvif 30.008s
|
||||
```
|
||||
|
||||
### Build Results
|
||||
Both CLI tools build successfully:
|
||||
```bash
|
||||
$ make build
|
||||
🔨 Building ONVIF CLI...
|
||||
🔨 Building ONVIF Quick Tool...
|
||||
```
|
||||
|
||||
## Best Practices Applied
|
||||
|
||||
1. **Explicit Error Handling:** All error returns are now explicitly handled or documented why they're ignored
|
||||
2. **Deferred Close Patterns:** Properly wrapped `Close()` calls in anonymous functions for defer statements
|
||||
3. **Code Cleanliness:** Removed unused struct fields to reduce code bloat
|
||||
4. **Documentation:** Added inline comments explaining why certain errors are explicitly ignored
|
||||
|
||||
## Impact
|
||||
- ✅ No functional changes to the library behavior
|
||||
- ✅ All tests still pass
|
||||
- ✅ CLI tools compile and work correctly
|
||||
- ✅ Code now follows Go best practices and linting standards
|
||||
- ✅ Ready for CI/CD pipelines with strict linting requirements
|
||||
@@ -96,29 +96,87 @@ examples:
|
||||
go build -o $(BINARY_DIR)/examples/ptz ./examples/ptz
|
||||
|
||||
# Build for multiple platforms
|
||||
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
|
||||
LDFLAGS := -ldflags "-s -w -X main.Version=$(VERSION)"
|
||||
|
||||
build-all:
|
||||
@echo "🌍 Building for multiple platforms..."
|
||||
@echo "🌍 Building for multiple platforms (version: $(VERSION))..."
|
||||
@mkdir -p $(BINARY_DIR)
|
||||
|
||||
# Linux AMD64
|
||||
GOOS=linux GOARCH=amd64 go build -o $(BINARY_DIR)/onvif-cli-linux-amd64 ./cmd/onvif-cli
|
||||
GOOS=linux GOARCH=amd64 go build -o $(BINARY_DIR)/onvif-quick-linux-amd64 ./cmd/onvif-quick
|
||||
@echo "Building Linux AMD64..."
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-cli-linux-amd64 ./cmd/onvif-cli
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-quick-linux-amd64 ./cmd/onvif-quick
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-server-linux-amd64 ./cmd/onvif-server
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-diagnostics-linux-amd64 ./cmd/onvif-diagnostics
|
||||
|
||||
# Linux ARM64
|
||||
GOOS=linux GOARCH=arm64 go build -o $(BINARY_DIR)/onvif-cli-linux-arm64 ./cmd/onvif-cli
|
||||
GOOS=linux GOARCH=arm64 go build -o $(BINARY_DIR)/onvif-quick-linux-arm64 ./cmd/onvif-quick
|
||||
@echo "Building Linux ARM64..."
|
||||
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-cli-linux-arm64 ./cmd/onvif-cli
|
||||
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-quick-linux-arm64 ./cmd/onvif-quick
|
||||
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-server-linux-arm64 ./cmd/onvif-server
|
||||
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-diagnostics-linux-arm64 ./cmd/onvif-diagnostics
|
||||
|
||||
# Linux ARM (32-bit)
|
||||
@echo "Building Linux ARM..."
|
||||
GOOS=linux GOARCH=arm CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-cli-linux-arm ./cmd/onvif-cli
|
||||
GOOS=linux GOARCH=arm CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-quick-linux-arm ./cmd/onvif-quick
|
||||
|
||||
# Windows AMD64
|
||||
GOOS=windows GOARCH=amd64 go build -o $(BINARY_DIR)/onvif-cli-windows-amd64.exe ./cmd/onvif-cli
|
||||
GOOS=windows GOARCH=amd64 go build -o $(BINARY_DIR)/onvif-quick-windows-amd64.exe ./cmd/onvif-quick
|
||||
@echo "Building Windows AMD64..."
|
||||
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-cli-windows-amd64.exe ./cmd/onvif-cli
|
||||
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-quick-windows-amd64.exe ./cmd/onvif-quick
|
||||
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-server-windows-amd64.exe ./cmd/onvif-server
|
||||
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-diagnostics-windows-amd64.exe ./cmd/onvif-diagnostics
|
||||
|
||||
# macOS AMD64
|
||||
GOOS=darwin GOARCH=amd64 go build -o $(BINARY_DIR)/onvif-cli-darwin-amd64 ./cmd/onvif-cli
|
||||
GOOS=darwin GOARCH=amd64 go build -o $(BINARY_DIR)/onvif-quick-darwin-amd64 ./cmd/onvif-quick
|
||||
# Windows ARM64
|
||||
@echo "Building Windows ARM64..."
|
||||
GOOS=windows GOARCH=arm64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-cli-windows-arm64.exe ./cmd/onvif-cli
|
||||
GOOS=windows GOARCH=arm64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-quick-windows-arm64.exe ./cmd/onvif-quick
|
||||
|
||||
# macOS AMD64 (Intel)
|
||||
@echo "Building macOS AMD64..."
|
||||
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-cli-darwin-amd64 ./cmd/onvif-cli
|
||||
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-quick-darwin-amd64 ./cmd/onvif-quick
|
||||
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-server-darwin-amd64 ./cmd/onvif-server
|
||||
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-diagnostics-darwin-amd64 ./cmd/onvif-diagnostics
|
||||
|
||||
# macOS ARM64 (Apple Silicon)
|
||||
GOOS=darwin GOARCH=arm64 go build -o $(BINARY_DIR)/onvif-cli-darwin-arm64 ./cmd/onvif-cli
|
||||
GOOS=darwin GOARCH=arm64 go build -o $(BINARY_DIR)/onvif-quick-darwin-arm64 ./cmd/onvif-quick
|
||||
@echo "Building macOS ARM64..."
|
||||
GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-cli-darwin-arm64 ./cmd/onvif-cli
|
||||
GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-quick-darwin-arm64 ./cmd/onvif-quick
|
||||
GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-server-darwin-arm64 ./cmd/onvif-server
|
||||
GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build $(LDFLAGS) -o $(BINARY_DIR)/onvif-diagnostics-darwin-arm64 ./cmd/onvif-diagnostics
|
||||
|
||||
@echo "✅ All binaries built successfully in $(BINARY_DIR)/"
|
||||
@echo ""
|
||||
@ls -lh $(BINARY_DIR)/
|
||||
|
||||
# Create release archives with checksums
|
||||
release: build-all
|
||||
@echo "📦 Creating release archives..."
|
||||
@mkdir -p releases
|
||||
|
||||
# Create archives for each platform
|
||||
@cd $(BINARY_DIR) && \
|
||||
for os in linux darwin windows; do \
|
||||
for arch in amd64 arm64 arm; do \
|
||||
if [ "$$os" = "windows" ] && [ "$$arch" != "arm" ]; then \
|
||||
if [ -f onvif-cli-$$os-$$arch.exe ]; then \
|
||||
zip -j ../releases/go-onvif-$(VERSION)-$$os-$$arch.zip onvif-*-$$os-$$arch.exe ../README.md ../LICENSE 2>/dev/null || true; \
|
||||
fi; \
|
||||
elif [ "$$os" != "windows" ]; then \
|
||||
if [ -f onvif-cli-$$os-$$arch ]; then \
|
||||
tar czf ../releases/go-onvif-$(VERSION)-$$os-$$arch.tar.gz onvif-*-$$os-$$arch ../README.md ../LICENSE 2>/dev/null || true; \
|
||||
fi; \
|
||||
fi; \
|
||||
done; \
|
||||
done
|
||||
|
||||
# Generate checksums
|
||||
@cd releases && sha256sum * > checksums.txt 2>/dev/null || shasum -a 256 * > checksums.txt
|
||||
@echo "✅ Release archives created in releases/"
|
||||
@ls -lh releases/
|
||||
|
||||
# Create Docker image
|
||||
docker:
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
# Unit Test Coverage Report
|
||||
|
||||
## Summary
|
||||
Added comprehensive unit tests to increase code coverage across the go-onvif library.
|
||||
|
||||
## Coverage Improvements
|
||||
|
||||
### Before
|
||||
- Main package (`onvif`): 8.1%
|
||||
- Discovery package: 0%
|
||||
- SOAP package: 0%
|
||||
- **Overall**: ~3% average
|
||||
|
||||
### After
|
||||
- Main package (`onvif`): **19.9%** ✅ (+11.8%)
|
||||
- Discovery package: **67.2%** ✅ (+67.2%)
|
||||
- SOAP package: **81.5%** ✅ (+81.5%)
|
||||
- **Overall**: ~56% average (+53%)
|
||||
|
||||
## Test Files Created
|
||||
|
||||
### 1. `/workspaces/go-onvif/soap/soap_test.go` (297 lines)
|
||||
Comprehensive tests for the SOAP client package:
|
||||
- `TestNewClient` - Client creation with/without credentials
|
||||
- `TestBuildEnvelope` - SOAP envelope generation
|
||||
- `TestClientCall` - HTTP request handling with multiple scenarios:
|
||||
- Successful request
|
||||
- Unauthorized request (401)
|
||||
- HTTP error status (500)
|
||||
- `TestClientCallWithTimeout` - Context timeout behavior
|
||||
- `TestSecurityHeaderCreation` - WS-Security header validation
|
||||
- `BenchmarkNewClient` - Performance: Client creation
|
||||
- `BenchmarkBuildEnvelope` - Performance: Envelope building
|
||||
- `BenchmarkCall` - Performance: SOAP calls
|
||||
|
||||
**Coverage**: 81.5%
|
||||
|
||||
### 2. `/workspaces/go-onvif/discovery/discovery_test.go` (194 lines)
|
||||
Unit tests for the WS-Discovery package:
|
||||
- `TestDevice_GetName` - Device name extraction from scopes
|
||||
- `TestDevice_GetDeviceEndpoint` - Endpoint extraction from XAddrs
|
||||
- `TestDevice_GetLocation` - Location extraction from scopes
|
||||
- `TestDiscover_WithTimeout` - Discovery with timeout
|
||||
- `TestDiscover_InvalidDuration` - Edge case: zero duration
|
||||
- `TestParseSpaceSeparated` - Utility function testing
|
||||
- `TestDevice_GetTypes` - Device type validation
|
||||
- `TestDevice_GetScopes` - Scope parsing
|
||||
- `BenchmarkDeviceGetName` - Performance: Name extraction
|
||||
- `BenchmarkDeviceGetDeviceEndpoint` - Performance: Endpoint extraction
|
||||
|
||||
**Coverage**: 67.2%
|
||||
|
||||
### 3. `/workspaces/go-onvif/device_test.go` (398 lines)
|
||||
Unit tests for the main ONVIF device service:
|
||||
- `TestGetDeviceInformation` - Device info retrieval (success & fault cases)
|
||||
- `TestGetCapabilities` - Capabilities retrieval
|
||||
- `TestGetHostname` - Hostname retrieval
|
||||
- `TestSetHostname` - Hostname modification
|
||||
- `TestGetDNS` - DNS configuration retrieval
|
||||
- `TestGetUsers` - User account listing
|
||||
- `TestCreateUsers` - User creation
|
||||
- `TestDeleteUsers` - User deletion
|
||||
- `TestGetNetworkInterfaces` - Network interface configuration
|
||||
- `BenchmarkDeviceGetDeviceInformation` - Performance: Device info
|
||||
|
||||
**Coverage**: 19.9% (main package also includes media, ptz, imaging which need additional tests)
|
||||
|
||||
## Test Patterns Used
|
||||
|
||||
### 1. Table-Driven Tests
|
||||
```go
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
wantErr bool
|
||||
}{
|
||||
{"success case", successHandler, false},
|
||||
{"error case", errorHandler, true},
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Mock HTTP Servers
|
||||
```go
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
response := `<?xml version="1.0"?>...</xml>`
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(response))
|
||||
}))
|
||||
defer server.Close()
|
||||
```
|
||||
|
||||
### 3. Context Testing
|
||||
```go
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
```
|
||||
|
||||
### 4. Benchmark Tests
|
||||
```go
|
||||
func BenchmarkOperation(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
operation()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Next Steps (Optional)
|
||||
|
||||
To achieve higher coverage (>80% overall), consider adding tests for:
|
||||
|
||||
1. **Media Service** (`media.go`)
|
||||
- GetProfiles
|
||||
- GetStreamURI
|
||||
- GetSnapshotURI
|
||||
- Video encoder configuration
|
||||
|
||||
2. **PTZ Service** (`ptz.go`)
|
||||
- ContinuousMove
|
||||
- AbsoluteMove
|
||||
- RelativeMove
|
||||
- Presets management
|
||||
|
||||
3. **Imaging Service** (`imaging.go`)
|
||||
- Imaging settings
|
||||
- Video source configuration
|
||||
|
||||
4. **Server Package** (`server/`)
|
||||
- Server initialization
|
||||
- SOAP handler
|
||||
- Service endpoints
|
||||
|
||||
5. **Integration Tests**
|
||||
- End-to-end workflows
|
||||
- Multi-service interactions
|
||||
- Real camera simulation
|
||||
|
||||
## Testing Commands
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
go test ./...
|
||||
|
||||
# Run tests with coverage
|
||||
go test -cover ./...
|
||||
|
||||
# Generate detailed coverage report
|
||||
go test -coverprofile=coverage.out ./...
|
||||
go tool cover -html=coverage.out
|
||||
|
||||
# Run specific package tests
|
||||
go test ./soap/
|
||||
go test ./discovery/
|
||||
go test .
|
||||
|
||||
# Run benchmarks
|
||||
go test -bench=. ./soap/
|
||||
go test -bench=. ./discovery/
|
||||
```
|
||||
|
||||
## Impact
|
||||
|
||||
✅ **Linting**: Clean (all previous linting errors fixed)
|
||||
✅ **Build**: Passes
|
||||
✅ **Tests**: All passing
|
||||
✅ **Coverage**: Increased from ~3% to ~56% average
|
||||
✅ **Quality**: Production-ready with comprehensive test coverage
|
||||
|
||||
The library now has:
|
||||
- Strong test coverage for core SOAP functionality
|
||||
- Good coverage for device discovery
|
||||
- Foundation for device service testing
|
||||
- Benchmark tests for performance monitoring
|
||||
- Patterns that can be extended to other services
|
||||
Executable
+112
@@ -0,0 +1,112 @@
|
||||
#!/bin/bash
|
||||
# build-release.sh - Build release binaries locally
|
||||
|
||||
set -e
|
||||
|
||||
VERSION=${1:-$(git describe --tags --always --dirty 2>/dev/null || echo "dev")}
|
||||
echo "Building release binaries for version: $VERSION"
|
||||
|
||||
# Clean previous builds
|
||||
rm -rf bin releases
|
||||
mkdir -p bin releases
|
||||
|
||||
# Platforms to build
|
||||
PLATFORMS=(
|
||||
"linux/amd64"
|
||||
"linux/arm64"
|
||||
"linux/arm"
|
||||
"windows/amd64"
|
||||
"windows/arm64"
|
||||
"darwin/amd64"
|
||||
"darwin/arm64"
|
||||
)
|
||||
|
||||
# Binaries to build
|
||||
BINARIES=(
|
||||
"onvif-cli"
|
||||
"onvif-quick"
|
||||
"onvif-server"
|
||||
"onvif-diagnostics"
|
||||
)
|
||||
|
||||
LDFLAGS="-s -w -X main.Version=${VERSION} -X main.Commit=$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
|
||||
|
||||
echo "Building binaries..."
|
||||
for platform in "${PLATFORMS[@]}"; do
|
||||
OS="${platform%/*}"
|
||||
ARCH="${platform#*/}"
|
||||
|
||||
echo ""
|
||||
echo "Building for $OS/$ARCH..."
|
||||
|
||||
for binary in "${BINARIES[@]}"; do
|
||||
OUTPUT="bin/${binary}-${OS}-${ARCH}"
|
||||
|
||||
if [ "$OS" = "windows" ]; then
|
||||
OUTPUT="${OUTPUT}.exe"
|
||||
fi
|
||||
|
||||
echo " - ${binary}"
|
||||
GOOS=$OS GOARCH=$ARCH CGO_ENABLED=0 go build -ldflags="${LDFLAGS}" -o "$OUTPUT" "./cmd/${binary}" 2>/dev/null || {
|
||||
echo " ⚠️ Skipped (build failed)"
|
||||
continue
|
||||
}
|
||||
done
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Creating release archives..."
|
||||
|
||||
cd bin
|
||||
|
||||
for platform in "${PLATFORMS[@]}"; do
|
||||
OS="${platform%/*}"
|
||||
ARCH="${platform#*/}"
|
||||
ARCHIVE_NAME="go-onvif-${VERSION}-${OS}-${ARCH}"
|
||||
|
||||
# Check if any binary exists for this platform
|
||||
if [ "$OS" = "windows" ]; then
|
||||
FILES=(*-${OS}-${ARCH}.exe)
|
||||
else
|
||||
FILES=(*-${OS}-${ARCH})
|
||||
fi
|
||||
|
||||
# Skip if no files found
|
||||
if [ "${FILES[0]}" = "*-${OS}-${ARCH}" ] || [ "${FILES[0]}" = "*-${OS}-${ARCH}.exe" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo " Creating archive for ${OS}/${ARCH}..."
|
||||
|
||||
if [ "$OS" = "windows" ]; then
|
||||
# ZIP for Windows
|
||||
zip -q "../releases/${ARCHIVE_NAME}.zip" *-${OS}-${ARCH}.exe ../README.md ../LICENSE
|
||||
else
|
||||
# tar.gz for Unix-like
|
||||
tar czf "../releases/${ARCHIVE_NAME}.tar.gz" *-${OS}-${ARCH} -C .. README.md LICENSE
|
||||
fi
|
||||
done
|
||||
|
||||
cd ..
|
||||
|
||||
echo ""
|
||||
echo "Generating checksums..."
|
||||
cd releases
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum * > checksums.txt
|
||||
else
|
||||
shasum -a 256 * > checksums.txt
|
||||
fi
|
||||
cd ..
|
||||
|
||||
echo ""
|
||||
echo "✅ Build complete!"
|
||||
echo ""
|
||||
echo "Binaries in: $(pwd)/bin/"
|
||||
echo "Archives in: $(pwd)/releases/"
|
||||
echo ""
|
||||
ls -lh releases/
|
||||
|
||||
echo ""
|
||||
echo "To create a GitHub release, run:"
|
||||
echo " gh release create ${VERSION} releases/* --title \"Release ${VERSION}\" --notes \"Release notes here\""
|
||||
Reference in New Issue
Block a user