Enhance Dockerfile and launch configuration for improved logging and UDP transport; update runtime dependencies and remove unnecessary dummy frames in video processing

This commit is contained in:
Akash Shingha 2026-03-12 14:43:17 +02:00
parent 5fd57decc9
commit f151a78ebd
8 changed files with 54 additions and 27 deletions

View File

@ -35,7 +35,11 @@ RUN apt-get update \
COPY --from=build /ws/install /ws/install
COPY docker-entrypoint.sh /ros_entrypoint.sh
RUN chmod +x /ros_entrypoint.sh
# Bake FastDDS UDP-only profile into the image so the shared-memory transport
# fix applies regardless of how the container is started (compose or docker run).
COPY fastdds_no_shm.xml /ws/fastdds_no_shm.xml
ENV ROS_DISTRO=humble
ENV FASTRTPS_DEFAULT_PROFILES_FILE=/ws/fastdds_no_shm.xml
## Make ROS and workspace overlays available for interactive shells
RUN echo "source /opt/ros/${ROS_DISTRO}/setup.bash || true" > /etc/profile.d/ros2.sh \

View File

@ -3,6 +3,7 @@ build-essential
cmake
pkg-config
libopencv-dev
python3-opencv
python3-rosdep
python3-rosdistro
software-properties-common

View File

@ -1,6 +1,11 @@
libgstreamer1.0-0
gstreamer1.0-plugins-base
gstreamer1.0-plugins-good
gstreamer1.0-plugins-bad
gstreamer1.0-plugins-ugly
libgstrtspserver-1.0-0
python3-opencv
ros-humble-ros2cli
ros-humble-rclpy
net-tools
iputils-ping

View File

@ -12,10 +12,11 @@ services:
- RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-}
# GStreamer debug level (uncomment for verbose GStreamer logging)
# - GST_DEBUG=${GST_DEBUG:-}
# FASTRTPS_DEFAULT_PROFILES_FILE is set in the Dockerfile (baked into image)
# Ensure the node binds to non-localhost interface (disable local_only)
# and subscribe to the actual image topic available on the host
command: ["image2rtsp.launch.py", "local_only:=false", "topic:=/camera/image"]
command: ["image2rtsp.launch.py", "local_only:=false", "topic:=/camera/image", "log_level:=debug"]
restart: unless-stopped

21
fastdds_no_shm.xml Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Force UDP transport only.
Shared memory transport fails between host and Docker container (different
PID namespaces) even with network_mode: host. Disabling it forces FastDDS
to use UDPv4, which works correctly over the loopback interface. -->
<profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">
<transport_descriptors>
<transport_descriptor>
<transport_id>UDPv4Transport</transport_id>
<type>UDPv4</type>
</transport_descriptor>
</transport_descriptors>
<participant profile_name="default_participant" is_default_profile="true">
<rtps>
<userTransports>
<transport_id>UDPv4Transport</transport_id>
</userTransports>
<useBuiltinTransports>false</useBuiltinTransports>
</rtps>
</participant>
</profiles>

View File

@ -1,6 +1,8 @@
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
def generate_launch_description():
@ -10,13 +12,19 @@ def generate_launch_description():
'parameters.yaml'
)
log_level = LaunchConfiguration('log_level')
return LaunchDescription([
DeclareLaunchArgument(
'log_level',
default_value='warn',
description='ROS logger level (debug, info, warn, error, fatal)'
),
Node(
package='image2rtsp',
executable='image2rtsp',
name='image2rtsp',
parameters=[config],
# Reduce runtime logging verbosity to WARN to avoid info spam in container logs
arguments=['--ros-args', '--log-level', 'warn']
arguments=['--ros-args', '--log-level', log_level]
)
])

View File

@ -69,7 +69,13 @@ Image2rtsp::Image2rtsp() : Node("image2rtsp"){
framerate = extract_framerate(pipeline, 30);
rtsp_server_add_url(mountpoint.c_str(), pipeline.c_str());
RCLCPP_DEBUG(this->get_logger(), "Stream available at rtsp://%s:%s%s", gst_rtsp_server_get_address(rtsp_server), port.c_str(), mountpoint.c_str());
const char *server_address = gst_rtsp_server_get_address(rtsp_server);
if (local_only) {
RCLCPP_DEBUG(this->get_logger(), "Stream available at rtsp://%s:%s%s", server_address, port.c_str(), mountpoint.c_str());
} else {
RCLCPP_DEBUG(this->get_logger(), "RTSP server bound to %s:%s%s", server_address, port.c_str(), mountpoint.c_str());
RCLCPP_DEBUG(this->get_logger(), "Connect clients using rtsp://<host-ip>:%s%s (0.0.0.0 is bind-only)", port.c_str(), mountpoint.c_str());
}
}
uint Image2rtsp::extract_framerate(const std::string& pipeline, uint default_framerate = 30) {

View File

@ -87,29 +87,9 @@ static void media_configure(GstRTSPMediaFactory *factory, GstRTSPMedia *media, g
gst_app_src_set_max_bytes(node->appsrc, 0);
gst_app_src_set_max_time(node->appsrc, 0);
/* create a minimal dummy preroll frame to satisfy pipeline preroll */
guint width = 2;
guint height = 2;
guint fr = node->framerate > 0 ? node->framerate : 30;
GstCaps *caps = gst_caps_new_simple("video/x-raw",
"format", G_TYPE_STRING, "RGB",
"width", G_TYPE_INT, width,
"height", G_TYPE_INT, height,
"framerate", GST_TYPE_FRACTION, fr, 1,
NULL);
gst_app_src_set_caps(node->appsrc, caps);
gsize buf_size = width * height * 3;
GstBuffer *buf = gst_buffer_new_allocate(NULL, buf_size, NULL);
GstMapInfo map;
if (gst_buffer_map(buf, &map, GST_MAP_WRITE)){
if (map.data) memset(map.data, 64, buf_size);
gst_buffer_unmap(buf, &map);
}
GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_LIVE);
gst_app_src_push_buffer(node->appsrc, buf);
gst_caps_unref(caps);
/* caps and first buffer are set by topic_callback from the real image
* so that caps always match the actual camera resolution/format.
* Pushing a dummy preroll with wrong caps breaks x264enc mid-stream. */
gst_object_unref(pipeline);
return;
} else {
@ -199,6 +179,7 @@ void Image2rtsp::topic_callback(const sensor_msgs::msg::Image::SharedPtr msg){
// Set caps from message
caps = gst_caps_new_from_image(msg);
gst_app_src_set_caps(appsrc, caps);
gst_caps_unref(caps);
buf = gst_buffer_new_allocate(nullptr, msg->data.size(), nullptr);
gst_buffer_fill(buf, 0, msg->data.data(), msg->data.size());
GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_LIVE);