Skip to content

Commit 23de47a

Browse files
gmathy2104claude
andcommitted
feat: add dynamic framerate control with intelligent clamping (v2.3.0)
Added comprehensive framerate control system with automatic hardware limit enforcement: New Features: - POST /v1/camera/framerate - Change framerate dynamically with smart clamping - Automatic clamping to hardware maximum based on current resolution - User-friendly API that never rejects requests, applies best available framerate - Detailed response with requested vs applied framerate and clamping indicator Enhanced Capabilities: - GET /v1/camera/capabilities now includes: - current_framerate: Currently configured framerate - max_framerate_for_current_resolution: Max fps for active resolution - framerate_limits_by_resolution: Complete table of limits per resolution Resolution-based Framerate Limits: - 4K (3840x2160): max 30 fps - 1440p (2560x1440): max 40 fps - 1080p (1920x1080): max 50 fps - 720p (1280x720): max 120 fps - VGA (640x480): max 120 fps Implementation: - Added calculate_max_framerate() function based on pixel count - Added set_framerate() method in CameraController with intelligent clamping - State tracking for current width, height, and framerate - Resolution changes now preserve current framerate setting Testing: - Added 3 new integration tests (16 total tests, all passing) - test_capabilities_with_framerate_limits - test_framerate_change_normal - test_framerate_change_with_clamping Documentation: - Updated README.md with v2.3 features and usage examples - Updated CHANGELOG.md with comprehensive v2.3.0 entry - Added cURL and Python examples for framerate control - API version bumped to 2.3.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 37629c5 commit 23de47a

File tree

7 files changed

+836
-22
lines changed

7 files changed

+836
-22
lines changed

CHANGELOG.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,60 @@ All notable changes to Pi Camera Service will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.3.0] - 2025-11-22
9+
10+
### Added
11+
12+
#### Dynamic Framerate Control with Intelligent Clamping
13+
- `POST /v1/camera/framerate` - Change framerate dynamically with smart limit enforcement
14+
- Automatically clamps requested framerate to hardware maximum for current resolution
15+
- Returns detailed information about requested vs applied framerate
16+
- Indicates with `clamped` flag when value was adjusted
17+
- Example: Requesting 500fps at 4K automatically applies 30fps (the maximum for 4K)
18+
19+
#### Framerate Limits in Capabilities
20+
- Enhanced `GET /v1/camera/capabilities` with framerate information:
21+
- `current_framerate`: Currently configured framerate
22+
- `max_framerate_for_current_resolution`: Maximum framerate for active resolution
23+
- `framerate_limits_by_resolution`: Complete table of max fps per resolution
24+
- 4K (3840x2160): 30fps max
25+
- 1440p (2560x1440): 40fps max
26+
- 1080p (1920x1080): 50fps max
27+
- 720p (1280x720): 120fps max
28+
- VGA (640x480): 120fps max
29+
30+
### Changed
31+
- Resolution tracking now persists across camera reconfigurations
32+
- Updated test suite with 3 new comprehensive tests (now 16 tests total for v2.1+)
33+
34+
### Documentation
35+
- Updated README.md with v2.3 framerate endpoint documentation
36+
- Added comprehensive CHANGELOG entry for v2.3
37+
38+
## [2.2.0] - 2025-11-22
39+
40+
### Added
41+
42+
#### Camera Capabilities Discovery
43+
- `GET /v1/camera/capabilities` - New endpoint to query camera hardware capabilities
44+
- Returns sensor model, supported resolutions, exposure/gain limits
45+
- Lists all supported features (autofocus, noise reduction modes, etc.)
46+
- Provides exposure value ranges and available control modes
47+
- Essential for clients to discover what the camera supports
48+
49+
#### Enhanced Status Information
50+
- `GET /v1/camera/status` now includes `current_limits` field
51+
- Shows currently active frame duration and exposure limits
52+
- Displays effective exposure limits based on hardware and configured constraints
53+
- Helps understand why certain exposure values may not be achievable
54+
55+
### Changed
56+
- Updated test suite with 2 new comprehensive tests (now 13 tests total for v2.1)
57+
58+
### Documentation
59+
- Updated README.md with v2.2 capabilities endpoint
60+
- Added comprehensive CHANGELOG entry for v2.2
61+
862
## [2.1.0] - 2025-11-22
963

