Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bd73182
Reorganize files
bjsowa Oct 9, 2025
bf4af8e
Clean up include directives by including only what's used
bjsowa Oct 9, 2025
26c1322
Clean up package.xml depend tags
bjsowa Oct 9, 2025
17ea6f6
Load streamer plugins using pluginlib
bjsowa Oct 10, 2025
e5903e0
Move checking for compressed topics to the plugin itself
bjsowa Oct 10, 2025
1da7991
Add snapshot streamer plugins
bjsowa Oct 10, 2025
ec8347c
Add ros_compressed snapshot streamer plugin
bjsowa Oct 10, 2025
72d483e
Detect content type in ros_compressed snapshot streamer
bjsowa Oct 10, 2025
18be8e2
Fix libav streamer factory
bjsowa Oct 10, 2025
90f3ed1
Update listing available topics
bjsowa Oct 10, 2025
3437fee
List snapshot streamers
bjsowa Oct 10, 2025
0e8f668
Add missing includes
bjsowa Oct 10, 2025
c4a90b2
Revise class naming conventions
bjsowa Oct 10, 2025
0e12cc8
Move base classes to web_video_server_streamers library
bjsowa Oct 10, 2025
bd1489b
Reformat for humble
bjsowa Oct 10, 2025
f2467b8
Add some docstrings
bjsowa Oct 12, 2025
1d41e26
Use snake_case for method names
bjsowa Oct 12, 2025
db07bd9
boundry -> boundary
bjsowa Oct 12, 2025
bf22ec0
decodeImage -> decode_image
bjsowa Oct 13, 2025
55a53cb
Move base streamer classes to streamers namespace
bjsowa Oct 18, 2025
7121805
Reduce duplicated code
bjsowa Oct 19, 2025
e9cfad2
Add virtual destructor
bjsowa Oct 19, 2025
f0103d0
Include what you use
bjsowa Oct 19, 2025
463b756
Update copyright headers
bjsowa Oct 19, 2025
c0177a4
Export project dependencies
bjsowa Oct 19, 2025
54bbd89
Add missing copyright header
bjsowa Oct 19, 2025
e2752fb
Pass reference to get_available_topics
bjsowa Oct 19, 2025
7c39972
Remove redundant ImageTransport object
bjsowa Oct 19, 2025
36cbb92
Fix restream frames feature
bjsowa Oct 20, 2025
1c7502c
Pass weak node pointer to streamers to avoid cyclic dependency
bjsowa Oct 20, 2025
79b965f
Fix formatting for humble
bjsowa Oct 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 46 additions & 19 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ find_package(ament_cmake_ros REQUIRED)
find_package(async_web_server_cpp REQUIRED)
find_package(cv_bridge REQUIRED)
find_package(image_transport REQUIRED)
find_package(pluginlib REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_components REQUIRED)
find_package(rmw REQUIRED)
find_package(sensor_msgs REQUIRED)

find_package(OpenCV REQUIRED)
Expand Down Expand Up @@ -39,36 +41,60 @@ if(${cv_bridge_VERSION} VERSION_LESS "3.3.0")
add_compile_definitions(CV_BRIDGE_USES_OLD_HEADERS)
endif()

## Specify additional locations of header files
include_directories(include
${avcodec_INCLUDE_DIRS}
${avformat_INCLUDE_DIRS}
${avutil_INCLUDE_DIRS}
${swscale_INCLUDE_DIRS}
)

## Declare a cpp library
add_library(${PROJECT_NAME} SHARED
src/web_video_server.cpp
src/image_streamer.cpp
src/libav_streamer.cpp
src/vp8_streamer.cpp
src/h264_streamer.cpp
src/vp9_streamer.cpp
src/multipart_stream.cpp
src/ros_compressed_streamer.cpp
src/jpeg_streamers.cpp
src/png_streamers.cpp
src/streamer.cpp
src/utils.cpp
)

target_include_directories(${PROJECT_NAME}
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>"
)

## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME}
PUBLIC
async_web_server_cpp::async_web_server_cpp
pluginlib::pluginlib
rclcpp::rclcpp
rmw::rmw
Boost::boost
PRIVATE
rclcpp_components::component
)

add_library(${PROJECT_NAME}_streamers SHARED
src/image_transport_streamer.cpp
src/libav_streamer.cpp
src/streamers/h264_streamer.cpp
src/streamers/jpeg_streamers.cpp
src/streamers/png_streamers.cpp
src/streamers/ros_compressed_streamer.cpp
src/streamers/vp8_streamer.cpp
src/streamers/vp9_streamer.cpp
)

target_include_directories(${PROJECT_NAME}_streamers
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>"
${avcodec_INCLUDE_DIRS}
${avformat_INCLUDE_DIRS}
${avutil_INCLUDE_DIRS}
${swscale_INCLUDE_DIRS}
)

target_link_libraries(${PROJECT_NAME}_streamers
${PROJECT_NAME}
async_web_server_cpp::async_web_server_cpp
cv_bridge::cv_bridge
image_transport::image_transport
pluginlib::pluginlib
rclcpp::rclcpp
rclcpp_components::component
${sensor_msgs_TARGETS}
Boost::boost
Boost::system
Expand All @@ -90,18 +116,19 @@ target_link_libraries(${PROJECT_NAME}_node

rclcpp_components_register_nodes(${PROJECT_NAME} "web_video_server::WebVideoServer")

pluginlib_export_plugin_description_file(web_video_server plugins.xml)

#############
## Install ##
#############

## Mark executables and/or libraries for installation
install(
DIRECTORY include/
DESTINATION include/${PROJECT_NAME}
)

install(
TARGETS ${PROJECT_NAME}
TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_streamers
EXPORT export_${PROJECT_NAME}
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Copyright (c) 2014, Worcester Polytechnic Institute
// Copyright (c) 2024, The Robot Web Tools Contributors
// Copyright (c) 2024-2025, The Robot Web Tools Contributors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -31,65 +30,32 @@
#pragma once

#include <chrono>
#include <memory>
#include <mutex>
#include <string>
#include <vector>

#include <opencv2/opencv.hpp>
#include <opencv2/core/mat.hpp>

#include "rclcpp/rclcpp.hpp"
#include "image_transport/image_transport.hpp"
#include "image_transport/transport_hints.hpp"
#include "web_video_server/utils.hpp"
#include "async_web_server_cpp/http_server.hpp"
#include "async_web_server_cpp/http_connection.hpp"
#include "async_web_server_cpp/http_request.hpp"
#include "image_transport/image_transport.hpp"
#include "image_transport/subscriber.hpp"
#include "rclcpp/node.hpp"
#include "sensor_msgs/msg/image.hpp"

#include "web_video_server/streamer.hpp"

namespace web_video_server
{

class ImageStreamer
class ImageTransportStreamerBase : public StreamerInterface
{
public:
ImageStreamer(
ImageTransportStreamerBase(
const async_web_server_cpp::HttpRequest & request,
async_web_server_cpp::HttpConnectionPtr connection,
rclcpp::Node::SharedPtr node);

virtual void start() = 0;
virtual ~ImageStreamer();

bool isInactive()
{
return inactive_;
}

/**
* Restreams the last received image frame if older than max_age.
*/
virtual void restreamFrame(std::chrono::duration<double> max_age) = 0;

std::string getTopic()
{
return topic_;
}

protected:
async_web_server_cpp::HttpConnectionPtr connection_;
async_web_server_cpp::HttpRequest request_;
rclcpp::Node::SharedPtr node_;
bool inactive_;
image_transport::Subscriber image_sub_;
std::string topic_;
};


class ImageTransportImageStreamer : public ImageStreamer
{
public:
ImageTransportImageStreamer(
const async_web_server_cpp::HttpRequest & request,
async_web_server_cpp::HttpConnectionPtr connection,
rclcpp::Node::SharedPtr node);
virtual ~ImageTransportImageStreamer();
virtual ~ImageTransportStreamerBase();

virtual void start();

Expand Down Expand Up @@ -117,15 +83,16 @@ class ImageTransportImageStreamer : public ImageStreamer
void imageCallback(const sensor_msgs::msg::Image::ConstSharedPtr & msg);
};

class ImageStreamerType
class ImageTransportStreamerFactoryBase : public StreamerFactoryInterface
{
public:
virtual std::shared_ptr<ImageStreamer> create_streamer(
const async_web_server_cpp::HttpRequest & request,
async_web_server_cpp::HttpConnectionPtr connection,
rclcpp::Node::SharedPtr node) = 0;
virtual std::vector<std::string> get_available_topics(rclcpp::Node::SharedPtr node);
};

virtual std::string create_viewer(const async_web_server_cpp::HttpRequest & request) = 0;
class ImageTransportSnapshotStreamerFactoryBase : public SnapshotStreamerFactoryInterface
{
public:
virtual std::vector<std::string> get_available_topics(rclcpp::Node::SharedPtr node);
};

} // namespace web_video_server
46 changes: 18 additions & 28 deletions include/web_video_server/libav_streamer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,40 +33,44 @@
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavcodec/codec.h>
#include <libavformat/avformat.h>
#include <libavutil/intreadwrite.h>
#include <libavformat/avio.h>
#include <libavutil/dict.h>
#include <libavutil/frame.h>
#include <libswscale/swscale.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/imgutils.h>
}

#include <chrono>
#include <cstdint>
#include <memory>
#include <mutex>
#include <string>

#include "image_transport/image_transport.hpp"
#include "web_video_server/image_streamer.hpp"
#include "async_web_server_cpp/http_request.hpp"
#include <opencv2/core/mat.hpp>

#include "async_web_server_cpp/http_connection.hpp"
#include "async_web_server_cpp/http_request.hpp"
#include "rclcpp/node.hpp"

#include "web_video_server/image_transport_streamer.hpp"
#include "web_video_server/streamer.hpp"

namespace web_video_server
{

class LibavStreamer : public ImageTransportImageStreamer
class LibavStreamerBase : public ImageTransportStreamerBase
{
public:
LibavStreamer(
LibavStreamerBase(
const async_web_server_cpp::HttpRequest & request,
async_web_server_cpp::HttpConnectionPtr connection,
rclcpp::Node::SharedPtr node, const std::string & format_name, const std::string & codec_name,
const std::string & content_type);

~LibavStreamer();
~LibavStreamerBase();

protected:
virtual void initializeEncoder();
virtual void initializeEncoder() = 0;
virtual void sendImage(const cv::Mat &, const std::chrono::steady_clock::time_point & time);
virtual void initialize(const cv::Mat &);
AVFormatContext * format_context_;
Expand Down Expand Up @@ -94,24 +98,10 @@ class LibavStreamer : public ImageTransportImageStreamer
uint8_t * io_buffer_; // custom IO buffer
};

class LibavStreamerType : public ImageStreamerType
class LibavStreamerFactoryBase : public ImageTransportStreamerFactoryBase
{
public:
LibavStreamerType(
const std::string & format_name, const std::string & codec_name,
const std::string & content_type);

std::shared_ptr<ImageStreamer> create_streamer(
const async_web_server_cpp::HttpRequest & request,
async_web_server_cpp::HttpConnectionPtr connection,
rclcpp::Node::SharedPtr node);

std::string create_viewer(const async_web_server_cpp::HttpRequest & request);

private:
const std::string format_name_;
const std::string codec_name_;
const std::string content_type_;
virtual std::string create_viewer(const async_web_server_cpp::HttpRequest & request);
};

} // namespace web_video_server
5 changes: 4 additions & 1 deletion include/web_video_server/multipart_stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,15 @@

#pragma once

#include <chrono>
#include <cstddef>
#include <queue>
#include <memory>
#include <vector>
#include <string>

#include "rclcpp/rclcpp.hpp"
#include <boost/asio/buffer.hpp>

#include "async_web_server_cpp/http_connection.hpp"

namespace web_video_server
Expand Down
Loading