From c821f9ec1f93034ee204868fc0e59fcb933ba9e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Mon, 5 Feb 2024 10:55:11 -0500 Subject: [PATCH 01/17] Post-release version bump --- CMakeLists.txt | 2 +- addons/blender/splash/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3da65ce7..063b74b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ endif() project( splash - VERSION 0.10.6 + VERSION 0.10.7 LANGUAGES C CXX ) diff --git a/addons/blender/splash/__init__.py b/addons/blender/splash/__init__.py index fb14720b..99408aa9 100644 --- a/addons/blender/splash/__init__.py +++ b/addons/blender/splash/__init__.py @@ -20,7 +20,7 @@ bl_info = { "name": "Splash output", "author": "Splash authors", - "version": (0, 10, 6), + "version": (0, 10, 7), "blender": (2, 80, 0), "location": "3D View > Toolbox, File > Export", "description": "Utility tools to connect Blender to the Splash videomapper", From 4d86c0d0f6ba3ede8840c53fd9fc1c171a7ba4db Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Mon, 5 Feb 2024 13:14:28 -0500 Subject: [PATCH 02/17] Fix CI build for Archlinux --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e4ca06ef..0278d42d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -280,7 +280,7 @@ staging:archlinux:gcc-amd64-bundled-libs: - pacman -Syyu --noconfirm --disable-download-timeout - pacman -Sy --noconfirm --disable-download-timeout git cmake ninja gcc yasm pkgconfig libxi libxinerama libxrandr libxcursor jsoncpp mesa glm gsl libgphoto2 python3 - portaudio zip zlib ffmpeg opencv qt6-base vtk hdf5 glew libxkbcommon fmt + portaudio zip zlib ffmpeg opencv qt6-base vtk hdf5 glew libxkbcommon fmt make - mkdir -p ccache - export CCACHE_BASEDIR=${PWD} - export CCACHE_DIR=${PWD}/ccache From cd4905d487da3b3e1c3da1cd094bbe1469fd4956 Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Mon, 5 Feb 2024 15:45:15 -0500 Subject: [PATCH 03/17] Updated appdata metainfo file with lead developer name --- data/share/metainfo/xyz.splashmapper.Splash.metainfo.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/share/metainfo/xyz.splashmapper.Splash.metainfo.xml b/data/share/metainfo/xyz.splashmapper.Splash.metainfo.xml index d7fc025f..79c817b8 100644 --- a/data/share/metainfo/xyz.splashmapper.Splash.metainfo.xml +++ b/data/share/metainfo/xyz.splashmapper.Splash.metainfo.xml @@ -4,6 +4,7 @@ xyz.splashmapper.Splash xyz.splashmapper.Splash.desktop + Emmanuel Durand Splash Video-mapping software From 0ca40f9747c8de36c34599d974ac360c6ac32fd7 Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Mon, 5 Feb 2024 17:30:01 -0500 Subject: [PATCH 04/17] Fixed YUYV and UYVY decoding from shmdata sources --- src/graphics/shaderSources.h | 10 ++++------ src/image/image_shmdata.cpp | 1 + 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/graphics/shaderSources.h b/src/graphics/shaderSources.h index 09ece6f6..7500f179 100644 --- a/src/graphics/shaderSources.h +++ b/src/graphics/shaderSources.h @@ -1057,12 +1057,10 @@ const std::string FRAGMENT_SHADER_IMAGE_FILTER{R"( color.rgb = color.bgr; // Color balance - - vec2 colorBalance = _colorBalance; + vec2 colorBalance = _colorBalance; - if(colorBalance.x == 0.0f && colorBalance.y == 0.0f) { - colorBalance = vec2(1.0f, 1.0f); - } + if(colorBalance.x == 0.0f && colorBalance.y == 0.0f) + colorBalance = vec2(1.0f, 1.0f); float maxBalanceRatio = max(colorBalance.r, _colorBalance.g); color.r *= colorBalance.r / maxBalanceRatio; @@ -1993,6 +1991,6 @@ const std::string FRAGMENT_SHADER_WINDOW{R"( const std::string FRAGMENT_SHADER_EMPTY = "void main() {}"; -} // namespace Splash +} // namespace Splash::ShaderSources #endif // SPLASH_SHADERSOURCES_H diff --git a/src/image/image_shmdata.cpp b/src/image/image_shmdata.cpp index cc138a0e..4aef1a3e 100644 --- a/src/image/image_shmdata.cpp +++ b/src/image/image_shmdata.cpp @@ -277,6 +277,7 @@ void Image_Shmdata::readUncompressedFrame(void* data, int /*data_size*/) { spec.format = "UYVY"; spec.bpp = 16; + spec.channels = 2; } _readerBuffer = ImageBuffer(spec); From dd99f86bd20103a214dad644c85edf4dd217ddac Mon Sep 17 00:00:00 2001 From: Nicolas Bouillot Date: Thu, 8 Feb 2024 15:32:39 +0000 Subject: [PATCH 05/17] Fix typo in README.md, building Splash --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9761c3a0..e9506e9d 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ mkdir -p build && cd build # The BUILD_GENERIC_ARCH flag allows for building an executable which can run on any # sufficiently modern (less than 15 years) CPU. It is usually safe to remove it but # people had issues in the past with some arch-specific flags -cmake -GNinja -GBUILD_GENERIC_ARCH=ON .. +cmake -GNinja -DBUILD_GENERIC_ARCH=ON .. ninja ``` @@ -169,7 +169,7 @@ mkdir -p build && cd build # The BUILD_GENERIC_ARCH flag allows for building an executable which can run on any # sufficiently modern (less than 15 years) CPU. It is usually safe to remove it but # people had issues in the past with some arch-specific flags -cmake -DUSE_SYSTEM_LIBS=ON -GBUILD_GENERIC_ARCH=ON .. +cmake -DUSE_SYSTEM_LIBS=ON -DBUILD_GENERIC_ARCH=ON .. ninja ``` From 59265c393a55992560a705669d516316f90bc888 Mon Sep 17 00:00:00 2001 From: Nicolas Bouillot Date: Fri, 9 Feb 2024 23:32:44 -0500 Subject: [PATCH 06/17] Added Image_Sh4lt --- CMakeLists.txt | 3 + src/CMakeLists.txt | 9 + src/config.h.cmake | 3 + src/core/factory.cpp | 20 +++ src/image/image_sh4lt.cpp | 347 ++++++++++++++++++++++++++++++++++++++ src/image/image_sh4lt.h | 140 +++++++++++++++ src/utils/osutils.h | 19 +++ tests/CMakeLists.txt | 2 + 8 files changed, 543 insertions(+) create mode 100644 src/image/image_sh4lt.cpp create mode 100644 src/image/image_sh4lt.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 063b74b7..2c6fb102 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,7 @@ if (BUILD_CODE) pkg_search_module(GPHOTO libgphoto2) pkg_search_module(JACK jack) pkg_search_module(PORTAUDIO portaudio-2.0) + pkg_search_module(SH4LT sh4lt-0.0) pkg_search_module(SHMDATA shmdata-1.3) pkg_search_module(CALIMIRO calimiro-0.2) endif() @@ -127,6 +128,7 @@ endif() set(HAVE_GPHOTO ${GPHOTO_FOUND}) set(HAVE_OPENCV ${OPENCV_FOUND}) set(HAVE_PORTAUDIO ${PORTAUDIO_FOUND}) +set(HAVE_SH4LT ${SH4LT_FOUND}) set(HAVE_SHMDATA ${SHMDATA_FOUND}) set(HAVE_PYTHON ${Python3_FOUND}) set(HAVE_CALIMIRO ${CALIMIRO_FOUND}) @@ -274,6 +276,7 @@ info_cfg_option(PORTAUDIO_VERSION) info_cfg_option(JACK_VERSION) info_cfg_option(Python3_VERSION) info_cfg_option(JSONCPP_VERSION) +info_cfg_option(SH4LT_VERSION) info_cfg_option(SHMDATA_VERSION) info_cfg_option(CALIMIRO_VERSION) info_cfg_option(ZMQ_VERSION) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 69a768e2..86ad8e3c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -103,6 +103,7 @@ include_directories(${GLFW_INCLUDE_DIRS}) include_directories(${GSL_INCLUDE_DIRS}) include_directories(${ZMQ_INCLUDE_DIRS}) include_directories(${JSONCPP_INCLUDE_DIRS}) +include_directories(${SH4LT_INCLUDE_DIRS}) include_directories(${SHMDATA_INCLUDE_DIRS}) include_directories(${GPHOTO_INCLUDE_DIRS}) include_directories(${FFMPEG_INCLUDE_DIRS}) @@ -119,6 +120,7 @@ link_directories(${GLFW_LIBRARY_DIRS}) link_directories(${JSONCPP_LIBRARY_DIRS}) link_directories(${GSL_LIBRARY_DIRS}) +link_directories(${SH4LT_LIBRARY_DIRS}) link_directories(${SHMDATA_LIBRARY_DIRS}) link_directories(${GPHOTO_LIBRARY_DIRS}) link_directories(${PORTAUDIO_LIBRARY_DIRS}) @@ -302,6 +304,12 @@ if (CALIMIRO_FOUND AND OPENCV_FOUND) ) endif() +if (HAVE_SH4LT) + target_sources(splash-${API_VERSION} PRIVATE + image/image_sh4lt.cpp + ) +endif() + if (HAVE_SHMDATA) target_sources(splash-${API_VERSION} PRIVATE image/image_ndi.cpp @@ -362,6 +370,7 @@ target_link_libraries(splash-${API_VERSION} ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(splash-${API_VERSION} ${JSONCPP_LIBRARIES}) target_link_libraries(splash-${API_VERSION} ${FFMPEG_LIBRARIES}) target_link_libraries(splash-${API_VERSION} ${GSL_LIBRARIES}) +target_link_libraries(splash-${API_VERSION} ${SH4LT_LIBRARIES}) target_link_libraries(splash-${API_VERSION} ${SHMDATA_LIBRARIES}) target_link_libraries(splash-${API_VERSION} ${GPHOTO_LIBRARIES}) target_link_libraries(splash-${API_VERSION} ${PORTAUDIO_LIBRARIES}) diff --git a/src/config.h.cmake b/src/config.h.cmake index 3e75f04d..c6854247 100644 --- a/src/config.h.cmake +++ b/src/config.h.cmake @@ -19,6 +19,9 @@ /* Defined to 1 if jack is detected */ #cmakedefine01 HAVE_JACK +/* Defined to 1 if sh4lt is detected */ +#cmakedefine01 HAVE_SH4LT + /* Defined to 1 if shmdata-1.0 is detected */ #cmakedefine01 HAVE_SHMDATA diff --git a/src/core/factory.cpp b/src/core/factory.cpp index 4af636de..5d27780f 100644 --- a/src/core/factory.cpp +++ b/src/core/factory.cpp @@ -34,6 +34,10 @@ #include "./image/image_gphoto.h" #endif +#if HAVE_SH4LT +#include "./image/image_sh4lt.h" +#endif + #if HAVE_SHMDATA #include "./image/image_ndi.h" #include "./image/image_shmdata.h" @@ -302,6 +306,22 @@ void Factory::registerObjects() true); #endif +#if HAVE_SH4LT + _objectBook["image_sh4lt"] = Page( + [&](RootObject* root) { + std::shared_ptr object; + if (!_scene) + object = std::dynamic_pointer_cast(std::make_shared(root)); + else + object = std::dynamic_pointer_cast(std::make_shared(root)); + return object; + }, + GraphObject::Category::IMAGE, + "video through Sh4lt (shared memory)", + "Image object reading frames from a Sh4lt shared memory.", + true); +#endif + #if HAVE_SHMDATA _objectBook["image_ndi"] = Page( [&](RootObject* root) { diff --git a/src/image/image_sh4lt.cpp b/src/image/image_sh4lt.cpp new file mode 100644 index 00000000..f434e317 --- /dev/null +++ b/src/image/image_sh4lt.cpp @@ -0,0 +1,347 @@ +#include "./image_sh4lt.h" + +#include + +#include + +// All existing 64bits x86 CPUs have SSE2 +#if __x86_64__ +#define GLM_FORCE_SSE2 +#define GLM_FORCE_INLINE +#include +#else +#define GLM_FORCE_INLINE +#include +#endif + +#include "./utils/cgutils.h" +#include "./utils/log.h" +#include "./utils/osutils.h" +#include "./utils/timer.h" + +namespace Splash +{ + +/*************/ +Image_Sh4lt::Image_Sh4lt(RootObject* root) + : Image(root) +{ + init(); +} + +/*************/ +Image_Sh4lt::~Image_Sh4lt() +{ + _reader.reset(); +#ifdef DEBUG + Log::get() << Log::DEBUGGING << "Image_Sh4lt::~Image_Sh4lt - Destructor" << Log::endl; +#endif +} + +/*************/ +bool Image_Sh4lt::read(const std::string& filename) +{ + _reader = std::make_unique( + filename, + [&](void* data, size_t size, const sh4lt::Time::info_t*) { + onData(data, size); + }, + [&](const sh4lt::ShType& caps) { onShType(caps); }, + [&]() {}, + &_logger); + + return true; +} + +/*************/ +bool Image_Sh4lt::read_by_label() +{ + _reader = std::make_unique( + sh4lt::ShType::get_path(_label, _group), + [&](void* data, size_t size, const sh4lt::Time::info_t*) { + onData(data, size); + }, + [&](const sh4lt::ShType& caps) { onShType(caps); }, + [&]() {}, + &_logger); + + return true; +} + +/*************/ +// Small function to work around a bug in GCC's libstdc++ +void Image_Sh4lt::removeExtraParenthesis(std::string& str) +{ + if (str.find(")") == 0) + str = str.substr(1); +} + +/*************/ +void Image_Sh4lt::init() +{ + _type = "image_sh4lt"; + registerAttributes(); + + // This is used for getting documentation "offline" + if (!_root) + return; +} + +/*************/ +void Image_Sh4lt::onShType(const sh4lt::ShType& shtype) +{ + Log::get() << Log::MESSAGE << "Image_Sh4lt::" << __FUNCTION__ << " - Trying to connect with the following ShType: " << sh4lt::ShType::serialize(shtype) << Log::endl; + + _bpp = 0; + _width = 0; + _height = 0; + _red = 0; + _green = 0; + _blue = 0; + _channels = 0; + _isHap = false; + _isYUV = false; + _is420 = false; + _is422 = false; + + // regVideo = std::regex("(.*video/x-raw)(.*)", std::regex_constants::extended); + // regHap = std::regex("(.*video/x-gst-fourcc-HapY)(.*)", std::regex_constants::extended); + // regFormat = std::regex("(.*format=\\(string\\))(.*)", std::regex_constants::extended); + // regWidth = std::regex("(.*width=)(.*)", std::regex_constants::extended); + // regHeight = std::regex("(.*height=)(.*)", std::regex_constants::extended); + + if (shtype.media() == "video/x-raw") + { + const auto format = shtype.get_prop("format").as(); + if ("BGR" == format) + { + _bpp = 24; + _channels = 3; + _red = 2; + _green = 1; + _blue = 0; + } + else if ("RGB" == format) + { + _bpp = 24; + _channels = 3; + _red = 0; + _green = 1; + _blue = 2; + } + else if ("RGBA" == format) + { + _bpp = 32; + _channels = 4; + _red = 2; + _green = 1; + _blue = 0; + } + else if ("BGRA" == format) + { + _bpp = 32; + _channels = 4; + _red = 0; + _green = 1; + _blue = 2; + } + else if ("I420" == format) + { + _bpp = 12; + _channels = 3; + _isYUV = true; + _is420 = true; + } + else if ("UYVY" == format) + { + _bpp = 12; + _channels = 3; + _isYUV = true; + _is422 = true; + } + } + + if (shtype.media() == "video/x-gst-fourcc-HapY") + { + _isHap = true; + } + + _width = shtype.get_prop("width").as(); + _height = shtype.get_prop("height").as(); + + Log::get() << Log::MESSAGE << "Image_Sh4lt::" << __FUNCTION__ << " - Connection successful" << Log::endl; +} + +/*************/ +void Image_Sh4lt::onData(void* data, int data_size) +{ + if (Timer::get().isDebug()) + { + Timer::get() << "image_sh4lt " + _name; + } + + // Standard images, RGB or YUV + if (_width != 0 && _height != 0 && _bpp != 0 && _channels != 0) + { + readUncompressedFrame(data, data_size); + } + // Hap compressed images + else if (_isHap == true) + { + readHapFrame(data, data_size); + } + + if (Timer::get().isDebug()) + Timer::get() >> ("image_sh4lt " + _name); +} + +/*************/ +void Image_Sh4lt::readHapFrame(void* data, int data_size) +{ + // We are using kind of a hack to store a DXT compressed image in an ImageBuffer + // First, we check the texture format type + auto textureFormat = std::string(""); + if (!hapDecodeFrame(data, data_size, nullptr, 0, textureFormat)) + return; + + // Check if we need to resize the reader buffer + // We set the size so as to have just enough place for the given texture format + auto bufSpec = _readerBuffer.getSpec(); + if (bufSpec.width != _width || (bufSpec.height != _height && textureFormat != _textureFormat)) + { + _textureFormat = textureFormat; + + ImageBufferSpec spec; + if (textureFormat == "RGB_DXT1") + spec = ImageBufferSpec(_width, (int)(ceil((float)_height / 2.f)), 1, 8, ImageBufferSpec::Type::UINT8); + else if (textureFormat == "RGBA_DXT5") + spec = ImageBufferSpec(_width, _height, 1, 8, ImageBufferSpec::Type::UINT8); + else if (textureFormat == "YCoCg_DXT5") + spec = ImageBufferSpec(_width, _height, 1, 8, ImageBufferSpec::Type::UINT8); + else + return; + + spec.format = textureFormat; + _readerBuffer = ImageBuffer(spec); + } + + unsigned long outputBufferBytes = bufSpec.width * bufSpec.height * bufSpec.channels; + if (!hapDecodeFrame(data, data_size, _readerBuffer.data(), outputBufferBytes, textureFormat)) + return; + + { + std::lock_guard updateLock(_updateMutex); + if (!_bufferImage) + _bufferImage = std::make_unique(); + std::swap(*(_bufferImage), _readerBuffer); + _bufferImageUpdated = true; + } + updateTimestamp(); +} + +/*************/ +void Image_Sh4lt::readUncompressedFrame(void* data, int /*data_size*/) +{ + // Check if we need to resize the reader buffer + auto bufSpec = _readerBuffer.getSpec(); + if (bufSpec.width != _width || bufSpec.height != _height || bufSpec.channels != _channels) + { + ImageBufferSpec spec(_width, _height, _channels, 8 * _channels, ImageBufferSpec::Type::UINT8); + if (_green < _blue) + spec.format = "BGR"; + else + spec.format = "RGB"; + if (_channels == 4) + spec.format.push_back('A'); + + if (_is420 || _is422) + { + spec.format = "UYVY"; + spec.bpp = 16; + spec.channels = 2; + } + + _readerBuffer = ImageBuffer(spec); + } + + if (!_isYUV && (_channels == 3 || _channels == 4)) + { + char* pixels = (char*)(_readerBuffer).data(); + std::vector> threads; + for (uint32_t block = 0; block < _sh4ltCopyThreads; ++block) + { + int size = _width * _height * _channels * sizeof(char); + threads.push_back(std::async(std::launch::async, [=]() { + int sizeOfBlock; // We compute the size of the block, to handle image size non divisible by _sh4ltCopyThreads + if (size - size / _sh4ltCopyThreads * block < 2 * size / _sh4ltCopyThreads) + sizeOfBlock = size - size / _sh4ltCopyThreads * block; + else + sizeOfBlock = size / _sh4ltCopyThreads; + + memcpy(pixels + size / _sh4ltCopyThreads * block, (const char*)data + size / _sh4ltCopyThreads * block, sizeOfBlock); + })); + } + } + else if (_is420) + { + const unsigned char* Y = static_cast(data); + const unsigned char* U = static_cast(data) + _width * _height; + const unsigned char* V = static_cast(data) + _width * _height * 5 / 4; + char* pixels = (char*)(_readerBuffer).data(); + + for (uint32_t y = 0; y < _height; ++y) + { + for (uint32_t x = 0; x < _width; x += 2) + { + pixels[(x + y * _width) * 2 + 0] = U[(x / 2) + (y / 2) * (_width / 2)]; + pixels[(x + y * _width) * 2 + 1] = Y[x + y * _width]; + pixels[(x + y * _width) * 2 + 2] = V[(x / 2) + (y / 2) * (_width / 2)]; + pixels[(x + y * _width) * 2 + 3] = Y[x + y * _width + 1]; + } + } + } + else if (_is422) + { + const unsigned char* YUV = static_cast(data); + char* pixels = (char*)(_readerBuffer).data(); + std::copy(YUV, YUV + _width * _height * 2, pixels); + } + else + return; + + { + std::lock_guard updateLock(_updateMutex); + if (!_bufferImage) + _bufferImage = std::make_unique(); + std::swap(*(_bufferImage), _readerBuffer); + _bufferImageUpdated = true; + } + updateTimestamp(); +} + +/*************/ +void Image_Sh4lt::registerAttributes() +{ + addAttribute( + "label", + [&](const Values& args) { + _label = args[0].as(); + return read_by_label(); + }, + [&]() -> Values { return {_label}; }, + {'s'}); + setAttributeDescription("label", "Label for the Sh4lt (applied if filename attribute is empty)"); + + addAttribute( + "group", + [&](const Values& args) { + _group = args[0].as(); + return read_by_label(); + }, + [&]() -> Values { return {_group}; }, + {'s'}); + setAttributeDescription("group", "Group for the Sh4lt (applied if filename attribute is empty)"); + + Image::registerAttributes(); +} +} // namespace Splash diff --git a/src/image/image_sh4lt.h b/src/image/image_sh4lt.h new file mode 100644 index 00000000..d9ab4e6d --- /dev/null +++ b/src/image/image_sh4lt.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2024 Splash authors + * + * This file is part of Splash. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Splash is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Splash. If not, see . + */ + +/* + * @image_sh4lt.h + * The Image_Sh4lt class + */ + +#ifndef SPLASH_IMAGE_SH4LT_H +#define SPLASH_IMAGE_SH4LT_H + +#include + +#include "./core/constants.h" + +#include "./image/image.h" +#include "./utils/osutils.h" + +namespace Splash +{ + +class Image_Sh4lt final : public Image +{ + public: + /** + * Constructor + */ + explicit Image_Sh4lt(RootObject* root); + + /** + * Destructor + */ + ~Image_Sh4lt() final; + + /** + * Constructors/operators + */ + Image_Sh4lt(const Image_Sh4lt&) = delete; + Image_Sh4lt& operator=(const Image_Sh4lt&) = delete; + Image_Sh4lt(Image_Sh4lt&&) = delete; + Image_Sh4lt& operator=(Image_Sh4lt&&) = delete; + + /** + * Set the path to read from + */ + bool read(const std::string& filename) final; + + private: + static const uint32_t _sh4ltCopyThreads = 2; + std::string _label{""}; + std::string _group{sh4lt::ShType::default_group()}; + Utils::Sh4ltLogger _logger; + std::unique_ptr _reader{nullptr}; + + ImageBuffer _readerBuffer; + uint32_t _bpp{0}; + uint32_t _width{0}; + uint32_t _height{0}; + uint32_t _red{0}; + uint32_t _green{0}; + uint32_t _blue{0}; + uint32_t _channels{0}; + bool _isHap{false}; + bool _isYUV{false}; + bool _is420{false}; + bool _is422{false}; + + // Hap specific attributes + std::string _textureFormat{""}; + + /** + * Compute some LUT (currently only the YCbCr to RGB one) + */ + void computeLUT(); + + /** + * Base init for the class + */ + void init(); + + /** + * Callback called when receiving a new caps + * \param dataType String holding the data type + */ + void onShType(const sh4lt::ShType& dataType); + + /** + * Construct the internal Sh4lt using label and group + **/ + bool read_by_label(); + + /** + * Callback called when receiving a new frame + * \param data Content of the frame + * \param data_size Size of the frame + * \param user_data User data + */ + void onData(void* data, int data_size); + + /** + * Read Hap compressed images + */ + void readHapFrame(void* data, int data_size); + + /** + * Read uncompressed RGB or YUV images + */ + void readUncompressedFrame(void* data, int data_size); + + /** + * Register new functors to modify attributes + */ + void registerAttributes(); + /** + * Small function to work around a bug in GCC's libstdc++ + * + * \param str The string that need extra parenthesis removal + */ + static void removeExtraParenthesis(std::string& str); +}; + +} // namespace Splash + +#endif // SPLASH_IMAGE_SH4LT_H diff --git a/src/utils/osutils.h b/src/utils/osutils.h index e753e993..c92fbb2f 100644 --- a/src/utils/osutils.h +++ b/src/utils/osutils.h @@ -46,6 +46,9 @@ #include "./core/constants.h" #include "./utils/log.h" +#if HAVE_SH4LT +#include +#endif #if HAVE_SHMDATA #include #endif @@ -337,6 +340,22 @@ inline std::string getCurrentExecutablePath() return currentExePath; } +#if HAVE_SH4LT +/** + * Sh4lt logger dedicated to splash + */ +class Sh4ltLogger : public sh4lt::logger::Logger +{ + private: + void on_error(std::string&& str) final { Log::get() << Log::ERROR << "Sh4lt::Sh4ltLogger - " << str << Log::endl; } + void on_critical(std::string&& str) final { Log::get() << Log::ERROR << "Sh4lt::Sh4ltLogger - " << str << Log::endl; } + void on_warning(std::string&& str) final { Log::get() << Log::WARNING << "Sh4lt::Sh4ltLogger - " << str << Log::endl; } + void on_message(std::string&& str) final { Log::get() << Log::MESSAGE << "Sh4lt::Sh4ltLogger - " << str << Log::endl; } + void on_info(std::string&& str) final { Log::get() << Log::MESSAGE << "Sh4lt::Sh4ltLogger - " << str << Log::endl; } + void on_debug(std::string&& str) final { Log::get() << Log::DEBUGGING << "Sh4lt::Sh4ltLogger - " << str << Log::endl; } +}; +#endif + #if HAVE_SHMDATA /** * Shmdata logger dedicated to splash diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a55fbfc9..2fb00980 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -49,6 +49,7 @@ include_directories(${GLFW_INCLUDE_DIRS}) include_directories(${GSL_INCLUDE_DIRS}) include_directories(${ZMQ_INCLUDE_DIRS}) include_directories(${JSONCPP_INCLUDE_DIRS}) +include_directories(${SH4LT_INCLUDE_DIRS}) include_directories(${SHMDATA_INCLUDE_DIRS}) include_directories(${GPHOTO_INCLUDE_DIRS}) include_directories(${FFMPEG_INCLUDE_DIRS}) @@ -66,6 +67,7 @@ link_directories(${GLFW_LIBRARY_DIRS}) link_directories(${GSL_LIBRARY_DIRS}) link_directories(${JSONCPP_LIBRARY_DIRS}) +link_directories(${SH4LT_LIBRARY_DIRS}) link_directories(${SHMDATA_LIBRARY_DIRS}) link_directories(${GPHOTO_LIBRARY_DIRS}) link_directories(${PORTAUDIO_LIBRARY_DIRS}) From 873fd9bf9328514d7187cef8de92a5cc99898c3d Mon Sep 17 00:00:00 2001 From: Nicolas Bouillot Date: Sun, 11 Feb 2024 11:23:14 -0500 Subject: [PATCH 07/17] Added sink_sh4lt and sink_sh4lt_encoded --- src/CMakeLists.txt | 2 + src/core/factory.cpp | 14 ++ src/sink/sink_sh4lt.cpp | 70 +++++++ src/sink/sink_sh4lt.h | 68 +++++++ src/sink/sink_sh4lt_encoded.cpp | 327 ++++++++++++++++++++++++++++++++ src/sink/sink_sh4lt_encoded.h | 143 ++++++++++++++ 6 files changed, 624 insertions(+) create mode 100644 src/sink/sink_sh4lt.cpp create mode 100644 src/sink/sink_sh4lt.h create mode 100644 src/sink/sink_sh4lt_encoded.cpp create mode 100644 src/sink/sink_sh4lt_encoded.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 86ad8e3c..a83db0d9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -307,6 +307,8 @@ endif() if (HAVE_SH4LT) target_sources(splash-${API_VERSION} PRIVATE image/image_sh4lt.cpp + sink/sink_sh4lt.cpp + sink/sink_sh4lt_encoded.cpp ) endif() diff --git a/src/core/factory.cpp b/src/core/factory.cpp index 5d27780f..447b0555 100644 --- a/src/core/factory.cpp +++ b/src/core/factory.cpp @@ -36,6 +36,8 @@ #if HAVE_SH4LT #include "./image/image_sh4lt.h" +#include "./sink/sink_sh4lt.h" +#include "./sink/sink_sh4lt_encoded.h" #endif #if HAVE_SHMDATA @@ -395,6 +397,18 @@ void Factory::registerObjects() "sink a texture to a host buffer", "Get the texture content to a host buffer. Only used internally."); +#if HAVE_SH4LT + _objectBook["sink_sh4lt"] = Page([&](RootObject* root) { return std::dynamic_pointer_cast(std::make_shared(root)); }, + GraphObject::Category::MISC, + "sink a texture to a Sh4lt", + "Outputs texture to a Sh4lt shared memory."); + + _objectBook["sink_sh4lt_encoded"] = Page([&](RootObject* root) { return std::dynamic_pointer_cast(std::make_shared(root)); }, + GraphObject::Category::MISC, + "sink a texture as an encoded video to a Sh4lt shared memory", + "Outputs texture as a compressed frame to a Sh4lt shared memory."); +#endif + #if HAVE_SHMDATA _objectBook["sink_shmdata"] = Page([&](RootObject* root) { return std::dynamic_pointer_cast(std::make_shared(root)); }, GraphObject::Category::MISC, diff --git a/src/sink/sink_sh4lt.cpp b/src/sink/sink_sh4lt.cpp new file mode 100644 index 00000000..f0e40dd6 --- /dev/null +++ b/src/sink/sink_sh4lt.cpp @@ -0,0 +1,70 @@ +#include "./sink/sink_sh4lt.h" + +#include + +namespace Splash +{ + +/*************/ +Sink_Sh4lt::Sink_Sh4lt(RootObject* root) + : Sink(root) +{ + _type = "sink_sh4lt"; + registerAttributes(); +} + +/*************/ +void Sink_Sh4lt::handlePixels(const char* pixels, const ImageBufferSpec& spec) +{ + auto size = spec.rawSize(); + if (!pixels || size == 0) + return; + + if (!_writer || spec != _previousSpec || _framerate != _previousFramerate) + { + _previousSpec = spec; + _previousFramerate = _framerate; + _shtype = sh4lt::shtype::shtype_from_gst_caps(getCaps(), _label, _group); + _writer.reset(); + _writer = std::make_unique(_shtype, size, &_logger); + } + + if (_writer) + { + // Sh4lt timestamp is expressed in nanoseconds, while Splash in microseconds + _writer->copy_to_shm(pixels, size, 1000 * spec.timestamp, 1000000000 / _framerate); + } +} + +/*************/ +void Sink_Sh4lt::registerAttributes() +{ + Sink::registerAttributes(); + addAttribute( + "group", + [&](const Values& args) { + _group = args[0].as(); + _previousSpec = ImageBufferSpec(); + return true; + }, + [&]() -> Values { return {_group}; }, + {'s'}); + setAttributeDescription("group", "Sh4lt group label"); + + addAttribute( + "label", + [&](const Values& args) { + _label = args[0].as(); + _previousSpec = ImageBufferSpec(); + return true; + }, + [&]() -> Values { return {_label}; }, + {'s'}); + setAttributeDescription("label", "Sh4lt group label"); + + addAttribute( + "sh4lt type", [&](const Values&) { return true; }, [&]() -> Values { return {sh4lt::shtype::shtype_to_gst_caps(_shtype)}; }, {'s'}); + setAttributeDescription("sh4lt type", "format of the data sent to the Sh4lt"); +} + +} // namespace Splash diff --git a/src/sink/sink_sh4lt.h b/src/sink/sink_sh4lt.h new file mode 100644 index 00000000..3f913d3b --- /dev/null +++ b/src/sink/sink_sh4lt.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 Splash authors + * + * This file is part of Splash. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Splash is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Splash. If not, see . + */ + +/* + * @sink_sh4lt.h + * The Sink_Sh4lt class, sending the connected object to sh4lt + */ + +#ifndef SPLASH_SINK_SH4LT_H +#define SPLASH_SINK_SH4LT_H + +#include + +#include "./sink/sink.h" +#include "./utils/osutils.h" + +namespace Splash +{ + +class Sink_Sh4lt final : public Sink +{ + public: + /** + * Constructor + */ + explicit Sink_Sh4lt(RootObject* root); + + private: + std::string _group{sh4lt::ShType::default_group()}; + std::string _label{"splash"}; + sh4lt::ShType _shtype{}; + Utils::Sh4ltLogger _logger; + std::unique_ptr _writer{nullptr}; + ImageBufferSpec _previousSpec{}; + uint32_t _previousFramerate{0}; + + /** + * Class to be implemented to copy the _mappedPixels somewhere + * \param pixels Input image + * \param spec Input image specifications + */ + void handlePixels(const char* pixels, const ImageBufferSpec& spec) final; + + /** + * Register new functors to modify attributes + */ + void registerAttributes(); +}; + +} // namespace Splash + +#endif diff --git a/src/sink/sink_sh4lt_encoded.cpp b/src/sink/sink_sh4lt_encoded.cpp new file mode 100644 index 00000000..41703921 --- /dev/null +++ b/src/sink/sink_sh4lt_encoded.cpp @@ -0,0 +1,327 @@ +#include "./sink/sink_sh4lt_encoded.h" + +#include +#include + +#include + +#include "./utils/timer.h" + +namespace Splash +{ + +/*************/ +Sink_Sh4lt_Encoded::Sink_Sh4lt_Encoded(RootObject* root) + : Sink(root) +{ + _type = "sink_sh4lt_encoded"; + registerAttributes(); +} + +/*************/ +Sink_Sh4lt_Encoded::~Sink_Sh4lt_Encoded() +{ + freeFFmpegObjects(); +} + +/*************/ +const AVCodec* Sink_Sh4lt_Encoded::findEncoderByName(const std::string& codecName) +{ + const AVCodec* codec{nullptr}; + + if (codecName == "h264") + { + codec = avcodec_find_encoder_by_name("h264_nvenc"); + if (!codec) + codec = avcodec_find_encoder_by_name("h264_vaapi"); + if (!codec) + codec = avcodec_find_encoder_by_name("h264_amf"); + if (!codec) + codec = avcodec_find_encoder_by_name("libx264"); + } + else if (codecName == "hevc") + { + codec = avcodec_find_encoder_by_name("hvec_nvenc"); + if (!codec) + codec = avcodec_find_encoder_by_name("hevc_vaapi"); + if (!codec) + codec = avcodec_find_encoder_by_name("hevc_amf"); + if (!codec) + codec = avcodec_find_encoder_by_name("libx265"); + } + + return codec; +} + +/*************/ +const std::unordered_map Sink_Sh4lt_Encoded::parseOptions(const std::string& options) +{ + static const std::regex re("(([^=]+)=([^ ,]+))+"); + const auto sre_begin = std::sregex_iterator(options.begin(), options.end(), re); + const auto sre_end = std::sregex_iterator(); + + std::unordered_map result; + for (auto i = sre_begin; i != sre_end; ++i) + { + const std::smatch match = *i; + const auto key = std::string(match[2]); + const auto value = std::string(match[3]); + result[key] = value; + } + + return result; +} + +/*************/ +bool Sink_Sh4lt_Encoded::initFFmpegObjects(const ImageBufferSpec& spec) +{ + _codec = findEncoderByName(_codecName); + if (!_codec) + { + Log::get() << Log::WARNING << "Sink_Sh4lt_Encoded::" << __FUNCTION__ << " - Unable to find encoder for codec " << _codecName << Log::endl; + return false; + } + + _context = avcodec_alloc_context3(_codec); + if (!_context) + { + Log::get() << Log::WARNING << "Sink_Sh4lt_Encoded::" << __FUNCTION__ << " - Unable to allocate video codec context for codec " << _codecName << Log::endl; + return false; + } + + _context->bit_rate = _bitRate; + _context->width = spec.width; + _context->height = spec.height; + _context->time_base = (AVRational){1, static_cast(_framerate)}; + _context->sample_aspect_ratio = (AVRational){static_cast(spec.width), static_cast(spec.height)}; + _context->pix_fmt = AV_PIX_FMT_YUV420P; + + const auto options = parseOptions(_options); + for (auto& option : options) + av_opt_set(_context->priv_data, option.first.c_str(), option.second.c_str(), 0); + + if (avcodec_open2(_context, _codec, nullptr) < 0) + { + Log::get() << Log::WARNING << "Sink_Sh4lt_Encoded::" << __FUNCTION__ << " - Unable to open codec " << _codecName << Log::endl; + return false; + } + + _swsContext = sws_getContext(spec.width, spec.height, AV_PIX_FMT_RGB32, spec.width, spec.height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, nullptr, nullptr, nullptr); + + _frame = av_frame_alloc(); + _yuvFrame = av_frame_alloc(); + if (!_frame || !_yuvFrame) + { + Log::get() << Log::WARNING << "Sink_Sh4lt_Encoded::" << __FUNCTION__ << " - Unable to allocate frame" << Log::endl; + return false; + } + + _frame->format = AV_PIX_FMT_RGB32; + _frame->width = spec.width; + _frame->height = spec.height; + if (av_image_alloc(_frame->data, _frame->linesize, _context->width, _context->height, AV_PIX_FMT_RGB32, 32) < 0) + { + Log::get() << Log::WARNING << "Sink_Sh4lt_Encoded::" << __FUNCTION__ << " - Unable to allocate raw RGBA picture buffer" << Log::endl; + return false; + } + + _yuvFrame->format = AV_PIX_FMT_YUV420P; + _yuvFrame->width = spec.width; + _yuvFrame->height = spec.height; + if (av_image_alloc(_yuvFrame->data, _yuvFrame->linesize, _context->width, _context->height, AV_PIX_FMT_YUV420P, 32) < 0) + { + Log::get() << Log::WARNING << "Sink_Sh4lt_Encoded::" << __FUNCTION__ << " - Unable to allocate raw YUV420 picture buffer" << Log::endl; + return false; + } + + _startTime = Timer::get().getTime(); + return true; +} + +/*************/ +void Sink_Sh4lt_Encoded::freeFFmpegObjects() +{ + if (_context) + { + avcodec_close(_context); + av_free(_context); + _context = nullptr; + } + + if (_frame) + av_frame_free(&_frame); + if (_yuvFrame) + av_frame_free(&_yuvFrame); + + if (_swsContext) + sws_freeContext(_swsContext); +} + +/*************/ +std::string Sink_Sh4lt_Encoded::generateCaps(const ImageBufferSpec& spec, uint32_t framerate, const std::string& optionString, const std::string& codecName, AVCodecContext* ctx) +{ + if (!ctx) + return "video/x-raw"; + + auto codec = std::string(reinterpret_cast(&ctx->codec_tag), 4); + if (ctx->codec_tag == 0) + codec = "x-" + codecName; + + auto profile = std::string("baseline"); + + const auto options = parseOptions(optionString); + auto optionIt = options.find("profile"); + if (optionIt != options.end()) + { + profile = optionIt->second; + } + else + { + uint8_t* profileStr{nullptr}; + if (av_opt_get(ctx, "profile", 0, &profileStr)) + profile = std::string(reinterpret_cast(profileStr)); + } + + auto caps = "video/" + codec + ",stream-format=(string)byte_stream,alignment=(string)au,profile=(string)" + profile + ",width=(int)" + std::to_string(spec.width) + + ",height=(int)" + std::to_string(spec.height) + ",pixel-aspect-ratio=(fraction)1/1,framerate=(fraction)" + std::to_string(framerate) + "/1"; + + return caps; +} + +/*************/ +void Sink_Sh4lt_Encoded::handlePixels(const char* pixels, const ImageBufferSpec& spec) +{ + auto size = spec.rawSize(); + if (!pixels || size == 0) + return; + + if (_resetEncoding || !_context || !_writer || spec != _previousSpec || _previousFramerate != _framerate) + { + _resetEncoding = false; + + // Reset FFmpeg context and stuff + freeFFmpegObjects(); + if (!initFFmpegObjects(spec)) + return; + + // Reset sh4lt writer + _shtype = sh4lt::shtype::shtype_from_gst_caps(generateCaps(spec, _framerate, _options, _codecName, _context), _label, _group); + _writer.reset(); + _writer = std::make_unique(_shtype, size, &_logger); + + _previousSpec = spec; + _previousFramerate = _framerate; + } + + // Encoding + AVPacket* packet = av_packet_alloc(); + if (!packet) + { + Log::get() << Log::ERROR << "Sink_Sh4lt_Encoded::" << __FUNCTION__ << " - Can not allocate memory for encoding frame" << Log::endl; + } + + packet->data = nullptr; + packet->size = 0; + + av_image_fill_arrays(_frame->data, _frame->linesize, reinterpret_cast(pixels), AV_PIX_FMT_RGB32, spec.width, spec.height, 1); + sws_scale(_swsContext, _frame->data, _frame->linesize, 0, spec.height, _yuvFrame->data, _yuvFrame->linesize); + + _yuvFrame->pts = (static_cast((Timer::get().getTime() - _startTime)) / 1e3) / _framerate; + _yuvFrame->quality = _context->global_quality; + _yuvFrame->pict_type = AV_PICTURE_TYPE_NONE; + + auto ret = avcodec_send_frame(_context, _yuvFrame); + if (ret < 0) + { + Log::get() << Log::WARNING << "Sink_Sh4lt_Encoded::" << __FUNCTION__ << " - Error encoding frame" << Log::endl; + return; + } + + while (1) + { + ret = avcodec_receive_packet(_context, packet); + if (ret == AVERROR(EAGAIN)) + break; + else if (ret < 0) + return; + + // Sending through sh4lt + if (_writer && packet->size != 0) + { + // Sh4lt timestamp is expressed in nanoseconds, while Splash in microseconds + _writer->copy_to_shm(packet->data, packet->size, 1000 * spec.timestamp, 1000000000 / _framerate); + } + } + + av_packet_unref(packet); +} + +/*************/ +void Sink_Sh4lt_Encoded::registerAttributes() +{ + Sink::registerAttributes(); + + addAttribute( + "group", + [&](const Values& args) { + _group = args[0].as(); + _previousSpec = ImageBufferSpec(); + return true; + }, + [&]() -> Values { return {_group}; }, + {'s'}); + setAttributeDescription("group", "Sh4lt group label"); + + addAttribute( + "label", + [&](const Values& args) { + _label = args[0].as(); + _previousSpec = ImageBufferSpec(); + return true; + }, + [&]() -> Values { return {_label}; }, + {'s'}); + setAttributeDescription("label", "Sh4lt group label"); + + addAttribute( + "bitrate", + [&](const Values& args) { + _bitRate = std::max(1000000, args[0].as()); + _resetEncoding = true; + return true; + }, + [&]() -> Values { return {_bitRate}; }, + {'i'}); + setAttributeDescription("bitrate", "Output encoded video target bitrate"); + + addAttribute("sh4lt type", [&]() -> Values { return {sh4lt::shtype::shtype_to_gst_caps(_shtype)}; }); + setAttributeDescription("sh4lt type", "format of the data sent to the Sh4lt"); + + addAttribute( + "codec", + [&](const Values& args) { + _codecName = args[0].as(); + transform(_codecName.begin(), _codecName.end(), _codecName.begin(), ::tolower); + _resetEncoding = true; + return true; + }, + [&]() -> Values { return {_codecName}; }, + {'s'}); + setAttributeDescription("codec", "Desired codec"); + + addAttribute( + "codecOptions", + [&](const Values& args) { + _options = args[0].as(); + _resetEncoding = true; + return true; + }, + [&]() -> Values { return {_options}; }, + {'s'}); + setAttributeDescription("codecOptions", + "Codec options as a string following the format: \"key1=value1, key2=value2, etc\".\n" + "Options can be listed with the following terminal command:\n" + "$ ffmpeg -h encoder=ENCODER_NAME"); +} + +} // namespace Splash diff --git a/src/sink/sink_sh4lt_encoded.h b/src/sink/sink_sh4lt_encoded.h new file mode 100644 index 00000000..36093580 --- /dev/null +++ b/src/sink/sink_sh4lt_encoded.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2024 Splash authors + * + * This file is part of Splash. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Splash is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Splash. If not, see . + */ + +/* + * @sink_sh4lt.h + * The Sink_Sh4lt_Encoded class, sending the connected object to sh4lt + */ + +#ifndef SPLASH_SINK_SH4LT_ENCODED_H +#define SPLASH_SINK_SH4LT_ENCODED_H + +#include + +#include + +extern "C" +{ +#include +#include +#include +#include +#include +} + +#include "./sink/sink.h" +#include "./utils/osutils.h" + +namespace Splash +{ + +class Sink_Sh4lt_Encoded final : public Sink +{ + public: + /** + * Constructor + */ + explicit Sink_Sh4lt_Encoded(RootObject* root); + + /** + * Destructor + */ + ~Sink_Sh4lt_Encoded() final; + + /** + * Constructors/operators + */ + Sink_Sh4lt_Encoded(const Sink_Sh4lt_Encoded&) = delete; + Sink_Sh4lt_Encoded& operator=(const Sink_Sh4lt_Encoded&) = delete; + Sink_Sh4lt_Encoded(Sink_Sh4lt_Encoded&&) = delete; + Sink_Sh4lt_Encoded& operator=(Sink_Sh4lt_Encoded&&) = delete; + + private: + std::string _group{sh4lt::ShType::default_group()}; + std::string _label{"splash encoded"}; + sh4lt::ShType _shtype{}; + Utils::Sh4ltLogger _logger; + std::unique_ptr _writer{nullptr}; + ImageBufferSpec _previousSpec{}; + uint32_t _previousFramerate{0}; + bool _resetEncoding{false}; + + // FFmpeg objects + const AVCodec* _codec{nullptr}; + AVCodecContext* _context{nullptr}; + AVFrame *_frame{nullptr}, *_yuvFrame{nullptr}; + SwsContext* _swsContext{nullptr}; + + // Codec parameters + int64_t _startTime{0ll}; + std::string _codecName{"h264"}; + int _bitRate{4000000}; + double _framerate{30.0}; + std::string _options{"profile=baseline"}; + + /** + * Find an encoder base on its name + * \param encoderName Codec name + * \return Return a codec + */ + const AVCodec* findEncoderByName(const std::string& codecName); + + /** + * Init FFmpeg objects + * \param spec Input image specifications + * \return Return true if all went well + */ + bool initFFmpegObjects(const ImageBufferSpec& spec); + + /** + * Free everything related to FFmpeg + */ + void freeFFmpegObjects(); + + /** + * Generate the caps from the spec, the context and the options + * \param spec Input image specifications + * \param framerate Expected framerate + * \param optionString String of the options sent to the encoder + * \param codecName Codec name + * \param ctx FFmpeg codec context + * \return Return a caps describing the output + */ + std::string generateCaps(const ImageBufferSpec& spec, uint32_t framerate, const std::string& optionString, const std::string& codecName, AVCodecContext* ctx); + + /** + * Class to be implemented to copy the _mappedPixels somewhere + * \param pixels Input image + * \param spec Input image specifications + */ + void handlePixels(const char* pixels, const ImageBufferSpec& spec) final; + + /** + * Parse the options from the given string, formatted as: + * option1=value1, option2=value2, etc + * \param options Encoding options + */ + const std::unordered_map parseOptions(const std::string& options); + + /** + * Register new functors to modify attributes + */ + void registerAttributes(); +}; + +} // namespace Splash + +#endif From 106577cc493843e1c4c454e556284692de5f2e25 Mon Sep 17 00:00:00 2001 From: Nicolas Bouillot Date: Sun, 11 Feb 2024 13:23:09 -0500 Subject: [PATCH 08/17] add sh4lt performance test --- tests/CMakeLists.txt | 6 ++ tests/performance_tests/perf_sh4lt.cpp | 85 +++++++++++++++++++++ tests/performance_tests/perf_zmq_inproc.cpp | 2 +- 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tests/performance_tests/perf_sh4lt.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2fb00980..749280f1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -216,6 +216,12 @@ add_executable(perf_dense_map performance_tests/perf_dense_map.cpp) target_link_libraries(perf_dense_map splash-${API_VERSION}) add_custom_command(OUTPUT run_perf_dense_map COMMAND ./perf_dense_map DEPENDS perf_dense_map) +if (HAVE_SH4LT) + add_executable(perf_sh4lt performance_tests/perf_sh4lt.cpp) + target_link_libraries(perf_sh4lt splash-${API_VERSION}) + add_custom_command(OUTPUT run_perf_sh4lt COMMAND ./perf_sh4lt DEPENDS perf_sh4lt) +endif() + if (HAVE_SHMDATA) add_executable(perf_shmdata performance_tests/perf_shmdata.cpp) target_link_libraries(perf_shmdata splash-${API_VERSION}) diff --git a/tests/performance_tests/perf_sh4lt.cpp b/tests/performance_tests/perf_sh4lt.cpp new file mode 100644 index 00000000..d058f8c5 --- /dev/null +++ b/tests/performance_tests/perf_sh4lt.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 Splash authors + * + * This file is part of Splash. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Splash is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Splash. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "./utils/osutils.h" + +const sh4lt::ShType shtype{/*media=*/"nomedia", /*label=*/"perf_sh4lt", /*group=*/"test"}; +const size_t shmsize = 1 << 26; +const size_t loopCount = 1 << 8; +Splash::Utils::Sh4ltLogger shmlogger; + +std::atomic_int bufferCount{0}; +std::mutex copyMutex{}; +std::condition_variable copyCondition{}; + +std::vector srcData((size_t)shmsize); +std::vector sinkData((size_t)shmsize); + +void onData(void* data, size_t size) +{ + std::unique_lock lock(copyMutex); + std::copy(static_cast(data), static_cast(data) + size, sinkData.data()); + std::cout << bufferCount << " received\n"; + bufferCount++; + copyCondition.notify_all(); +} + +int main() +{ + + sh4lt::Writer writer(shtype, shmsize, &shmlogger); + sh4lt::Follower follower( + shtype.path(), [&](void* data, size_t size, const sh4lt::Time::info_t*) { onData(data, size); }, nullptr, nullptr, &shmlogger); + + std::cout << "Preparing buffer data...\n"; + for (size_t i = 0; i < shmsize; ++i) + srcData[i] = static_cast(i % 256); + + const auto start = std::chrono::steady_clock::now(); + + for (size_t loopId = 0; loopId < loopCount; ++loopId) + { + std::unique_lock lock(copyMutex); + writer.copy_to_shm(srcData.data(), srcData.size(), -1, -1); + std::cout << "Buffer " << loopId << " sent... "; + + while (true) + { + copyCondition.wait(lock); + if (static_cast(bufferCount) > loopId) + break; + } + } + + const auto end = std::chrono::steady_clock::now(); + const auto duration = std::chrono::duration_cast(end - start).count(); + std::cout << "Sent " << loopCount << " buffers of size " << static_cast(shmsize) / static_cast(1 << 20) << " MB through sh4lt, in " << duration << " us\n"; + std::cout << "Bandwidth : " << (static_cast(loopCount * shmsize) / static_cast(1 << 20)) / (static_cast(duration) / 1000000.0) << " MB/sec\n"; +} diff --git a/tests/performance_tests/perf_zmq_inproc.cpp b/tests/performance_tests/perf_zmq_inproc.cpp index 87cafb1c..59fec293 100644 --- a/tests/performance_tests/perf_zmq_inproc.cpp +++ b/tests/performance_tests/perf_zmq_inproc.cpp @@ -106,7 +106,7 @@ int main() const auto end = std::chrono::steady_clock::now(); const auto duration = std::chrono::duration_cast(end - start).count(); - std::cout << "Sent " << loopCount << " buffers of size " << static_cast(bufferSize) / static_cast(1 << 20) << " MB through shmdata, in " << duration << " us\n"; + std::cout << "Sent " << loopCount << " buffers of size " << static_cast(bufferSize) / static_cast(1 << 20) << " MB through zmq, in " << duration << " us\n"; std::cout << "Bandwidth : " << (static_cast(loopCount * bufferSize) / static_cast(1 << 20)) / (static_cast(duration) / 1000000.0) << " MB/sec\n"; continueThread = false; From 9b91af044cfe4a1f33ba2291e2c95fc1909845ef Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Tue, 13 Feb 2024 15:58:19 -0500 Subject: [PATCH 09/17] Replaced Ubuntu 23.10 for 24.04 in CI --- .gitlab-ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0278d42d..64be5bb9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -121,7 +121,7 @@ analysis:static-analysis: extends: - .amd64 - .ubuntu - image: ubuntu:23.10 + image: ubuntu:24.04 script: - DEBIAN_FRONTEND=noninteractive apt install -y clang-tools - ./make_deps.sh @@ -202,12 +202,12 @@ staging:22.04:gcc-arm64-bundled-libs: rules: - if: $CI_COMMIT_BRANCH == 'staging' -staging:23.10:gcc-amd64-system-libs: +staging:24.04:gcc-amd64-system-libs: stage: staging extends: - .amd64 - .ubuntu - image: ubuntu:23.10 + image: ubuntu:24.04 script: - DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends libglm-dev libsnappy-dev libzmq3-dev @@ -222,12 +222,12 @@ staging:23.10:gcc-amd64-system-libs: rules: - if: $CI_COMMIT_BRANCH == 'staging' -staging:23.10:clang-amd64-system-libs: +staging:24.04:clang-amd64-system-libs: stage: staging extends: - .amd64 - .ubuntu - image: ubuntu:23.10 + image: ubuntu:24.04 script: - DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends libglm-dev libsnappy-dev libzmq3-dev clang @@ -372,11 +372,11 @@ package:debian:22.04: - if: $CI_COMMIT_TAG =~ /^\d+\.\d+\.\d+$/ - if: $CI_COMMIT_BRANCH == 'master' -package:debian:23.10: +package:debian:24.04: stage: package extends: - .ubuntu - image: ubuntu:23.10 + image: ubuntu:24.04 script: # Build package - ./make_deps.sh From 9439c79e0afc3958fade5e3c568c0265d6dec3e0 Mon Sep 17 00:00:00 2001 From: Nicolas Bouillot Date: Wed, 21 Feb 2024 14:00:57 -0500 Subject: [PATCH 10/17] port to sh4lt 0.1 --- CMakeLists.txt | 2 +- src/image/image_sh4lt.cpp | 14 +++----------- src/image/image_sh4lt.h | 1 - src/sink/sink_sh4lt.cpp | 2 +- src/sink/sink_sh4lt.h | 1 - src/sink/sink_sh4lt_encoded.cpp | 2 +- src/sink/sink_sh4lt_encoded.h | 1 - tests/performance_tests/perf_sh4lt.cpp | 6 +++--- 8 files changed, 9 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c6fb102..81a37b73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,7 +100,7 @@ if (BUILD_CODE) pkg_search_module(GPHOTO libgphoto2) pkg_search_module(JACK jack) pkg_search_module(PORTAUDIO portaudio-2.0) - pkg_search_module(SH4LT sh4lt-0.0) + pkg_search_module(SH4LT sh4lt-0.1) pkg_search_module(SHMDATA shmdata-1.3) pkg_search_module(CALIMIRO calimiro-0.2) endif() diff --git a/src/image/image_sh4lt.cpp b/src/image/image_sh4lt.cpp index f434e317..acc56710 100644 --- a/src/image/image_sh4lt.cpp +++ b/src/image/image_sh4lt.cpp @@ -43,12 +43,10 @@ bool Image_Sh4lt::read(const std::string& filename) { _reader = std::make_unique( filename, - [&](void* data, size_t size, const sh4lt::Time::info_t*) { - onData(data, size); - }, + [&](void* data, size_t size, const sh4lt::Time::info_t*) { onData(data, size); }, [&](const sh4lt::ShType& caps) { onShType(caps); }, [&]() {}, - &_logger); + std::make_shared()); return true; } @@ -63,7 +61,7 @@ bool Image_Sh4lt::read_by_label() }, [&](const sh4lt::ShType& caps) { onShType(caps); }, [&]() {}, - &_logger); + std::make_shared()); return true; } @@ -104,12 +102,6 @@ void Image_Sh4lt::onShType(const sh4lt::ShType& shtype) _is420 = false; _is422 = false; - // regVideo = std::regex("(.*video/x-raw)(.*)", std::regex_constants::extended); - // regHap = std::regex("(.*video/x-gst-fourcc-HapY)(.*)", std::regex_constants::extended); - // regFormat = std::regex("(.*format=\\(string\\))(.*)", std::regex_constants::extended); - // regWidth = std::regex("(.*width=)(.*)", std::regex_constants::extended); - // regHeight = std::regex("(.*height=)(.*)", std::regex_constants::extended); - if (shtype.media() == "video/x-raw") { const auto format = shtype.get_prop("format").as(); diff --git a/src/image/image_sh4lt.h b/src/image/image_sh4lt.h index d9ab4e6d..52bc5fee 100644 --- a/src/image/image_sh4lt.h +++ b/src/image/image_sh4lt.h @@ -65,7 +65,6 @@ class Image_Sh4lt final : public Image static const uint32_t _sh4ltCopyThreads = 2; std::string _label{""}; std::string _group{sh4lt::ShType::default_group()}; - Utils::Sh4ltLogger _logger; std::unique_ptr _reader{nullptr}; ImageBuffer _readerBuffer; diff --git a/src/sink/sink_sh4lt.cpp b/src/sink/sink_sh4lt.cpp index f0e40dd6..1c618a7e 100644 --- a/src/sink/sink_sh4lt.cpp +++ b/src/sink/sink_sh4lt.cpp @@ -26,7 +26,7 @@ void Sink_Sh4lt::handlePixels(const char* pixels, const ImageBufferSpec& spec) _previousFramerate = _framerate; _shtype = sh4lt::shtype::shtype_from_gst_caps(getCaps(), _label, _group); _writer.reset(); - _writer = std::make_unique(_shtype, size, &_logger); + _writer = std::make_unique(_shtype, size, std::make_shared()); } if (_writer) diff --git a/src/sink/sink_sh4lt.h b/src/sink/sink_sh4lt.h index 3f913d3b..8477a2fe 100644 --- a/src/sink/sink_sh4lt.h +++ b/src/sink/sink_sh4lt.h @@ -45,7 +45,6 @@ class Sink_Sh4lt final : public Sink std::string _group{sh4lt::ShType::default_group()}; std::string _label{"splash"}; sh4lt::ShType _shtype{}; - Utils::Sh4ltLogger _logger; std::unique_ptr _writer{nullptr}; ImageBufferSpec _previousSpec{}; uint32_t _previousFramerate{0}; diff --git a/src/sink/sink_sh4lt_encoded.cpp b/src/sink/sink_sh4lt_encoded.cpp index 41703921..96e8eaef 100644 --- a/src/sink/sink_sh4lt_encoded.cpp +++ b/src/sink/sink_sh4lt_encoded.cpp @@ -207,7 +207,7 @@ void Sink_Sh4lt_Encoded::handlePixels(const char* pixels, const ImageBufferSpec& // Reset sh4lt writer _shtype = sh4lt::shtype::shtype_from_gst_caps(generateCaps(spec, _framerate, _options, _codecName, _context), _label, _group); _writer.reset(); - _writer = std::make_unique(_shtype, size, &_logger); + _writer = std::make_unique(_shtype, size, std::make_shared()); _previousSpec = spec; _previousFramerate = _framerate; diff --git a/src/sink/sink_sh4lt_encoded.h b/src/sink/sink_sh4lt_encoded.h index 36093580..bf6cab42 100644 --- a/src/sink/sink_sh4lt_encoded.h +++ b/src/sink/sink_sh4lt_encoded.h @@ -69,7 +69,6 @@ class Sink_Sh4lt_Encoded final : public Sink std::string _group{sh4lt::ShType::default_group()}; std::string _label{"splash encoded"}; sh4lt::ShType _shtype{}; - Utils::Sh4ltLogger _logger; std::unique_ptr _writer{nullptr}; ImageBufferSpec _previousSpec{}; uint32_t _previousFramerate{0}; diff --git a/tests/performance_tests/perf_sh4lt.cpp b/tests/performance_tests/perf_sh4lt.cpp index d058f8c5..f5e6939a 100644 --- a/tests/performance_tests/perf_sh4lt.cpp +++ b/tests/performance_tests/perf_sh4lt.cpp @@ -33,7 +33,7 @@ const sh4lt::ShType shtype{/*media=*/"nomedia", /*label=*/"perf_sh4lt", /*group=*/"test"}; const size_t shmsize = 1 << 26; const size_t loopCount = 1 << 8; -Splash::Utils::Sh4ltLogger shmlogger; +auto shmlogger = std::make_shared(); std::atomic_int bufferCount{0}; std::mutex copyMutex{}; @@ -54,9 +54,9 @@ void onData(void* data, size_t size) int main() { - sh4lt::Writer writer(shtype, shmsize, &shmlogger); + sh4lt::Writer writer(shtype, shmsize, shmlogger); sh4lt::Follower follower( - shtype.path(), [&](void* data, size_t size, const sh4lt::Time::info_t*) { onData(data, size); }, nullptr, nullptr, &shmlogger); + shtype.path(), [&](void* data, size_t size, const sh4lt::Time::info_t*) { onData(data, size); }, nullptr, nullptr, shmlogger); std::cout << "Preparing buffer data...\n"; for (size_t i = 0; i < shmsize; ++i) From 7c16b31edf0042fc1d4fac9cc065db7210bcaafa Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Sun, 3 Mar 2024 10:46:20 -0500 Subject: [PATCH 11/17] Updated libltc to 1.3.2 --- external/libltc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/libltc b/external/libltc index ea3b2501..bf84b010 160000 --- a/external/libltc +++ b/external/libltc @@ -1 +1 @@ -Subproject commit ea3b2501271a530b05ce169d1824590011d93112 +Subproject commit bf84b01097a1789c0296cc5fcfc3bf4608407930 From b860028e5dff5827ab918d326ba8380bd7fbe5c6 Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Sun, 3 Mar 2024 10:41:29 -0500 Subject: [PATCH 12/17] Updated GLFW to version 3.4.0 --- external/glfw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/glfw b/external/glfw index 8e6c8d7e..7b6aead9 160000 --- a/external/glfw +++ b/external/glfw @@ -1 +1 @@ -Subproject commit 8e6c8d7effc54f8aecd30eda17069588298f4ada +Subproject commit 7b6aead9fb88b3623e3b3725ebb42670cbe4c579 From 0ed81fe8f45a05041ef93965c9fd0510f4460aa4 Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Sun, 3 Mar 2024 10:44:29 -0500 Subject: [PATCH 13/17] Updated Snappy to 1.1.10 --- external/snappy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/snappy b/external/snappy index 2b63814b..dc05e026 160000 --- a/external/snappy +++ b/external/snappy @@ -1 +1 @@ -Subproject commit 2b63814b15a2aaae54b7943f0cd935892fae628f +Subproject commit dc05e026488865bc69313a68bcc03ef2e4ea8e83 From 91975d5023a8ec80d4159589d7f27e09f6b1052f Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Sun, 3 Mar 2024 10:36:13 -0500 Subject: [PATCH 14/17] Updated GLM to version 1.0.0 --- external/glm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/glm b/external/glm index 4db8f89a..33b0eb9f 160000 --- a/external/glm +++ b/external/glm @@ -1 +1 @@ -Subproject commit 4db8f89aace8f04c839b606e15b39fb8383ec732 +Subproject commit 33b0eb9fa336ffd8551024b1d2690e418014553b From 6199309bdf6510206ec4aa423c779723ce798ef4 Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Sun, 3 Mar 2024 21:33:58 -0500 Subject: [PATCH 15/17] Updated imgui to 1.90.4 --- external/imgui | 2 +- src/controller/widget/widget_camera.cpp | 16 ++++++++-------- src/controller/widget/widget_filters.cpp | 2 +- src/controller/widget/widget_media.cpp | 2 +- src/controller/widget/widget_warp.cpp | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/external/imgui b/external/imgui index 9aae45eb..277ae93c 160000 --- a/external/imgui +++ b/external/imgui @@ -1 +1 @@ -Subproject commit 9aae45eb4a05a5a1f96be1ef37eb503a12ceb889 +Subproject commit 277ae93c41314ba5f4c7444f37c4319cdf07e8cf diff --git a/src/controller/widget/widget_camera.cpp b/src/controller/widget/widget_camera.cpp index 41c84d3a..4f65af7e 100644 --- a/src/controller/widget/widget_camera.cpp +++ b/src/controller/widget/widget_camera.cpp @@ -77,7 +77,7 @@ void GuiCamera::render() int w = ImGui::GetWindowWidth() - 3 * leftMargin; int h = w * size[1].as() / size[0].as(); - if (ImGui::ImageButton((void*)(intptr_t)camera->getTexture()->getTexId(), ImVec2(w, h), ImVec2(0, 1), ImVec2(1, 0))) + if (ImGui::ImageButton(camera->getName().c_str(), (void*)(intptr_t)camera->getTexture()->getTexId(), ImVec2(w, h), ImVec2(0, 1), ImVec2(1, 0))) { // If shift is pressed, we hide / unhide this camera if (io.KeyCtrl) @@ -489,38 +489,38 @@ void GuiCamera::processKeyEvents() return; ImGuiIO& io = ImGui::GetIO(); - if (ImGui::IsKeyPressed(' ', false)) + if (ImGui::IsKeyPressed((ImGuiKey)' ', false)) { nextCamera(); return; } - else if (ImGui::IsKeyPressed('A', false)) + else if (ImGui::IsKeyPressed((ImGuiKey)'A', false)) { showAllCalibrationPoints(); return; } - else if (ImGui::IsKeyPressed('C', false)) + else if (ImGui::IsKeyPressed((ImGuiKey)'C', false)) { doCalibration(); return; } - else if (ImGui::IsKeyPressed('H', false)) + else if (ImGui::IsKeyPressed((ImGuiKey)'H', false)) { _hideCameras = !_camerasHidden; return; } - else if (ImGui::IsKeyPressed('O', false)) + else if (ImGui::IsKeyPressed((ImGuiKey)'O', false)) { showAllCamerasCalibrationPoints(); return; } - else if (ImGui::IsKeyPressed('V', false)) + else if (ImGui::IsKeyPressed((ImGuiKey)'V', false)) { _camerasColorized = !_camerasColorized; return; } // Reset to the previous camera calibration - else if (ImGui::IsKeyPressed('Z', false)) + else if (ImGui::IsKeyPressed((ImGuiKey)'Z', false)) { if (io.KeyCtrl) revertCalibration(); diff --git a/src/controller/widget/widget_filters.cpp b/src/controller/widget/widget_filters.cpp index 921cc8cf..824a3c61 100644 --- a/src/controller/widget/widget_filters.cpp +++ b/src/controller/widget/widget_filters.cpp @@ -34,7 +34,7 @@ void GuiFilters::render() int w = ImGui::GetWindowWidth() - 3 * leftMargin; int h = w * spec.height / spec.width; - if (ImGui::ImageButton((void*)(intptr_t)filter->getTexId(), ImVec2(w, h), ImVec2(0, 0), ImVec2(1, 1))) + if (ImGui::ImageButton(filter->getName().c_str(), (void*)(intptr_t)filter->getTexId(), ImVec2(w, h), ImVec2(0, 0), ImVec2(1, 1))) _selectedFilterName = filter->getName(); if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", filter->getAlias().c_str()); diff --git a/src/controller/widget/widget_media.cpp b/src/controller/widget/widget_media.cpp index eeeef113..ba4b4af4 100644 --- a/src/controller/widget/widget_media.cpp +++ b/src/controller/widget/widget_media.cpp @@ -52,7 +52,7 @@ void GuiMedia::render() int w = ImGui::GetWindowWidth() - 3 * leftMargin; int h = w * spec.height / spec.width; - if (ImGui::ImageButton((void*)(intptr_t)filter->getTexId(), ImVec2(w, h))) + if (ImGui::ImageButton(filter->getName().c_str(), (void*)(intptr_t)filter->getTexId(), ImVec2(w, h))) _selectedMediaName = media->getName(); if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", media->getAlias().c_str()); diff --git a/src/controller/widget/widget_warp.cpp b/src/controller/widget/widget_warp.cpp index a1008eed..afe58437 100644 --- a/src/controller/widget/widget_warp.cpp +++ b/src/controller/widget/widget_warp.cpp @@ -52,7 +52,7 @@ void GuiWarp::render() int w = ImGui::GetWindowWidth() - 4 * leftMargin; int h = w * warpSpec.height / warpSpec.width; - if (ImGui::ImageButton((void*)(intptr_t)warp->getTexId(), ImVec2(w, h), ImVec2(0, 1), ImVec2(1, 0))) + if (ImGui::ImageButton(warp->getName().c_str(), (void*)(intptr_t)warp->getTexId(), ImVec2(w, h), ImVec2(0, 1), ImVec2(1, 0))) _currentWarp = i; if (ImGui::IsItemHovered()) From 62885a38c726afd240979619e65812dbab1cc3b8 Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Mon, 4 Mar 2024 15:04:25 -0500 Subject: [PATCH 16/17] Updated News and version number --- CMakeLists.txt | 2 +- News.md | 17 +++++++++++++++++ addons/blender/splash/__init__.py | 2 +- .../xyz.splashmapper.Splash.metainfo.xml | 1 + 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 81a37b73..68f5bab5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ endif() project( splash - VERSION 0.10.7 + VERSION 0.10.8 LANGUAGES C CXX ) diff --git a/News.md b/News.md index f2aeae18..3746fa7e 100644 --- a/News.md +++ b/News.md @@ -1,6 +1,23 @@ Splash release notes ==================== +Splash 0.10.8 (2024-03-04) +------------------------- +Improvements: +* Updated imgui to 1.90.4 +* Updated GLM to version 1.0.0 +* Updated Snappy to 1.1.10 +* Updated GLFW to version 3.4.0 +* Updated libltc to 1.3.2 +* Added support for Sh4lt memory sharing library +* Replaced Ubuntu 23.10 for 24.04 in CI + +Documentation: +* Fixed typo in README.md + +Bugs Fixed: +* Fixed YUYV and UYVY decoding from shmdata sources + Splash 0.10.6 (2024-02-05) ------------------------- Improvements: diff --git a/addons/blender/splash/__init__.py b/addons/blender/splash/__init__.py index 99408aa9..e9754183 100644 --- a/addons/blender/splash/__init__.py +++ b/addons/blender/splash/__init__.py @@ -20,7 +20,7 @@ bl_info = { "name": "Splash output", "author": "Splash authors", - "version": (0, 10, 7), + "version": (0, 10, 8), "blender": (2, 80, 0), "location": "3D View > Toolbox, File > Export", "description": "Utility tools to connect Blender to the Splash videomapper", diff --git a/data/share/metainfo/xyz.splashmapper.Splash.metainfo.xml b/data/share/metainfo/xyz.splashmapper.Splash.metainfo.xml index 79c817b8..49305f57 100644 --- a/data/share/metainfo/xyz.splashmapper.Splash.metainfo.xml +++ b/data/share/metainfo/xyz.splashmapper.Splash.metainfo.xml @@ -32,6 +32,7 @@ + From 5d22eeca54c83a4a5250b6fe0662ef1563ce607d Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Mon, 4 Mar 2024 17:14:31 -0500 Subject: [PATCH 17/17] Fixed build issue with static analysis --- src/CMakeLists.txt | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a83db0d9..33be4698 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,26 +57,33 @@ set_source_files_properties( PROPERTIES COMPILE_FLAGS "-Wno-error -Wno-all -Wno-extra -Wno-unused-parameter -Wno-unused-result" ) -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") +set_source_files_properties( + graphics/shader.cpp + graphics/object.cpp + graphics/camera.cpp + PROPERTIES COMPILE_FLAGS "-Wno-type-limits" +) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set_source_files_properties( network/channel_zmq.cpp PROPERTIES COMPILE_FLAGS "-Wno-maybe-uninitialized" ) + + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) + set_source_files_properties( + image/image.cpp + PROPERTIES COMPILE_FLAGS "-Wno-missing-field-initializers -Wno-stringop-overflow" + ) + endif() endif() -if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # This needs to be set because of stb_image.h set_source_files_properties( image/image.cpp PROPERTIES COMPILE_FLAGS "-Wno-missing-field-initializers" ) - - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set_source_files_properties( - image/image.cpp - PROPERTIES COMPILE_FLAGS "-Wno-missing-field-initializers -Wno-stringop-overflow" - ) - endif() endif() # @@ -268,7 +275,7 @@ if (GPHOTO_FOUND AND OPENCV_FOUND) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set_source_files_properties( controller/colorcalibrator.cpp - PROPERTIES COMPILE_FLAGS "-Wno-maybe-uninitialized" + PROPERTIES COMPILE_FLAGS "-Wno-maybe-uninitialized -Wno-type-limits" ) endif()