1064
### Added

README.md

Lines changed: 172 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@
22

33
Production-ready **FastAPI** microservice for controlling Raspberry Pi Camera (libcamera/Picamera2) with **H.264 streaming** to **MediaMTX** via RTSP.
44

5-
**Version 2.1** - Advanced exposure controls, noise reduction, dynamic resolution, and low-light optimization!
5+
**Version 2.3** - Dynamic framerate control with intelligent clamping and comprehensive camera capabilities!
66

7-
[![Version](https://img.shields.io/badge/version-2.1.0-blue.svg)](https://github.com/gmathy2104/pi-camera-service/releases)
7+
[![Version](https://img.shields.io/badge/version-2.3.0-blue.svg)](https://github.com/gmathy2104/pi-camera-service/releases)
88
[![Python](https://img.shields.io/badge/python-3.9+-green.svg)](https://www.python.org/)
99
[![FastAPI](https://img.shields.io/badge/FastAPI-0.121+-teal.svg)](https://fastapi.tiangolo.com/)
1010
[![License](https://img.shields.io/badge/license-MIT-orange.svg)](LICENSE)
1111
[![Tests](https://github.com/gmathy2104/pi-camera-service/workflows/Tests/badge.svg)](https://github.com/gmathy2104/pi-camera-service/actions)
1212

13-
> 🆕 **New in v2.1**: Exposure value (EV) compensation, noise reduction modes, advanced AE controls (constraint/exposure modes), AWB mode presets, autofocus trigger, dynamic resolution change, and corrected exposure limits! Perfect for low-light scenarios.
13+
> 🆕 **New in v2.3**: Dynamic framerate control with intelligent clamping! Request any framerate and the API automatically applies the maximum for your resolution. Enhanced capabilities endpoint with complete framerate limits table.
14+
>
15+
> ℹ️ **v2.2 features**: Camera capabilities discovery endpoint to query supported resolutions, exposure/gain limits, and available features.
16+
>
17+
> ℹ️ **v2.1 features**: Exposure value (EV) compensation, noise reduction modes, advanced AE controls, AWB mode presets, autofocus trigger, dynamic resolution change.
1418
>
1519
> ℹ️ **v2.0 features**: Autofocus control, snapshot capture, manual AWB with NoIR presets, image processing, HDR support, ROI/digital zoom, day/night detection. See [docs/upgrade-v2.md](docs/upgrade-v2.md).
1620
@@ -166,6 +170,44 @@ This service runs **on the Raspberry Pi**, controls the camera (e.g., Raspberry
166170
- `./scripts/set-normal-mode.sh` - Reset to defaults
167171
- **Documentation**: See [docs/low-light-modes.md](docs/low-light-modes.md)
168172

173+
### Advanced Features (v2.2) 🆕
174+
175+
#### 📊 Camera Capabilities Discovery
176+
- **Hardware discovery**: Query camera sensor model, resolution, and supported features
177+
- **Exposure/gain limits**: Get minimum and maximum values for exposure and gain
178+
- **Feature detection**: Discover what controls are available (autofocus, HDR, etc.)
179+
- **Resolution support**: List all supported streaming resolutions
180+
- **Essential for clients**: Know what the camera supports before configuring
181+
- **Endpoint**: `GET /v1/camera/capabilities`
182+
183+
#### 📈 Enhanced Status Information
184+
- **Current limits**: See active frame duration and exposure constraints
185+
- **Effective limits**: Understand why certain exposure values may not apply
186+
- **Real-time constraints**: Monitor hardware and configured limits
187+
- **Enhanced field**: `current_limits` in `GET /v1/camera/status`
188+
189+
### Advanced Features (v2.3) 🆕
190+
191+
#### 🎬 Dynamic Framerate Control with Intelligent Clamping
192+
- **Independent framerate adjustment**: Change framerate without changing resolution
193+
- **Smart limit enforcement**: Automatically clamps to hardware maximum for current resolution
194+
- **User-friendly**: No rejected requests - API applies best available framerate
195+
- **Detailed feedback**: Returns requested vs applied framerate with clamping indicator
196+
- **Resolution-aware limits**:
197+
- 4K (3840x2160): max 30 fps
198+
- 1440p (2560x1440): max 40 fps
199+
- 1080p (1920x1080): max 50 fps
200+
- 720p (1280x720): max 120 fps
201+
- VGA (640x480): max 120 fps
202+
- **Example**: Request 500fps at 4K → API applies 30fps (the maximum) and indicates clamping occurred
203+
- **Endpoint**: `POST /v1/camera/framerate`
204+
205+
#### 📊 Complete Framerate Limits Table
206+
- **Enhanced capabilities**: `GET /v1/camera/capabilities` now includes:
207+
- `current_framerate`: Currently configured framerate
208+
- `max_framerate_for_current_resolution`: Maximum fps for active resolution
209+
- `framerate_limits_by_resolution`: Complete table of max fps for each resolution
210+
169211
The video stream is published to MediaMTX, which then serves it via **RTSP / WebRTC / HLS**.
170212

171213
---
@@ -339,7 +381,7 @@ sudo journalctl -u pi-camera-service -f
339381
"status": "healthy",
340382
"camera_configured": true,
341383
"streaming_active": true,
342-
"version": "2.0.0"
384+
"version": "2.3.0"
343385
}
344386
```
345387

@@ -365,7 +407,63 @@ sudo journalctl -u pi-camera-service -f
365407
"day_night_mode": "auto",
366408
"day_night_threshold_lux": 10.0,
367409
"frame_duration_us": 33321,
368-
"sensor_black_levels": [4096, 4096, 4096, 4096]
410+
"sensor_black_levels": [4096, 4096, 4096, 4096],
411+
412+
// New v2.2 field
413+
"current_limits": {
414+
"frame_duration_us": 33321,
415+
"frame_duration_limits_us": null,
416+
"effective_exposure_limit_us": {
417+
"min": 100,
418+
"max": 1000000
419+
}
420+
}
421+
}
422+
```
423+
424+
### Camera Capabilities (New in v2.2, Enhanced in v2.3)
425+
426+
**GET** `/v1/camera/capabilities`
427+
428+
Discover camera hardware capabilities and supported features.
429+
430+
```json
431+
{
432+
"sensor_model": "imx708",
433+
"sensor_resolution": {
434+
"width": 4608,
435+
"height": 2592
436+
},
437+
"supported_resolutions": [
438+
{"width": 640, "height": 480, "label": "VGA"},
439+
{"width": 1280, "height": 720, "label": "720p"},
440+
{"width": 1920, "height": 1080, "label": "1080p"},
441+
{"width": 2560, "height": 1440, "label": "1440p"},
442+
{"width": 3840, "height": 2160, "label": "4K"}
443+
],
444+
"exposure_limits_us": {"min": 100, "max": 1000000},
445+
"gain_limits": {"min": 1.0, "max": 16.0},
446+
"lens_position_limits": {"min": 0.0, "max": 15.0},
447+
"exposure_value_range": {"min": -8.0, "max": 8.0},
448+
"supported_noise_reduction_modes": ["off", "fast", "high_quality", "minimal", "zsl"],
449+
"supported_ae_constraint_modes": ["normal", "highlight", "shadows", "custom"],
450+
"supported_ae_exposure_modes": ["normal", "short", "long", "custom"],
451+
"supported_awb_modes": ["auto", "tungsten", "fluorescent", "indoor", "daylight", "cloudy", "custom"],
452+
"features": [
453+
"auto_exposure", "manual_exposure", "auto_white_balance", "manual_white_balance",
454+
"exposure_value_compensation", "noise_reduction", "ae_constraint_modes",
455+
"ae_exposure_modes", "awb_modes", "image_processing", "roi_digital_zoom",
456+
"exposure_limits", "autofocus", "lens_position_control", "autofocus_trigger"
457+
],
458+
"current_framerate": 30.0,
459+
"max_framerate_for_current_resolution": 50.0,
460+
"framerate_limits_by_resolution": [
461+
{"width": 3840, "height": 2160, "label": "4K", "max_fps": 30.0},
462+
{"width": 2560, "height": 1440, "label": "1440p", "max_fps": 40.0},
463+
{"width": 1920, "height": 1080, "label": "1080p", "max_fps": 50.0},
464+
{"width": 1280, "height": 720, "label": "720p", "max_fps": 120.0},
465+
{"width": 640, "height": 480, "label": "VGA", "max_fps": 120.0}
466+
]
369467
}
370468
```
371469

@@ -473,6 +571,57 @@ sudo journalctl -u pi-camera-service -f
473571
}
474572
```
475573

574+
### Dynamic Framerate Control (v2.3)
575+
576+
**POST** `/v1/camera/framerate`
577+
578+
Change camera framerate dynamically with intelligent clamping. The API automatically applies the hardware maximum for your current resolution, ensuring a user-friendly experience without rejected requests.
579+
580+
**Request**:
581+
```json
582+
{
583+
"framerate": 60.0, // Desired framerate (1-1000 fps)
584+
"restart_streaming": true // Restart streaming after change (default: true)
585+
}
586+
```
587+
588+
**Response**:
589+
```json
590+
{
591+
"status": "ok",
592+
"requested_framerate": 60.0,
593+
"applied_framerate": 50.0, // Actual framerate applied (may be clamped)
594+
"max_framerate_for_resolution": 50.0,
595+
"resolution": "1920x1080",
596+
"clamped": true // Indicates if framerate was clamped to max
597+
}
598+
```
599+
600+
**Example - Requesting high framerate at 4K**:
601+
```bash
602+
# Request 500fps at 4K resolution
603+
curl -X POST http://raspberrypi:8000/v1/camera/framerate \
604+
-H "Content-Type: application/json" \
605+
-d '{"framerate": 500}'
606+
607+
# Response: API automatically clamps to 30fps (4K maximum)
608+
# {
609+
# "status": "ok",
610+
# "requested_framerate": 500.0,
611+
# "applied_framerate": 30.0,
612+
# "max_framerate_for_resolution": 30.0,
613+
# "resolution": "3840x2160",
614+
# "clamped": true
615+
# }
616+
```
617+
618+
**Resolution-based framerate limits**:
619+
- 4K (3840x2160): max 30 fps
620+
- 1440p (2560x1440): max 40 fps
621+
- 1080p (1920x1080): max 50 fps
622+
- 720p (1280x720): max 120 fps
623+
- VGA (640x480): max 120 fps
624+
476625
### Streaming Control
477626

478627
**POST** `/v1/streaming/start`
@@ -555,6 +704,11 @@ curl -X POST http://raspberrypi:8000/v1/camera/roi \
555704
-H "Content-Type: application/json" \
556705
-d '{"x": 0.25, "y": 0.25, "width": 0.5, "height": 0.5}'
557706

707+
# Change framerate (v2.3) - intelligent clamping
708+
curl -X POST http://raspberrypi:8000/v1/camera/framerate \
709+
-H "Content-Type: application/json" \
710+
-d '{"framerate": 60}'
711+
558712
# With authentication (if CAMERA_API_KEY is set)
559713
curl -H "X-API-Key: your-key" \
560714
http://raspberrypi:8000/v1/camera/status
@@ -612,6 +766,19 @@ requests.post(
612766
},
613767
headers=HEADERS
614768
)
769+
770+
# Change framerate (v2.3) with intelligent clamping
771+
response = requests.post(
772+
f"{BASE_URL}/v1/camera/framerate",
773+
json={"framerate": 60},
774+
headers=HEADERS
775+
)
776+
result = response.json()
777+
if result['clamped']:
778+
print(f"Framerate clamped: {result['requested_framerate']}fps → {result['applied_framerate']}fps")
779+
print(f"Max for {result['resolution']}: {result['max_framerate_for_resolution']}fps")
780+
else:
781+
print(f"Framerate set to {result['applied_framerate']}fps")
615782
```
616783

617784
### JavaScript / TypeScript

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.1.0
1+
2.3.0

0 commit comments

Comments
 (0)