Skip to content

Commit 90f3ed1

Browse files
committed
Update listing available topics
1 parent 18be8e2 commit 90f3ed1

9 files changed

Lines changed: 99 additions & 66 deletions

include/web_video_server/base_image_streamer.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ class BaseImageStreamerFactory
8787
rclcpp::Node::SharedPtr node) = 0;
8888

8989
virtual std::string create_viewer(const async_web_server_cpp::HttpRequest & request);
90+
91+
virtual std::vector<std::string> get_available_topics(rclcpp::Node::SharedPtr node);
9092
};
9193

9294
class BaseSnapshotStreamerFactory : public BaseImageStreamerFactory {};

include/web_video_server/base_image_transport_streamer.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,10 @@ class BaseImageTransportStreamer : public BaseImageStreamer
8282
void imageCallback(const sensor_msgs::msg::Image::ConstSharedPtr & msg);
8383
};
8484

85+
class BaseImageTransportStreamerFactory : public BaseImageStreamerFactory
86+
{
87+
public:
88+
virtual std::vector<std::string> get_available_topics(rclcpp::Node::SharedPtr node);
89+
};
90+
8591
} // namespace web_video_server

include/web_video_server/streamers/jpeg_streamers.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class MjpegStreamer : public web_video_server::BaseImageTransportStreamer
6464
int quality_;
6565
};
6666

67-
class MjpegStreamerFactory : public web_video_server::BaseImageStreamerFactory
67+
class MjpegStreamerFactory : public web_video_server::BaseImageTransportStreamerFactory
6868
{
6969
public:
7070
std::string get_type() {return "mjpeg";}

include/web_video_server/streamers/png_streamers.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class PngStreamer : public web_video_server::BaseImageTransportStreamer
6565
int quality_;
6666
};
6767

68-
class PngStreamerFactory : public web_video_server::BaseImageStreamerFactory
68+
class PngStreamerFactory : public web_video_server::BaseImageTransportStreamerFactory
6969
{
7070
public:
7171
std::string get_type() {return "png";}

include/web_video_server/streamers/ros_compressed_streamer.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class RosCompressedStreamerFactory : public web_video_server::BaseImageStreamerF
8181
const async_web_server_cpp::HttpRequest & request,
8282
async_web_server_cpp::HttpConnectionPtr connection,
8383
rclcpp::Node::SharedPtr node);
84+
std::vector<std::string> get_available_topics(rclcpp::Node::SharedPtr node);
8485
};
8586

8687
class RosCompressedSnapshotStreamer : public web_video_server::BaseImageStreamer

src/base_image_streamer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,10 @@ std::string BaseImageStreamerFactory::create_viewer(
6060
return ss.str();
6161
}
6262

63+
std::vector<std::string> BaseImageStreamerFactory::get_available_topics(
64+
rclcpp::Node::SharedPtr /* node */)
65+
{
66+
return {};
67+
}
68+
6369
} // namespace web_video_server

src/base_image_transport_streamer.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,20 @@ cv::Mat BaseImageTransportStreamer::decodeImage(
238238
}
239239
}
240240

241+
std::vector<std::string> BaseImageTransportStreamerFactory::get_available_topics(
242+
rclcpp::Node::SharedPtr node)
243+
{
244+
std::vector<std::string> result;
245+
auto tnat = node->get_topic_names_and_types();
246+
for (auto topic_and_types : tnat) {
247+
for (auto & type : topic_and_types.second) {
248+
if (type == "sensor_msgs/msg/Image") {
249+
result.push_back(topic_and_types.first);
250+
break;
251+
}
252+
}
253+
}
254+
return result;
255+
}
256+
241257
} // namespace web_video_server

src/streamers/ros_compressed_streamer.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,25 @@ std::shared_ptr<web_video_server::BaseImageStreamer> RosCompressedStreamerFactor
191191
return std::make_shared<RosCompressedStreamer>(request, connection, node);
192192
}
193193

