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:
parent
5fd57decc9
commit
f151a78ebd
|
|
@ -35,7 +35,11 @@ RUN apt-get update \
|
||||||
COPY --from=build /ws/install /ws/install
|
COPY --from=build /ws/install /ws/install
|
||||||
COPY docker-entrypoint.sh /ros_entrypoint.sh
|
COPY docker-entrypoint.sh /ros_entrypoint.sh
|
||||||
RUN chmod +x /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 ROS_DISTRO=humble
|
||||||
|
ENV FASTRTPS_DEFAULT_PROFILES_FILE=/ws/fastdds_no_shm.xml
|
||||||
|
|
||||||
## Make ROS and workspace overlays available for interactive shells
|
## Make ROS and workspace overlays available for interactive shells
|
||||||
RUN echo "source /opt/ros/${ROS_DISTRO}/setup.bash || true" > /etc/profile.d/ros2.sh \
|
RUN echo "source /opt/ros/${ROS_DISTRO}/setup.bash || true" > /etc/profile.d/ros2.sh \
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ build-essential
|
||||||
cmake
|
cmake
|
||||||
pkg-config
|
pkg-config
|
||||||
libopencv-dev
|
libopencv-dev
|
||||||
|
python3-opencv
|
||||||
python3-rosdep
|
python3-rosdep
|
||||||
python3-rosdistro
|
python3-rosdistro
|
||||||
software-properties-common
|
software-properties-common
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
libgstreamer1.0-0
|
libgstreamer1.0-0
|
||||||
gstreamer1.0-plugins-base
|
gstreamer1.0-plugins-base
|
||||||
gstreamer1.0-plugins-good
|
gstreamer1.0-plugins-good
|
||||||
|
gstreamer1.0-plugins-bad
|
||||||
gstreamer1.0-plugins-ugly
|
gstreamer1.0-plugins-ugly
|
||||||
libgstrtspserver-1.0-0
|
libgstrtspserver-1.0-0
|
||||||
python3-opencv
|
python3-opencv
|
||||||
|
ros-humble-ros2cli
|
||||||
|
ros-humble-rclpy
|
||||||
|
net-tools
|
||||||
|
iputils-ping
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,11 @@ services:
|
||||||
- RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-}
|
- RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-}
|
||||||
# GStreamer debug level (uncomment for verbose GStreamer logging)
|
# GStreamer debug level (uncomment for verbose GStreamer logging)
|
||||||
# - GST_DEBUG=${GST_DEBUG:-}
|
# - 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)
|
# Ensure the node binds to non-localhost interface (disable local_only)
|
||||||
# and subscribe to the actual image topic available on the host
|
# 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
|
restart: unless-stopped
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import os
|
import os
|
||||||
from ament_index_python.packages import get_package_share_directory
|
from ament_index_python.packages import get_package_share_directory
|
||||||
from launch import LaunchDescription
|
from launch import LaunchDescription
|
||||||
|
from launch.actions import DeclareLaunchArgument
|
||||||
|
from launch.substitutions import LaunchConfiguration
|
||||||
from launch_ros.actions import Node
|
from launch_ros.actions import Node
|
||||||
|
|
||||||
def generate_launch_description():
|
def generate_launch_description():
|
||||||
|
|
@ -10,13 +12,19 @@ def generate_launch_description():
|
||||||
'parameters.yaml'
|
'parameters.yaml'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
log_level = LaunchConfiguration('log_level')
|
||||||
|
|
||||||
return LaunchDescription([
|
return LaunchDescription([
|
||||||
|
DeclareLaunchArgument(
|
||||||
|
'log_level',
|
||||||
|
default_value='warn',
|
||||||
|
description='ROS logger level (debug, info, warn, error, fatal)'
|
||||||
|
),
|
||||||
Node(
|
Node(
|
||||||
package='image2rtsp',
|
package='image2rtsp',
|
||||||
executable='image2rtsp',
|
executable='image2rtsp',
|
||||||
name='image2rtsp',
|
name='image2rtsp',
|
||||||
parameters=[config],
|
parameters=[config],
|
||||||
# Reduce runtime logging verbosity to WARN to avoid info spam in container logs
|
arguments=['--ros-args', '--log-level', log_level]
|
||||||
arguments=['--ros-args', '--log-level', 'warn']
|
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
|
@ -69,7 +69,13 @@ Image2rtsp::Image2rtsp() : Node("image2rtsp"){
|
||||||
framerate = extract_framerate(pipeline, 30);
|
framerate = extract_framerate(pipeline, 30);
|
||||||
rtsp_server_add_url(mountpoint.c_str(), pipeline.c_str());
|
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) {
|
uint Image2rtsp::extract_framerate(const std::string& pipeline, uint default_framerate = 30) {
|
||||||
|
|
|
||||||
|
|
@ -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_bytes(node->appsrc, 0);
|
||||||
gst_app_src_set_max_time(node->appsrc, 0);
|
gst_app_src_set_max_time(node->appsrc, 0);
|
||||||
|
|
||||||
/* create a minimal dummy preroll frame to satisfy pipeline preroll */
|
/* caps and first buffer are set by topic_callback from the real image
|
||||||
guint width = 2;
|
* so that caps always match the actual camera resolution/format.
|
||||||
guint height = 2;
|
* Pushing a dummy preroll with wrong caps breaks x264enc mid-stream. */
|
||||||
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);
|
|
||||||
gst_object_unref(pipeline);
|
gst_object_unref(pipeline);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -199,6 +179,7 @@ void Image2rtsp::topic_callback(const sensor_msgs::msg::Image::SharedPtr msg){
|
||||||
// Set caps from message
|
// Set caps from message
|
||||||
caps = gst_caps_new_from_image(msg);
|
caps = gst_caps_new_from_image(msg);
|
||||||
gst_app_src_set_caps(appsrc, caps);
|
gst_app_src_set_caps(appsrc, caps);
|
||||||
|
gst_caps_unref(caps);
|
||||||
buf = gst_buffer_new_allocate(nullptr, msg->data.size(), nullptr);
|
buf = gst_buffer_new_allocate(nullptr, msg->data.size(), nullptr);
|
||||||
gst_buffer_fill(buf, 0, msg->data.data(), msg->data.size());
|
gst_buffer_fill(buf, 0, msg->data.data(), msg->data.size());
|
||||||
GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_LIVE);
|
GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_LIVE);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue