Skip to content

Commit ac29602

Browse files
committed
Refactor node layout, enable clang checks, and format code
1 parent 29f58d6 commit ac29602

35 files changed

Lines changed: 2285 additions & 913 deletions

.github/workflows/ci.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: ros2-gst-video-bridge-ci
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: ["main", "ft/1stversion_w_srt"]
7+
schedule:
8+
- cron: "0 3 * * *"
9+
10+
jobs:
11+
build-and-test:
12+
runs-on: ubuntu-22.04
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
17+
- name: Setup ROS 2 Humble
18+
uses: ros-tooling/setup-ros@v0.7
19+
with:
20+
required-ros-distributions: humble
21+
22+
- name: Build
23+
run: |
24+
source /opt/ros/humble/setup.bash
25+
colcon build --event-handlers console_direct+
26+
27+
- name: Test
28+
run: |
29+
source /opt/ros/humble/setup.bash
30+
colcon test --event-handlers console_direct+
31+
colcon test-result --all --verbose
32+
33+
nightly-matrix:
34+
if: github.event_name == 'schedule'
35+
runs-on: ubuntu-22.04
36+
steps:
37+
- name: Checkout
38+
uses: actions/checkout@v4
39+
40+
- name: Setup ROS 2 Humble
41+
uses: ros-tooling/setup-ros@v0.7
42+
with:
43+
required-ros-distributions: humble
44+
45+
- name: Build
46+
run: |
47+
source /opt/ros/humble/setup.bash
48+
colcon build --event-handlers console_direct+
49+
50+
- name: Run matrix
51+
run: |
52+
chmod +x src/ros2_gst_video_bridge/scripts/run_transport_codec_matrix.zsh
53+
src/ros2_gst_video_bridge/scripts/run_transport_codec_matrix.zsh "$PWD" /tmp/matrix.csv
54+
55+
- name: Upload report
56+
uses: actions/upload-artifact@v4
57+
with:
58+
name: transport-codec-matrix-report
59+
path: /tmp/matrix.csv

CMakeLists.txt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ option(ROS2_GST_VIDEO_BRIDGE_ENABLE_CLANG_FORMAT_CHECK "Enable clang-format chec
1414
option(ROS2_GST_VIDEO_BRIDGE_ENABLE_AMENT_LINT "Enable ament_lint_auto checks" OFF)
1515

1616
if(ROS2_GST_VIDEO_BRIDGE_ENABLE_CLANG_TIDY)
17-
find_program(CLANG_TIDY_EXE NAMES clang-tidy)
17+
find_program(CLANG_TIDY_EXE NAMES clang-tidy clang-tidy-18 clang-tidy-17 clang-tidy-16 clang-tidy-15)
1818
if(CLANG_TIDY_EXE)
1919
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE})
2020
message(STATUS "clang-tidy enabled: ${CLANG_TIDY_EXE}")
@@ -27,11 +27,12 @@ find_package(ament_cmake REQUIRED)
2727
find_package(rclcpp REQUIRED)
2828
find_package(sensor_msgs REQUIRED)
2929
find_package(std_msgs REQUIRED)
30+
find_package(ros2_gst_video_bridge_msgs REQUIRED)
3031
find_package(PkgConfig REQUIRED)
3132
pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0 gstreamer-app-1.0)
3233

