Skip to content
Merged
77 changes: 77 additions & 0 deletions src/accelerated_image_processor_decompression/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
cmake_minimum_required(VERSION 3.14)
project(accelerated_image_processor_decompression LANGUAGES CXX CUDA)

# Set CMAKE_MODULE_PATH to include the custom cmake modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")

if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic -Wunused-function)
endif()

find_package(ament_cmake_auto REQUIRED)
ament_auto_find_build_dependencies()

# find_package(CUDA)
find_package(CUDAToolkit)
find_library(CUDA_nppicc_LIBRARY nppicc ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES})
find_library(CUDA_nppidei_LIBRARY nppidei
${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES})
find_library(CUDA_nppisu_LIBRARY nppisu ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES})

# --------------------------------------------------------------------------------
# Find FFMpeg shared libraries manually ref:
# https://github.com/ros-misc-utilities/ffmpeg_encoder_decoder/blob/master/CMakeLists.txt
# --------------------------------------------------------------------------------
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBAV REQUIRED IMPORTED_TARGET libavcodec libavutil)
# --------------------------------------------------------------------------------

add_library(${PROJECT_NAME} SHARED src/builder.cpp
src/video_decompressor/ffmpeg.cpp)

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

target_include_directories(
${PROJECT_NAME}
PRIVATE $<$<BOOL:${CUDAToolkit_FOUND}>:${CUDAToolkit_INCLUDE_DIRS}>)

target_link_libraries(
${PROJECT_NAME}
PRIVATE PkgConfig::LIBAV
CUDA::cudart
CUDA::cuda_driver
${CUDA_nppicc_LIBRARY} # color conversion
${CUDA_nppisu_LIBRARY} # memory support functions
${CUDA_nppidei_LIBRARY} # data exchange and initialization functions
)

ament_target_dependencies(${PROJECT_NAME} PUBLIC
accelerated_image_processor_common)

install(TARGETS ${PROJECT_NAME} EXPORT export_${PROJECT_NAME})
install(DIRECTORY include/${PROJECT_NAME} DESTINATION include)

if(BUILD_TESTING)
find_package(ament_cmake_gtest REQUIRED)
ament_add_gtest(test_builder test/builder.cpp)
target_link_libraries(test_builder ${PROJECT_NAME})

pkg_check_modules(LIBAVFILTER REQUIRED IMPORTED_TARGET libavfilter)
ament_add_gtest(test_ffmpeg_video_decompressor
test/ffmpeg_video_decompressor.cpp)
target_link_libraries(test_ffmpeg_video_decompressor ${PROJECT_NAME}
PkgConfig::LIBAV PkgConfig::LIBAVFILTER)
endif()

ament_export_include_directories(include)
ament_export_targets(export_${PROJECT_NAME})
ament_export_dependencies(accelerated_image_processor_common)
ament_package()
79 changes: 79 additions & 0 deletions src/accelerated_image_processor_decompression/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# accelerated_image_processor_decompression

This package provides decompression functionalities for compressed video streams
using a CUDA‑accelerated FFmpeg backend. It can be used on Jetson devices (GPU) as well as on a generic CUDA‑capable
platform.

## Decompressor Supports

| Decompressor | Format | Backend | Device |
| ------------------------- | ----------------------- | ------- | ------ |
| `FfmpegVideoDecompressor` | `VIDEO` (H264/H265/AV1) | FFmpeg | GPU |

> **Note**
> _Only a single hardware‑accelerated backend is currently supported:_
> _`FfmpegVideoDecompressor` uses FFmpeg + NPP to decode the video and convert
> the decoded frames to RGB (`BGR` on the GPU). The decompressor accepts a
> `common::Image` whose `format` field indicates the encoded format
> (`H264`, `H265`, or `AV1`). It outputs a `common::Image` with
> `format` set to `RAW`/`BGR`._

## Example Usage in ROS 2

Below is a minimal example of how to use the decompressor in a ROS 2 node.
It mirrors the example in the _compression_ package, but uses the
`decompression::create_decompressor` factory.

