From ac36d5eee6da51e66778e432764b472cf012fc87 Mon Sep 17 00:00:00 2001 From: Gerald Storer Date: Tue, 1 Apr 2025 14:00:31 +0800 Subject: [PATCH] Add hardware encoding --- README.md | 5 +++++ config/parameters.yaml | 1 + include/image2rtsp.hpp | 2 ++ src/image2rtsp.cpp | 46 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 70dee26..e8a4f93 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,11 @@ sudo apt-get install libgstreamer-plugins-base1.0-dev libgstreamer-plugins-good1 # False = rtsp://0.0.0.0:portAndMountpoint (The stream is accessible from the outside) # For example, to access the stream running on the machine with IP = 192.168.20.20, # use rtsp://192.186.20.20:portAndMountpoint + encoder: "software" # Which encoder to use, "hardware" automatically selects an available hardware encoder. + # Use "nvidia", "intel" or "amd" to force an encoder if available. + # Use "software" to force the software encoder. + # Always falls back to software if a hardware encoder can't be found. + # Defaults to "software". - Save your configuration and navigate to `ros2_ws` colcon root, source and build the package: ```bashrc cd ~/ros2_ws/ diff --git a/config/parameters.yaml b/config/parameters.yaml index 72cc3ea..caef994 100644 --- a/config/parameters.yaml +++ b/config/parameters.yaml @@ -19,3 +19,4 @@ # False = rtsp://0.0.0.0:portAndMountpoint (The stream is accessible from the outside) # For example, to access the stream running on the machine with IP = 192.168.20.20, # use rtsp://192.186.20.20:portAndMountpoint + encoder: "software" diff --git a/include/image2rtsp.hpp b/include/image2rtsp.hpp index 28dda0c..0a6bc4e 100644 --- a/include/image2rtsp.hpp +++ b/include/image2rtsp.hpp @@ -28,8 +28,10 @@ private: string pipeline_tail; bool local_only; bool camera; + string encoder; GstAppSrc *appsrc; + std::string get_encoder_pipeline(); void video_mainloop_start(); void rtsp_server_add_url(const char *url, const char *sPipeline, GstElement **appsrc); void topic_callback(const sensor_msgs::msg::Image::SharedPtr msg); diff --git a/src/image2rtsp.cpp b/src/image2rtsp.cpp index 822d2a6..1d17573 100644 --- a/src/image2rtsp.cpp +++ b/src/image2rtsp.cpp @@ -19,6 +19,7 @@ Image2rtsp::Image2rtsp() : Node("image2rtsp"){ this->declare_parameter("port", "8554"); this->declare_parameter("local_only", true); this->declare_parameter("camera", false); + this->declare_parameter("encoder", "software"); source = this->get_parameter("source").as_string(); topic = this->get_parameter("topic").as_string(); @@ -30,6 +31,7 @@ Image2rtsp::Image2rtsp() : Node("image2rtsp"){ port = this->get_parameter("port").as_string(); local_only = this->get_parameter("local_only").as_bool(); camera = this->get_parameter("camera").as_bool(); + encoder = this->get_parameter("encoder").as_string(); // Start the subscription subscription_ = this->create_subscription(topic, 10, std::bind(&Image2rtsp::topic_callback, this, _1)); @@ -39,11 +41,17 @@ Image2rtsp::Image2rtsp() : Node("image2rtsp"){ rtsp_server = rtsp_server_create(port, local_only); appsrc = NULL; // Setup the pipeline - pipeline_tail = " key-int-max=30 ! video/x-h264, profile=baseline ! rtph264pay name=pay0 pt=96 )"; + pipeline_tail = " ! video/x-h264,profile=constrained-baseline ! rtph264pay name=pay0 pt=96 " + "config-interval=1 aggregate-mode=zero-latency ! " + "application/x-rtp,media=video )"; + auto encoder = get_encoder_pipeline(); if (camera == false){ - pipeline_head = "( appsrc name=imagesrc do-timestamp=true min-latency=0 max-latency=0 max-bytes=1000 is-live=true ! videoconvert ! videoscale ! "; - pipeline = pipeline_head + caps_1 + framerate + caps_2 + " ! x264enc tune=zerolatency bitrate=" + bitrate + pipeline_tail; + pipeline_head = "( appsrc name=imagesrc do-timestamp=true min-latency=0 " + "max-latency=0 max-bytes=2000000 is-live=true ! queue max-size-buffers=1 leaky=downstream ! videoconvert ! videoscale ! "; + pipeline = pipeline_head + caps_1 + framerate + caps_2 + + " ! " + encoder + " bitrate=" + bitrate + pipeline_tail; rtsp_server_add_url(mountpoint.c_str(), pipeline.c_str(), (GstElement **)&(appsrc)); + RCLCPP_INFO(this->get_logger(), "pipeline: %s", pipeline.c_str()); } else { pipeline = "( " + source + " ! videoconvert ! videoscale ! " + caps_1 + framerate + caps_2 + " ! x264enc tune=zerolatency bitrate=" + bitrate + pipeline_tail; @@ -52,6 +60,38 @@ Image2rtsp::Image2rtsp() : Node("image2rtsp"){ RCLCPP_INFO(this->get_logger(), "Stream available at rtsp://%s:%s%s", gst_rtsp_server_get_address(rtsp_server), port.c_str(), mountpoint.c_str()); } +std::string Image2rtsp::get_encoder_pipeline() { + GstElementFactory* factory; + + // Check for NVIDIA encoder + factory = gst_element_factory_find("nvh264enc"); + if (factory && (encoder == "hardware" || encoder == "nvidia")) { + gst_object_unref(factory); + RCLCPP_INFO(this->get_logger(), "Using NVIDIA hardware encoder"); + return "nvh264enc preset=low-latency-hp rc-mode=cbr-ld-hq zerolatency=true"; + } + + // Check for VA-API encoder + factory = gst_element_factory_find("vaapih264enc"); + if (factory && (encoder == "hardware" || encoder == "intel")) { + gst_object_unref(factory); + RCLCPP_INFO(this->get_logger(), "Using VA-API hardware encoder"); + return "vaapipostproc ! vaapih264enc rate-control=cbr quality-level=7"; + } + + // Check for AMD encoder + factory = gst_element_factory_find("amfh264enc"); + if (factory && (encoder == "hardware" || encoder == "amd")) { + gst_object_unref(factory); + RCLCPP_INFO(this->get_logger(), "Using AMD hardware encoder"); + return "amfh264enc usage=ultra-low-latency"; + } + + // Fallback to software encoder + RCLCPP_INFO(this->get_logger(), "Using software encoder"); + return "x264enc tune=zerolatency speed-preset=ultrafast"; +} + int main(int argc, char *argv[]){ rclcpp::init(argc, argv); auto node = std::make_shared();