3334
if(ROS2_GST_VIDEO_BRIDGE_ENABLE_CLANG_FORMAT_CHECK)
34-
find_program(CLANG_FORMAT_EXE NAMES clang-format)
35+
find_program(CLANG_FORMAT_EXE NAMES clang-format clang-format-18 clang-format-17 clang-format-16 clang-format-15)
3536
if(CLANG_FORMAT_EXE)
3637
file(GLOB_RECURSE ROS2_GST_VIDEO_BRIDGE_FORMAT_FILES CONFIGURE_DEPENDS
3738
"${CMAKE_CURRENT_SOURCE_DIR}/include/*.h"
@@ -52,6 +53,13 @@ endif()
5253
add_executable(gst_video_bridge_node
5354
src/main.cc
5455
src/gst_video_bridge_node.cc
56+
src/node/core.cc
57+
src/node/lifecycle.cc
58+
src/node/modes.cc
59+
src/node/streaming.cc
60+
src/node/recovery.cc
61+
src/node/fallback.cc
62+
src/node/observability.cc
5563
src/core/config_loader.cc
5664
src/core/pipeline_builder.cc
5765
src/runtime/capability_probe.cc
@@ -76,6 +84,7 @@ ament_target_dependencies(gst_video_bridge_node
7684
rclcpp
7785
sensor_msgs
7886
std_msgs
87+
ros2_gst_video_bridge_msgs
7988
)
8089

8190
install(TARGETS gst_video_bridge_node

README.md

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Generic ROS 2 video bridge that subscribes to a raw `sensor_msgs/Image` topic an
2525
- `transport.reconnect.max_attempts`
2626

2727
- Codec parameters:
28-
- `codec.name` (`h264`, `h265`, `mjpeg`)
28+
- `codec.name` (`auto`, `h264`, `h265`, `mjpeg`)
2929
- `codec.profile`
3030
- `codec.tune`
3131
- `codec.rate_control`
@@ -119,6 +119,16 @@ ros2 launch ros2_gst_video_bridge gst_video_bridge_minimal.launch.py \
119119
"sink_uri:=srt://0.0.0.0:9000?mode=listener"
120120
```
121121

122+
For Bayer cameras, enable optional debayer (color output before bridge):
123+
124+
```bash
125+
ros2 launch ros2_gst_video_bridge gst_video_bridge_minimal.launch.py \
126+
input_topic:=/camera_driver_uv_example/vis/image_raw \
127+
enable_debayer:=true \
128+
debayer_output_topic:=/camera_driver_uv_example/vis/image_color \
129+
"sink_uri:=srt://0.0.0.0:9000?mode=listener"
130+
```
131+
122132
### 4) Launch SRT receiver as caller (Terminal C)
123133

124134
```bash
@@ -180,13 +190,101 @@ Under `config/profiles/`:
180190

181191
### Runtime metrics
182192

183-
The node publishes runtime metrics on `~/runtime_metrics` (`std_msgs/msg/String`), including:
193+
The node publishes runtime metrics on `~/runtime_metrics` (`std_msgs/msg/String`) for backward compatibility, and typed control-plane topics via `ros2_gst_video_bridge_msgs`:
194+
195+
- `~/runtime_status` (`ros2_gst_video_bridge_msgs/msg/RuntimeStatus`)
196+
- `~/runtime_events` (`ros2_gst_video_bridge_msgs/msg/RuntimeEvent`)
197+
- `~/set_streaming_profile` (`ros2_gst_video_bridge_msgs/srv/SetStreamingProfile`)
198+
199+
Fields include:
184200

185201
- state (`connecting|streaming|degraded|reconnecting|failed`)
186202
- `fps_in`, `fps_out`
187203
- dropped frame counters
188204
- reconnect counter
189205
- latency estimate in milliseconds
206+
- selected codec/encoder and fallback flags
207+
- adaptation profile and current adaptation level
208+
209+
Operator runtime command example:
210+
211+
```bash
212+
ros2 service call /gst_video_bridge/set_streaming_profile ros2_gst_video_bridge_msgs/srv/SetStreamingProfile "{adaptation_profile: balanced, reset_counters: false}"
213+
```
214+
215+
First-failure snapshot:
216+
217+
- On the first streaming failure in a session, the bridge publishes `FIRST_FAILURE_SNAPSHOT` on `~/runtime_events`.
218+
- Payload includes session/stream IDs, runtime state, selected codec/encoder, sink URI, fallback status, and effective pipeline string.
219+
220+
### Automatic codec selection (`codec.name:=auto`)
221+
222+
When `codec.name` is set to `auto`, the node inspects available encoder implementations via
223+
`gst-inspect-1.0` and resolves to the best codec for the selected machine profile.
224+
225+
Selection order by machine profile:
226+
227+
| Machine profile | Preferred implementation classes |
228+
|---|---|
229+
| `jetson` | `hw:nvidia-v4l2` -> `hw:nvidia` -> other HW (`v4l2`/`omx`/`vaapi`) -> `sw` |
230+
| `x86` | `hw:vaapi` -> `hw:v4l2` -> NVIDIA HW -> `sw` |
231+
| `raspi` | `hw:v4l2` -> `hw:omx` -> `sw` |
232+
| `generic` | `hw:vaapi` -> `hw:v4l2` -> NVIDIA HW -> `hw:omx` -> `sw` |
233+
234+
Codec tie-break preference is stable: `h264` -> `h265` -> `mjpeg`.
235+
236+
If `codec.name:=auto` picks a hardware encoder and runtime fails repeatedly while streaming,
237+
the bridge now falls back automatically to a software encoder for the same codec.
238+
This fallback is cross-platform (Jetson/x86/Raspberry/generic) and uses detected encoders from
239+
`gst-inspect-1.0`, not Jetson-only logic.
240+
241+
Fallback sensitivity can be tuned with:
242+
243+
```bash
244+
-p runtime.hw_fallback_failures:=3
245+
```
246+
247+
Adaptive resilience controls:
248+
249+
```bash
250+
-p runtime.adaptation.enabled:=true \
251+
-p runtime.adaptation.profile:=balanced \
252+
-p runtime.adaptation.interval_ms:=2000 \
253+
-p runtime.adaptation.cooldown_ms:=5000
254+
```
255+
256+
Supported adaptation profiles:
257+
258+
- `conservative`
259+
- `balanced`
260+
- `aggressive`
261+
262+
### Validation automation
263+
264+
Codec/transport matrix script:
265+
266+
```bash
267+
chmod +x /home/ccu-001/ws_dev/src/ros2_gst_video_bridge/scripts/run_transport_codec_matrix.zsh
268+
/home/ccu-001/ws_dev/src/ros2_gst_video_bridge/scripts/run_transport_codec_matrix.zsh /home/ccu-001/ws_dev /tmp/matrix.csv
269+
```
270+
271+
Soak run script:
272+
273+
```bash
274+
chmod +x /home/ccu-001/ws_dev/src/ros2_gst_video_bridge/scripts/run_soak_profile.zsh
275+
/home/ccu-001/ws_dev/src/ros2_gst_video_bridge/scripts/run_soak_profile.zsh /home/ccu-001/ws_dev 1800 generic low_latency /camera/image_raw
276+
```
277+
278+
Release/versioning policy documents:
279+
280+
- `docs/VERSIONING.md`
281+
- `docs/RELEASE.md`
282+
283+
Example:
284+
285+
```bash
286+
ros2 run ros2_gst_video_bridge gst_video_bridge_node --ros-args -p codec.name:=auto
287+
```
190288

191289
### Discoverability modes (Phase 3)
192290

docs/RELEASE.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Release Gate
2+
3+
## Required Gates
4+
5+
1. PR gate
6+
- Build + unit tests + smoke tests must pass.
7+
8+
2. Nightly gate
9+
- Transport/codec matrix script must run and generate report.
10+
11+
3. Release gate
12+
- Soak run >= 30 minutes per target machine class (Jetson/x86/Raspi/generic as available).
13+
14+
## KPIs
15+
16+
Release candidate is blocked if any threshold regresses vs previous stable:
17+
- reconnects/hour
18+
- dropped frames ratio
19+
- startup success ratio
20+
- median and p95 latency estimate
21+
22+
## Rollback
23+
24+
- Keep previous release profile bundle and parameter files.
25+
- Revert to last stable tag and documented config migration pair.

docs/VERSIONING.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Versioning Policy
2+
3+
## Scope
4+
5+
This policy applies to:
6+
- Runtime node behavior and parameters
7+
- Pipeline profile semantics
8+
- Interface contracts in ros2_gst_video_bridge_msgs
9+
10+
## SemVer Rules
11+
12+
- MAJOR: incompatible contract change (message field removals, parameter removals/renames without migration support).
13+
- MINOR: backward-compatible additions (new parameters with defaults, new message fields preserving old behavior).
14+
- PATCH: bug fixes and performance/stability improvements without contract break.
15+
16+
## Contract Stability
17+
18+
- `runtime_status` and `runtime_events` topics are versioned by package version.
19+
- Config schema is tracked as `v1alpha`, then `v1` on first production release.
20+
- Deprecated fields require at least one MINOR release overlap before removal.

include/ros2_gst_video_bridge/core/config_loader.hpp

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,19 @@
1111
#include <string>
1212
#include <vector>
1313

14-
namespace ros2_gst_video_bridge
15-
{
14+
namespace ros2_gst_video_bridge {
1615

17-
class ConfigLoader
18-
{
16+
class ConfigLoader {
1917
public:
20-
static GstBridgeConfig loadFromNode(rclcpp::Node & node);
21-
static std::vector<std::string> validate(const GstBridgeConfig & config);
22-
static std::string toDebugString(const GstBridgeConfig & config);
18+
static GstBridgeConfig loadFromNode(rclcpp::Node& node);
19+
static std::vector<std::string> validate(const GstBridgeConfig& config);
20+
static std::string toDebugString(const GstBridgeConfig& config);
2321

2422
private:
25-
static void applyMachineProfileDefaults(GstBridgeConfig & config);
26-
static void applyStreamProfileDefaults(GstBridgeConfig & config);
23+
static void applyMachineProfileDefaults(GstBridgeConfig& config);
24+
static void applyStreamProfileDefaults(GstBridgeConfig& config);
2725
};
2826

29-
} // namespace ros2_gst_video_bridge
27+
} // namespace ros2_gst_video_bridge
3028

31-
#endif // ROS2_GST_VIDEO_BRIDGE__CORE__CONFIG_LOADER_HPP_
29+
#endif // ROS2_GST_VIDEO_BRIDGE__CORE__CONFIG_LOADER_HPP_

0 commit comments

Comments
 (0)