Skip to content

Commit 29f58d6

Browse files
committed
Add end-to-end streaming flow, metrics, launches, and docs
1 parent 20dcea0 commit 29f58d6

25 files changed

Lines changed: 1335 additions & 27 deletions

CMakeLists.txt

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
55
add_compile_options(-Wall -Wextra -Wpedantic)
66
endif()
77

8-
set(CMAKE_CXX_STANDARD 17)
8+
set(CMAKE_CXX_STANDARD 20)
99
set(CMAKE_CXX_STANDARD_REQUIRED ON)
10+
set(CMAKE_CXX_EXTENSIONS OFF)
1011

1112
option(ROS2_GST_VIDEO_BRIDGE_ENABLE_CLANG_TIDY "Enable clang-tidy during C++ compilation" ON)
1213
option(ROS2_GST_VIDEO_BRIDGE_ENABLE_CLANG_FORMAT_CHECK "Enable clang-format check target" ON)
14+
option(ROS2_GST_VIDEO_BRIDGE_ENABLE_AMENT_LINT "Enable ament_lint_auto checks" OFF)
1315

1416
if(ROS2_GST_VIDEO_BRIDGE_ENABLE_CLANG_TIDY)
1517
find_program(CLANG_TIDY_EXE NAMES clang-tidy)
@@ -23,6 +25,10 @@ endif()
2325

2426
find_package(ament_cmake REQUIRED)
2527
find_package(rclcpp REQUIRED)
28+
find_package(sensor_msgs REQUIRED)
29+
find_package(std_msgs REQUIRED)
30+
find_package(PkgConfig REQUIRED)
31+
pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0 gstreamer-app-1.0)
2632