194+
std::vector<std::string> RosCompressedStreamerFactory::get_available_topics(
195+
rclcpp::Node::SharedPtr node)
196+
{
197+
std::vector<std::string> result;
198+
auto tnat = node->get_topic_names_and_types();
199+
for (auto topic_and_types : tnat) {
200+
for (auto & type : topic_and_types.second) {
201+
if (type == "sensor_msgs/msg/CompressedImage") {
202+
std::string topic_name = topic_and_types.first;
203+
if (topic_name.size() > 11 && topic_name.substr(topic_name.size() - 11) == "/compressed") {
204+
topic_name = topic_name.substr(0, topic_name.size() - 11);
205+
}
206+
result.push_back(topic_name);
207+
}
208+
}
209+
}
210+
return result;
211+
}
212+
194213
RosCompressedSnapshotStreamer::RosCompressedSnapshotStreamer(
195214
const async_web_server_cpp::HttpRequest & request,
196215
async_web_server_cpp::HttpConnectionPtr connection, rclcpp::Node::SharedPtr node)

src/web_video_server.cpp

Lines changed: 47 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@
3636
#include <exception>
3737
#include <memory>
3838
#include <mutex>
39+
#include <set>
3940
#include <string>
4041
#include <sstream>
4142
#include <vector>
4243