```c++
#include <accelerated_image_processor_common/datatype.hpp>
#include <accelerated_image_processor_decompression/builder.hpp>
#include <rclcpp/rclcpp.hpp>

using namespace accelerated_image_processor;

class SomeNode final : public rclcpp::Node
{
public:
explicit SomeNode(const rclcpp::NodeOptions & options)
: Node("some_node", options)
{
// Choose decompression type
decompression::DecompressionType type = decompression::DecompressionType::VIDEO;
decompressor_ = decompression::create_decompressor<SomeNode, &SomeNode::publish>(type, this);

// Update parameters of the decompressor
for (auto & [name, value] : decompressor_->parameters()) {
std::visit([&](auto & v) {
using T = std::decay_t<decltype(v)>;
v = this->declare_parameter<T>(name, v);
}, value);
}

// Subscription to compressed stream and publisher for raw image
subscription_ = this->create_subscription<ffmpeg_image_transport_msgs::msg::FFMPEGPacket>(
"~/input/image/compressed", 10,
[this](const ffmpeg_image_transport_msgs::msg::FFMPEGPacket::ConstSharedPtr msg)
{ this->callback(msg); });
publisher_ = this->create_publisher<sensor_msgs::msg::Image>("~/output/image", 10);
}

private:
void callback(const ffmpeg_image_transport_msgs::msg::FFMPEGPacket::ConstSharedPtr msg)
{
common::Image image;
// Convert ROS message → accelerated_image_processor::common::Image …
decompressor_->process(image);
}

void publish(const common::Image & image)
{
sensor_msgs::msg::Image msg;
// Convert accelerated_image_processor::common::Image → ROS message …
publisher_->publish(msg);
}

std::unique_ptr<decompression::Decompressor> decompressor_;
rclcpp::Subscription<ffmpeg_image_transport_msgs::msg::FFMPEGPacket>::SharedPtr subscription_;
rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr publisher_;
};
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright 2026 TIER IV, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <accelerated_image_processor_common/processor.hpp>

#include <memory>
#include <string>
#include <type_traits>

namespace accelerated_image_processor::decompression
{
/**
* @brief Type alias for decompressor processor.
* @note This might be a specific implementation of a decompressor processor in the future.
*/
using Decompressor = common::BaseProcessor;

/**
* @brief Compression type enum
*/
enum class DecompressionType : uint8_t { VIDEO };

/**
* @brief Convert a string to a decompression type
* @param str String to convert expected strings ["VIDEO"]
* @return DecompressionType
*/
DecompressionType to_decompression_type(const std::string & str);

/**
* @brief Create a decompressor processor.
*
* @param type Decompression type
* @return std::unique_ptr<Decompressor>
*/
std::unique_ptr<Decompressor> create_decompressor(DecompressionType type);

/**
* @brief Create a decompressor processor.
*
* @param type Decompression type name in string format
* @return std::unique_ptr<Decompressor>
*/
std::unique_ptr<Decompressor> create_decompressor(const std::string & type);

/**
* @brief Create a decompressor processor with a free function for the postprocess.
*
* @param type Decompression type
* @param fn Free function for the postprocess
* @return std::unique_ptr<Decompressor>
*/
template <
typename F, std::enable_if_t<std::is_convertible_v<F, void (*)(const common::Image &)>, int> = 0>
inline std::unique_ptr<Decompressor> create_decompressor(DecompressionType type, F fn)
{
auto processor = create_decompressor(type);
auto fp = static_cast<void (*)(const common::Image &)>(fn);
if (fp) processor->register_postprocess(fp);
return processor;
}

/**
* @brief Create a decompressor processor with a free function for the postprocess.
*
* @param type Decompression type name in string format
* @param fn Free function for the postprocess
* @return std::unique_ptr<Decompressor>
*/
template <
typename F, std::enable_if_t<std::is_convertible_v<F, void (*)(const common::Image &)>, int> = 0>
inline std::unique_ptr<Decompressor> create_decompressor(const std::string & type, F fn)
{
return create_decompressor(to_decompression_type(type), fn);
}

/**
* @brief Create a decompressor processor with a member function for the postprocess.
*
* @param type Decompression type
* @param obj Object that has a member function for the postprocess
* @return std::unique_ptr<Decompressor>
*/
template <typename Obj, void (Obj::*Method)(const common::Image &)>
inline std::unique_ptr<Decompressor> create_decompressor(DecompressionType type, Obj * obj)
{
auto processor = create_decompressor(type);
processor->register_postprocess<Obj, Method>(obj);
return processor;
}

/**
* @brief Create a decompressor processor with a member function for the postprocess.
*
* @param type Decompression type name in string format
* @param obj Object that has a member function for the postprocess
* @return std::unique_ptr<Decompressor>
*/
template <typename Obj, void (Obj::*Method)(const common::Image &)>
inline std::unique_ptr<Decompressor> create_decompressor(const std::string & type, Obj * obj)
{
return create_decompressor<Obj, Method>(to_decompression_type(type), obj);
}
} // namespace accelerated_image_processor::decompression
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2026 TIER IV, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <accelerated_image_processor_common/datatype.hpp>
#include <accelerated_image_processor_common/parameter.hpp>
#include <accelerated_image_processor_common/processor.hpp>

#include <memory>

namespace accelerated_image_processor::decompression
{
class FfmpegVideoDecompressor;

/**
* @brief Enumeration of video decompression backends.
*/
enum class VideoBackend : uint8_t { FFMPEG };

/**
* @brief Abstract base class for video decompressors.
*/
Comment thread
manato marked this conversation as resolved.
class VideoDecompressor : public common::BaseProcessor
{
public:
explicit VideoDecompressor(VideoBackend backend, common::ParameterMap dedicated_parameters = {})
: BaseProcessor(dedicated_parameters += {}), backend_(backend)
{
}

~VideoDecompressor() override = default;

/**
* @brief Return the backend enum
*/
VideoBackend backend() const { return backend_; }

private:
const VideoBackend backend_; //!< Decompression backend type.
};
//!< @brief Factory function to create a FfmpegVideoDecompressor.
std::unique_ptr<VideoDecompressor> make_ffmpeg_video_decompressor();
} // namespace accelerated_image_processor::decompression
23 changes: 23 additions & 0 deletions src/accelerated_image_processor_decompression/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>accelerated_image_processor_decompression</name>
<version>0.0.0</version>
<description>Decompression library for accelerated image processing</description>
<maintainer email="contact@tier4.jp">TIER IV</maintainer>
<license>Apache-2.0</license>

<buildtool_depend>ament_cmake_auto</buildtool_depend>

<depend>accelerated_image_processor_common</depend>
<depend>libavcodec-dev</depend>
<depend>libavutil-dev</depend>

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<test_depend>libavfilter-dev</test_depend>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>
Loading