2733
if(ROS2_GST_VIDEO_BRIDGE_ENABLE_CLANG_FORMAT_CHECK)
2834
find_program(CLANG_FORMAT_EXE NAMES clang-format)
@@ -57,10 +63,19 @@ add_executable(gst_video_bridge_node
5763
target_include_directories(gst_video_bridge_node
5864
PRIVATE
5965
include
66+
${GSTREAMER_INCLUDE_DIRS}
6067
)
6168

69+
target_link_libraries(gst_video_bridge_node
70+
${GSTREAMER_LIBRARIES}
71+
)
72+
73+
target_compile_options(gst_video_bridge_node PRIVATE ${GSTREAMER_CFLAGS_OTHER})
74+
6275
ament_target_dependencies(gst_video_bridge_node
6376
rclcpp
77+
sensor_msgs
78+
std_msgs
6479
)
6580

6681
install(TARGETS gst_video_bridge_node
@@ -72,8 +87,45 @@ install(DIRECTORY launch config
7287
)
7388

7489
if(BUILD_TESTING)
75-
find_package(ament_lint_auto REQUIRED)
76-
ament_lint_auto_find_test_dependencies()
90+
find_package(ament_cmake_gtest REQUIRED)
91+
92+
ament_add_gtest(test_config_loader test/test_config_loader.cpp src/core/config_loader.cc)
93+
target_include_directories(test_config_loader PRIVATE include)
94+
ament_target_dependencies(test_config_loader rclcpp)
95+
96+
ament_add_gtest(test_pipeline_builder test/test_pipeline_builder.cpp src/core/pipeline_builder.cc)
97+
target_include_directories(test_pipeline_builder PRIVATE include)
98+
99+
add_test(
100+
NAME smoke_runtime_validate_config
101+
COMMAND $<TARGET_FILE:gst_video_bridge_node> --ros-args -p runtime.mode:=validate_config)
102+
103+
add_test(
104+
NAME smoke_runtime_list_capabilities
105+
COMMAND $<TARGET_FILE:gst_video_bridge_node> --ros-args -p runtime.mode:=list_capabilities)
106+
107+
add_test(
108+
NAME smoke_launch_minimal_show_args
109+
COMMAND ros2 launch ros2_gst_video_bridge gst_video_bridge_minimal.launch.py --show-args)
110+
111+
add_test(
112+
NAME smoke_launch_advanced_show_args
113+
COMMAND ros2 launch ros2_gst_video_bridge gst_video_bridge_advanced.launch.py --show-args)
114+
115+
add_test(
116+
NAME smoke_launch_compat_show_args
117+
COMMAND ros2 launch ros2_gst_video_bridge gst_video_bridge.launch.py --show-args)
118+
119+
set_tests_properties(smoke_runtime_validate_config PROPERTIES TIMEOUT 20)
120+
set_tests_properties(smoke_runtime_list_capabilities PROPERTIES TIMEOUT 20)
121+
set_tests_properties(smoke_launch_minimal_show_args PROPERTIES TIMEOUT 20)
122+
set_tests_properties(smoke_launch_advanced_show_args PROPERTIES TIMEOUT 20)
123+
set_tests_properties(smoke_launch_compat_show_args PROPERTIES TIMEOUT 20)
124+
125+
if(ROS2_GST_VIDEO_BRIDGE_ENABLE_AMENT_LINT)
126+
find_package(ament_lint_auto REQUIRED)
127+
ament_lint_auto_find_test_dependencies()
128+
endif()
77129
endif()
78130

79131
ament_package()

README.md

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,124 @@ source install/setup.bash
7070
ros2 launch ros2_gst_video_bridge gst_video_bridge.launch.py
7171
```
7272

73+
## End-to-End Basler USB Test (SRT Listener -> Caller)
74+
75+
This section documents the validated launch flow for a Basler USB camera with:
76+
77+
- camera node publishing ROS images,
78+
- bridge node running SRT in listener mode,
79+
- receiver running as SRT caller.
80+
81+
### 0) One-time USB udev rule for Basler
82+
83+
```bash
84+
sudo tee /etc/udev/rules.d/99-basler-usb.rules >/dev/null <<'EOF'
85+
SUBSYSTEM=="usb", ATTR{idVendor}=="2676", MODE="0666", GROUP="plugdev"
86+
EOF
87+
88+
sudo udevadm control --reload-rules
89+
sudo udevadm trigger
90+
```
91+
92+
Reconnect the USB camera (or reboot) after applying the rule.
93+
94+
### 1) Verify camera detection
95+
96+
```bash
97+
cd /home/ccu-001/ws_dev
98+
source /opt/ros/humble/setup.zsh
99+
source install/setup.zsh
100+
101+
ros2 run camera_aravis2 camera_finder
102+
```
103+
104+
### 2) Launch camera node (Terminal A)
105+
106+
```bash
107+
source /opt/ros/humble/setup.zsh
108+
source /home/ccu-001/ws_dev/install/setup.zsh
109+
ros2 launch camera_aravis2 camera_driver_uv_example.launch.py guid:=Basler-2676016350B6-23285942
110+
```
111+
112+
### 3) Launch bridge node in SRT listener mode (Terminal B)
113+
114+
```bash
115+
source /opt/ros/humble/setup.zsh
116+
source /home/ccu-001/ws_dev/install/setup.zsh
117+
ros2 launch ros2_gst_video_bridge gst_video_bridge_minimal.launch.py \
118+
input_topic:=/camera_driver_uv_example/vis/image_raw \
119+
"sink_uri:=srt://0.0.0.0:9000?mode=listener"
120+
```
121+
122+
### 4) Launch SRT receiver as caller (Terminal C)
123+
124+
```bash
125+
gst-launch-1.0 -v srtsrc uri="srt://1.0.0.22:9000?mode=caller" latency=60 ! tsdemux ! h264parse ! avdec_h264 ! videoconvert ! autovideosink sync=false
126+
```
127+
128+
### 5) Quick runtime checks
129+
130+
Check that the camera topic is publishing:
131+
132+
```bash
133+
source /opt/ros/humble/setup.zsh
134+
source /home/ccu-001/ws_dev/install/setup.zsh
135+
ros2 topic hz /camera_driver_uv_example/vis/image_raw
136+
```
137+
138+
Check bridge runtime metrics:
139+
140+
```bash
141+
source /opt/ros/humble/setup.zsh
142+
source /home/ccu-001/ws_dev/install/setup.zsh
143+
ros2 topic echo /gst_video_bridge/runtime_metrics --once
144+
```
145+
146+
If `fps_in` and `fps_out` are both greater than zero, the bridge is actively forwarding frames.
147+
148+
### Launch files
149+
150+
- `gst_video_bridge_minimal.launch.py`:
151+
- essential arguments only (`profile_machine`, `profile_stream`, `input_topic`, `sink_uri`)
152+
- `gst_video_bridge_advanced.launch.py`:
153+
- full override surface for transport/codec/runtime plus `params_file`
154+
- `gst_video_bridge.launch.py`:
155+
- compatibility wrapper to the advanced launch
156+
157+
Examples:
158+
159+
```bash
160+
ros2 launch ros2_gst_video_bridge gst_video_bridge_minimal.launch.py \
161+
profile_machine:=jetson profile_stream:=low_latency \
162+
input_topic:=/camera_driver_uv/vis/image_raw \
163+
sink_uri:=srt://127.0.0.1:9000?mode=listener
164+
165+
ros2 launch ros2_gst_video_bridge gst_video_bridge_advanced.launch.py \
166+
params_file:=/home/ccu-001/ws_dev/src/ros2_gst_video_bridge/config/profiles/jetson_monitoring_udp.yaml
167+
```
168+
169+
### Curated profile files
170+
171+
Under `config/profiles/`:
172+
173+
- `jetson_low_latency_srt.yaml`
174+
- `x86_low_latency_srt.yaml`
175+
- `raspi_low_latency_srt.yaml`
176+
- `jetson_monitoring_udp.yaml`
177+
- `x86_monitoring_udp.yaml`
178+
- `raspi_monitoring_udp.yaml`
179+
- `recording_file_sink.yaml`
180+
181+
### Runtime metrics
182+
183+
The node publishes runtime metrics on `~/runtime_metrics` (`std_msgs/msg/String`), including:
184+
185+
- state (`connecting|streaming|degraded|reconnecting|failed`)
186+
- `fps_in`, `fps_out`
187+
- dropped frame counters
188+
- reconnect counter
189+
- latency estimate in milliseconds
190+
73191
### Discoverability modes (Phase 3)
74192

75193
- List ROS image topics visible on the host:
@@ -98,5 +216,5 @@ ros2 run ros2_gst_video_bridge gst_video_bridge_node --ros-args -p runtime.mode:
98216

99217
## Current Status
100218

101-
This is a professional starter scaffold with formatting/linting and a minimal ROS 2 node.
102-
The next implementation milestone is wiring ROS image subscriptions into a real GStreamer `appsrc` pipeline.
219+
The node now supports ROS image ingestion into a real GStreamer `appsrc` pipeline with runtime modes,
220+
reconnection policy, and profile-driven launch/configuration.

config/default_params.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ gst_video_bridge:
66
input_topic: /camera_driver_uv/vis/image_raw
77

88
transport.kind: srt
9-
transport.sink_uri: srt://127.0.0.1:9000?mode=caller
9+
transport.sink_uri: srt://127.0.0.1:9000?mode=listener
1010
transport.latency_ms: 60
1111
transport.reconnect.enabled: true
1212
transport.reconnect.interval_ms: 1000
@@ -23,7 +23,7 @@ gst_video_bridge:
2323
gst.transport: srt
2424
gst.codec: h264
2525
gst.profile: baseline
26-
gst.sink_uri: srt://127.0.0.1:9000?mode=caller
26+
gst.sink_uri: srt://127.0.0.1:9000?mode=listener
2727
gst.bitrate_kbps: 2000
2828
gst.latency_ms: 60
2929
gst.pipeline_override: ""
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
gst_video_bridge:
2+
ros__parameters:
3+
profile.machine: jetson
4+
profile.stream: low_latency
5+
6+
input_topic: /camera_driver_uv/vis/image_raw
7+
8+
transport.kind: srt
9+
transport.sink_uri: srt://127.0.0.1:9000?mode=listener
10+
transport.latency_ms: 60
11+
transport.reconnect.enabled: true
12+
transport.reconnect.interval_ms: 1000
13+
transport.reconnect.max_attempts: 0
14+
15+
codec.name: h264
16+
codec.profile: baseline
17+
codec.tune: zerolatency
18+
codec.rate_control: cbr
19+
codec.bitrate_kbps: 2500
20+
codec.gop: 30
21+
22+
max_fps: 30.0
23+
use_wall_clock_timestamps: false
24+
runtime.mode: stream
25+
runtime.print_effective_config: true
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
gst_video_bridge:
2+
ros__parameters:
3+
profile.machine: jetson
4+
profile.stream: monitoring_udp
5+
6+
input_topic: /camera_driver_uv/vis/image_raw
7+
8+
transport.kind: udp
9+
transport.sink_uri: udp://127.0.0.1:5000
10+
transport.latency_ms: 40
11+
transport.reconnect.enabled: false
12+
transport.reconnect.interval_ms: 1000
13+
transport.reconnect.max_attempts: 0
14+
15+
codec.name: h264
16+
codec.profile: baseline
17+
codec.tune: zerolatency
18+
codec.rate_control: cbr
19+
codec.bitrate_kbps: 2000
20+
codec.gop: 30
21+
22+
max_fps: 20.0
23+
use_wall_clock_timestamps: false
24+
runtime.mode: stream
25+
runtime.print_effective_config: true
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
gst_video_bridge:
2+
ros__parameters:
3+
profile.machine: raspi
4+
profile.stream: low_latency
5+
6+
input_topic: /camera/image_raw
7+
8+
transport.kind: srt
9+
transport.sink_uri: srt://127.0.0.1:9000?mode=listener
10+
transport.latency_ms: 80
11+
transport.reconnect.enabled: true
12+
transport.reconnect.interval_ms: 1000
13+
transport.reconnect.max_attempts: 0
14+
15+
codec.name: h264
16+
codec.profile: main
17+
codec.tune: zerolatency
18+
codec.rate_control: cbr
19+
codec.bitrate_kbps: 1800
20+
codec.gop: 30
21+
22+
max_fps: 25.0
23+
use_wall_clock_timestamps: false
24+
runtime.mode: stream
25+
runtime.print_effective_config: true
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
gst_video_bridge:
2+
ros__parameters:
3+
profile.machine: raspi
4+
profile.stream: monitoring_udp
5+
6+
input_topic: /camera/image_raw
7+
8+
transport.kind: udp
9+
transport.sink_uri: udp://127.0.0.1:5000
10+
transport.latency_ms: 60
11+
transport.reconnect.enabled: false
12+
transport.reconnect.interval_ms: 1000
13+
transport.reconnect.max_attempts: 0
14+
15+
codec.name: h264
16+
codec.profile: main
17+
codec.tune: zerolatency
18+
codec.rate_control: cbr
19+
codec.bitrate_kbps: 1500
20+
codec.gop: 30
21+
22+
max_fps: 15.0
23+
use_wall_clock_timestamps: false
24+
runtime.mode: stream
25+
runtime.print_effective_config: true
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
gst_video_bridge:
2+
ros__parameters:
3+
profile.machine: generic
4+
profile.stream: high_quality
5+
6+
input_topic: /camera/image_raw
7+
8+
transport.kind: file
9+
transport.sink_uri: /tmp/ros2_gst_video_bridge_recording.ts
10+
transport.latency_ms: 0
11+
transport.reconnect.enabled: false
12+
transport.reconnect.interval_ms: 1000
13+
transport.reconnect.max_attempts: 0
14+
15+
codec.name: h264
16+
codec.profile: high
17+
codec.tune: zerolatency
18+
codec.rate_control: cbr
19+
codec.bitrate_kbps: 5000
20+
codec.gop: 60
21+
22+
max_fps: 30.0
23+
use_wall_clock_timestamps: false
24+
runtime.mode: stream
25+
runtime.print_effective_config: true

0 commit comments

Comments
 (0)