1+ #include < rclcpp/rclcpp.hpp>
2+ #include < sensor_msgs/msg/image.hpp>
3+
4+ #include < gst/gst.h>
5+ #include < gst/app/gstappsrc.h>
6+
7+ class ImageToGStreamer : public rclcpp ::Node
8+ {
9+ public:
10+ ImageToGStreamer ()
11+ : Node(" image_to_gstreamer_node" )
12+ {
13+ gst_init (nullptr , nullptr );
14+
15+ input_topic_ = this ->declare_parameter <std::string>(" input_topic" , " /cam/image_color" );
16+
17+ sub_ = create_subscription<sensor_msgs::msg::Image>(
18+ input_topic_, rclcpp::SensorDataQoS (),
19+ std::bind (&ImageToGStreamer::imageCb, this , std::placeholders::_1));
20+
21+ create_pipeline ();
22+ }
23+
24+ ~ImageToGStreamer ()
25+ {
26+ gst_element_set_state (pipeline_, GST_STATE_NULL);
27+ gst_object_unref (pipeline_);
28+ }
29+
30+ private:
31+ rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr sub_;
32+
33+ GstElement *pipeline_;
34+ GstElement *appsrc_;
35+
36+ bool pipeline_started_ = false ;
37+
38+ void create_pipeline ()
39+ {
40+ pipeline_ = gst_pipeline_new (" ros2-h265-pipeline" );
41+
42+ appsrc_ = gst_element_factory_make (" appsrc" , " source" );
43+ GstElement *convert = gst_element_factory_make (" videoconvert" , " convert" );
44+ GstElement *encoder = gst_element_factory_make (" nvh265enc" , " encoder" );
45+ GstElement *parser = gst_element_factory_make (" h265parse" , " parser" );
46+ GstElement *pay = gst_element_factory_make (" rtph265pay" , " pay" );
47+ GstElement *sink = gst_element_factory_make (" udpsink" , " sink" );
48+
49+ if (!appsrc_ || !convert ||
50+ !encoder || !parser || !pay || !sink)
51+ {
52+ if (!appsrc_)
53+ RCLCPP_FATAL (get_logger (), " Failed to create appsrc" );
54+ if (!convert)
55+ RCLCPP_FATAL (get_logger (), " Failed to create convert" );
56+ if (!encoder)
57+ RCLCPP_FATAL (get_logger (), " Failed to create encoder" );
58+ if (!parser)
59+ RCLCPP_FATAL (get_logger (), " Failed to create parser" );
60+ if (!pay)
61+ RCLCPP_FATAL (get_logger (), " Failed to create pay" );
62+ if (!sink)
63+ RCLCPP_FATAL (get_logger (), " Failed to create sink" );
64+
65+ RCLCPP_FATAL (get_logger (), " Failed to create GStreamer elements" );
66+ return ;
67+ }
68+ if (!pipeline_) {
69+ RCLCPP_FATAL (get_logger (), " Pipeline creation failed" );
70+ return ;
71+ }
72+
73+ g_object_set (sink,
74+ " host" , " 127.0.0.1" ,
75+ " port" , 5000 ,
76+ " sync" , FALSE ,
77+ NULL );
78+
79+ g_object_set (encoder,
80+ " bitrate" , 50000 , // 4 Mbps
81+ " preset" , 3 , // low-latency
82+ " rc-mode" , 1 , // CBR
83+ " gop-size" , 30 ,
84+ NULL );
85+
86+ g_object_set (pay,
87+ " config-interval" , 1 , // send SPS/PPS every 1 second
88+ " pt" , 96 , // payload type
89+ NULL );
90+
91+ gst_bin_add_many (GST_BIN (pipeline_),
92+ appsrc_, convert, encoder, parser, pay, sink, NULL );
93+
94+ if (!gst_element_link_many (
95+ appsrc_, convert, encoder, parser, pay, sink, NULL ))
96+ {
97+ RCLCPP_FATAL (get_logger (), " Pipeline linking failed" );
98+ return ;
99+ }
100+ }
101+
102+ void imageCb (const sensor_msgs::msg::Image::SharedPtr msg)
103+ {
104+ if (!pipeline_started_)
105+ {
106+ GstCaps *caps = gst_caps_new_simple (
107+ " video/x-raw" ,
108+ " format" , G_TYPE_STRING, " RGB" ,
109+ " width" , G_TYPE_INT, msg->width ,
110+ " height" , G_TYPE_INT, msg->height ,
111+ " framerate" , GST_TYPE_FRACTION, 30 , 1 ,
112+ NULL );
113+ g_object_set (appsrc_,
114+ " caps" , caps,
115+ " format" , GST_FORMAT_TIME,
116+ " is-live" , TRUE ,
117+ " do-timestamp" , TRUE ,
118+ NULL );
119+ gst_caps_unref (caps);
120+
121+ gst_element_set_state (pipeline_, GST_STATE_PLAYING);
122+ pipeline_started_ = true ;
123+
124+ RCLCPP_INFO (get_logger (), " H.265 GPU pipeline started" );
125+ }
126+
127+ GstBuffer *buffer = gst_buffer_new_allocate (
128+ nullptr , msg->data .size (), nullptr );
129+
130+ gst_buffer_fill (buffer, 0 , msg->data .data (), msg->data .size ());
131+
132+ GstFlowReturn ret;
133+ g_signal_emit_by_name (appsrc_, " push-buffer" , buffer, &ret);
134+ gst_buffer_unref (buffer);
135+
136+ if (ret != GST_FLOW_OK)
137+ RCLCPP_WARN (get_logger (), " Failed to push buffer" );
138+ }
139+
140+ // ----------------------------- Params & ROS --------------------------------
141+ // Topics
142+ std::string input_topic_{" /cam/image_color" };
143+ };
144+
145+ int main (int argc, char **argv)
146+ {
147+ rclcpp::init (argc, argv);
148+ rclcpp::spin (std::make_shared<ImageToGStreamer>());
149+ rclcpp::shutdown ();
150+ return 0 ;
151+ }
0 commit comments