44+
#include <boost/algorithm/string/join.hpp>
4345
#include <boost/algorithm/string/predicate.hpp>
4446
#include <boost/bind/bind.hpp>
4547
#include <boost/bind/placeholders.hpp>
@@ -263,21 +265,19 @@ bool WebVideoServer::handle_list_streams(
263265
async_web_server_cpp::HttpConnectionPtr connection, const char * /* begin */,
264266
const char * /* end */)
265267
{
266-
std::vector<std::string> image_topics;
267-
std::vector<std::string> camera_info_topics;
268-
auto tnat = get_topic_names_and_types();
269-
for (auto topic_and_types : tnat) {
270-
if (topic_and_types.second.size() > 1) {
271-
// skip over topics with more than one type
272-
continue;
273-
}
274-
auto & topic_name = topic_and_types.first;
275-
auto & topic_type = topic_and_types.second[0]; // explicitly take the first
276-
277-
if (topic_type == "sensor_msgs/msg/Image") {
278-
image_topics.push_back(topic_name);
279-
} else if (topic_type == "sensor_msgs/msg/CameraInfo") {
280-
camera_info_topics.push_back(topic_name);
268+
std::map<std::string, std::vector<std::string>> topics_by_type;
269+
std::set<std::string> all_topics;
270+
271+
for (const auto & factory_pair : image_streamer_factories_) {
272+
RCLCPP_DEBUG(get_logger(), "Getting topics from factory: %s", factory_pair.first.c_str());
273+
std::vector<std::string> factory_topics =
274+
factory_pair.second->get_available_topics(shared_from_this());
275+
RCLCPP_DEBUG(get_logger(), "Factory %s returned %zu topics",
276+
factory_pair.first.c_str(), factory_topics.size());
277+
for (const auto & topic : factory_topics) {
278+
RCLCPP_DEBUG(get_logger(), " Topic: %s", topic.c_str());
279+
topics_by_type[factory_pair.first].push_back(topic);
280+
all_topics.insert(topic);
281281
}
282282
}
283283

@@ -291,61 +291,44 @@ bool WebVideoServer::handle_list_streams(
291291

292292
connection->write(
293293
"<html>"
294-
"<head><title>ROS Image Topic List</title></head>"
295-
"<body><h1>Available ROS Image Topics:</h1>");
294+
"<head><title>ROS Streamable Topic List</title></head>"
295+
"<body><h1>Available ROS Topics for streaming:</h1>");
296296
connection->write("<ul>");
297-
for (std::string & camera_info_topic : camera_info_topics) {
298-
if (boost::algorithm::ends_with(camera_info_topic, "/camera_info")) {
299-
std::string base_topic = camera_info_topic.substr(
300-
0,
301-
camera_info_topic.size() - strlen("camera_info"));
302-
connection->write("<li>");
303-
connection->write(base_topic);
304-
connection->write("<ul>");
305-
std::vector<std::string>::iterator image_topic_itr = image_topics.begin();
306-
for (; image_topic_itr != image_topics.end(); ) {
307-
if (boost::starts_with(*image_topic_itr, base_topic)) {
308-
connection->write("<li><a href=\"/stream_viewer?topic=");
309-
connection->write(*image_topic_itr);
310-
connection->write("\">");
311-
connection->write(image_topic_itr->substr(base_topic.size()));
312-
connection->write("</a> (");
313-
connection->write("<a href=\"/stream?topic=");
314-
connection->write(*image_topic_itr);
315-
connection->write("\">Stream</a>) (");
316-
connection->write("<a href=\"/snapshot?topic=");
317-
connection->write(*image_topic_itr);
318-
connection->write("\">Snapshot</a>)");
319-
connection->write("</li>");
320-
321-
image_topic_itr = image_topics.erase(image_topic_itr);
322-
} else {
323-
++image_topic_itr;
324-
}
297+
for (const std::string & topic : all_topics) {
298+
std::vector<std::string> available_stream_viewers;
299+
std::vector<std::string> available_streams;
300+
301+
for (const auto & factory_pair : topics_by_type) {
302+
const auto & type = factory_pair.first;
303+
const auto & topics = factory_pair.second;
304+
if (std::find(topics.begin(), topics.end(), topic) != topics.end()) {
305+
available_stream_viewers.push_back(
306+
"<a href=\"/stream_viewer?topic=" + topic +
307+
"&type=" + type + "\">" + type + "</a>");
308+
available_streams.push_back(
309+
"<a href=\"/stream?topic=" + topic +
310+
"&type=" + type + "\">" + type + "</a>");
325311
}
326-
connection->write("</ul>");
327312
}
313+
314+
connection->write("<li>");
315+
connection->write(topic);
316+
connection->write("<ul>");
317+
connection->write("<li>");
318+
connection->write("<a href=\"/stream_viewer?topic=" + topic + "\">");
319+
connection->write("Stream Viewer</a> (");
320+
connection->write(boost::algorithm::join(available_stream_viewers, ", "));
321+
connection->write(")");
328322
connection->write("</li>");
329-
}
330-
connection->write("</ul>");
331-
// Add the rest of the image topics that don't have camera_info.
332-
connection->write("<ul>");
333-
std::vector<std::string>::iterator image_topic_itr = image_topics.begin();
334-
for (; image_topic_itr != image_topics.end(); ) {
335-
connection->write("<li><a href=\"/stream_viewer?topic=");
336-
connection->write(*image_topic_itr);
337-
connection->write("\">");
338-
connection->write(*image_topic_itr);
339-
connection->write("</a> (");
340-
connection->write("<a href=\"/stream?topic=");
341-
connection->write(*image_topic_itr);
342-
connection->write("\">Stream</a>) (");
343-
connection->write("<a href=\"/snapshot?topic=");
344-
connection->write(*image_topic_itr);
345-
connection->write("\">Snapshot</a>)");
323+
connection->write("<li>");
324+
connection->write("<a href=\"/stream?topic=" + topic + "\">");
325+
connection->write("Stream</a> (");
326+
connection->write(boost::algorithm::join(available_streams, ", "));
327+
connection->write(")");
328+
connection->write("</li>");
329+
connection->write("</ul>");
346330
connection->write("</li>");
347331

348-
image_topic_itr = image_topics.erase(image_topic_itr);
349332
}
350333
connection->write("</ul></body></html>");
351334
return true;

0 commit comments

Comments
 (0)