-
Notifications
You must be signed in to change notification settings - Fork 7
Move resolution/rotation/frame rate/timestamp config to my_secrets.py, remove CLI code #539
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: resolution
Are you sure you want to change the base?
Changes from all commits
7886c26
225738b
e4fc134
0ad81a2
c2a71bd
e96f0af
496a2d7
29357c7
a4df250
61dcb3a
d359359
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,3 @@ | ||||||||||||||
| import argparse | ||||||||||||||
| import json | ||||||||||||||
| import subprocess | ||||||||||||||
| import shutil | ||||||||||||||
|
|
@@ -7,12 +6,26 @@ | |||||||||||||
| from my_secrets import ( | ||||||||||||||
| CAM_NAME, | ||||||||||||||
| CAMERA_HFLIP, | ||||||||||||||
| CAMERA_ROTATION, | ||||||||||||||
| CAMERA_VFLIP, | ||||||||||||||
| FRAME_RATE, | ||||||||||||||
| LAMBDA_FUNCTION_URL, | ||||||||||||||
| PRIVACY_STATUS, | ||||||||||||||
| RESOLUTION, | ||||||||||||||
| TIMESTAMP_OVERLAY, | ||||||||||||||
| WORKFLOW_NAME, | ||||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
| # Resolution mappings for YouTube-compatible resolutions | ||||||||||||||
| RESOLUTION_MAP = { | ||||||||||||||
| "144p": (256, 144), | ||||||||||||||
| "240p": (426, 240), | ||||||||||||||
| "360p": (640, 360), | ||||||||||||||
| "480p": (854, 480), | ||||||||||||||
| "720p": (1280, 720), | ||||||||||||||
| "1080p": (1920, 1080), | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| def get_camera_command(): | ||||||||||||||
| """ | ||||||||||||||
|
|
@@ -28,30 +41,49 @@ def get_camera_command(): | |||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| def start_stream(ffmpeg_url, width=854, height=480): | ||||||||||||||
| def start_stream(ffmpeg_url, width=854, height=480, rotation=0, framerate=15, timestamp_overlay=False): | ||||||||||||||
| """ | ||||||||||||||
| Starts the libcamera -> ffmpeg pipeline and returns two Popen objects: | ||||||||||||||
| p1: camera process (rpicam-vid or libcamera-vid) | ||||||||||||||
| p2: ffmpeg process | ||||||||||||||
|
|
||||||||||||||
| Args: | ||||||||||||||
| ffmpeg_url: RTMP URL for streaming | ||||||||||||||
| width: Output width in pixels (final output after rotation) | ||||||||||||||
| height: Output height in pixels (final output after rotation) | ||||||||||||||
| rotation: Rotation angle (0, 90, 180, 270 degrees clockwise) | ||||||||||||||
| framerate: Frame rate in fps | ||||||||||||||
| timestamp_overlay: Whether to show timestamp on video | ||||||||||||||
| """ | ||||||||||||||
| # Get the available camera command | ||||||||||||||
| camera_cmd = get_camera_command() | ||||||||||||||
|
|
||||||||||||||
| # Camera always captures in landscape orientation using the full sensor. | ||||||||||||||
| # For portrait output (90/270 rotation), we capture landscape and rotate in ffmpeg. | ||||||||||||||
| # This preserves the full field of view instead of cropping. | ||||||||||||||
| # | ||||||||||||||
| # For 90/270 rotation: capture at height x width (landscape), rotate to width x height (portrait) | ||||||||||||||
| # For 0/180 rotation: capture at width x height (landscape), output same orientation | ||||||||||||||
| if rotation in (90, 270): | ||||||||||||||
| # For portrait output, capture in landscape (swap dimensions for camera) | ||||||||||||||
| # Camera captures height x width, then ffmpeg rotates to width x height | ||||||||||||||
| cam_width, cam_height = height, width | ||||||||||||||
| else: | ||||||||||||||
| cam_width, cam_height = width, height | ||||||||||||||
|
|
||||||||||||||
| # First: camera command with core parameters | ||||||||||||||
| libcamera_cmd = [ | ||||||||||||||
| camera_cmd, | ||||||||||||||
| "--inline", | ||||||||||||||
| "--nopreview", | ||||||||||||||
| "-t", | ||||||||||||||
| "0", | ||||||||||||||
| "--mode", | ||||||||||||||
| "1536:864", # A known 16:9 sensor mode | ||||||||||||||
| "--width", | ||||||||||||||
| str(width), # Scale width | ||||||||||||||
| str(cam_width), # Scale width | ||||||||||||||
| "--height", | ||||||||||||||
| str(height), # Scale height | ||||||||||||||
| str(cam_height), # Scale height | ||||||||||||||
| "--framerate", | ||||||||||||||
| "15", # Frame rate | ||||||||||||||
| str(framerate), # Frame rate | ||||||||||||||
| "--codec", | ||||||||||||||
| "h264", # H.264 encoding | ||||||||||||||
| "--bitrate", | ||||||||||||||
|
|
@@ -67,6 +99,37 @@ def start_stream(ffmpeg_url, width=854, height=480): | |||||||||||||
| # Add output parameters last | ||||||||||||||
| libcamera_cmd.extend(["-o", "-"]) # Output to stdout (pipe) | ||||||||||||||
|
|
||||||||||||||
| # Build video filter chain for ffmpeg | ||||||||||||||
| video_filters = [] | ||||||||||||||
|
|
||||||||||||||
| # Add rotation filter if needed | ||||||||||||||
| if rotation == 90: | ||||||||||||||
| video_filters.append("transpose=1") # 90 degrees clockwise | ||||||||||||||
| elif rotation == 180: | ||||||||||||||
| video_filters.append("hflip,vflip") # 180 degrees | ||||||||||||||
| elif rotation == 270: | ||||||||||||||
| video_filters.append("transpose=2") # 90 degrees counter-clockwise (270 clockwise) | ||||||||||||||
|
|
||||||||||||||
| # Add timestamp overlay if enabled | ||||||||||||||
| # Format: YYYY-MM-DD_HH-MM-SS (updates every second) | ||||||||||||||
|
||||||||||||||
| # Format: YYYY-MM-DD_HH-MM-SS (updates every second) | |
| # Format: YYYY-MM-DD_HH-MM-SS (ffmpeg's localtime evaluates per-frame, so the overlay updates once per second at typical framerates) |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timestamp filter's fontsize calculation uses the original height parameter, but when rotation is 90 or 270 degrees, this refers to the pre-rotation dimension. After rotation, the actual video height will be different. This could result in incorrectly sized text.
For portrait mode (90/270 rotation), the actual video height after rotation will be width, so the fontsize should be calculated based on the final rotated dimensions, not the input parameters.
Consider calculating fontsize based on the actual output dimensions:
# Calculate fontsize based on actual output dimensions after rotation
if rotation in (90, 270):
actual_height = width # After rotation, width becomes height
else:
actual_height = height
fontsize = max(16, actual_height // 20)| fontsize = max(16, height // 20) | |
| if rotation in (90, 270): | |
| actual_height = width | |
| else: | |
| actual_height = height | |
| fontsize = max(16, actual_height // 20) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changelog entry mentions "display date/time with seconds" but the actual format is
YYYY-MM-DD_HH-MM-SS, which uses dashes instead of colons for the time portion. This is a technical detail but could be misleading.Consider updating to be more precise: "Timestamp overlay setting in
my_secrets_example.pyto display date/time (format: YYYY-MM-DD_HH-MM-SS) on video stream."