From 3c0e7d5660c873ba92a4a4befec049e105031889 Mon Sep 17 00:00:00 2001 From: Martin Piatka Date: Thu, 17 Jul 2025 12:57:53 +0200 Subject: [PATCH 001/100] Makefile: Fix GUI build Add missing $(srcdir) --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 85368cd8f..ea83bc891 100644 --- a/Makefile.in +++ b/Makefile.in @@ -487,7 +487,7 @@ dxt_compress/dxt_glsl.h:dxt_compress/compress_vp.glsl \ gui/QT/Makefile: gui/QT/uv-qt.pro @if test -z "$(QMAKE)"; then echo "Reconfigure with '--enable-qt'"; exit 1; fi - CFLAGS="$(CFLAGS_ORIG)" CXXFLAGS="$(CXXFLAGS_ORIG)" $(QMAKE) -makefile gui/QT/uv-qt.pro "DESTDIR+=./" -o $@ $(QMAKE_FLAGS) + CFLAGS="$(CFLAGS_ORIG)" CXXFLAGS="$(CXXFLAGS_ORIG)" $(QMAKE) -makefile $(srcdir)/gui/QT/uv-qt.pro "DESTDIR+=./" -o $@ $(QMAKE_FLAGS) .PHONY: $(GUI_TARGET) From 7b44acee22f319421de9b9c474ffdaf6064138a4 Mon Sep 17 00:00:00 2001 From: Martin Piatka Date: Thu, 17 Jul 2025 13:59:28 +0200 Subject: [PATCH 002/100] tools/ipc_frame: Fix color_spec selection --- tools/ipc_frame_ug.cpp | 11 ++++++++++- tools/ipc_frame_ug.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/ipc_frame_ug.cpp b/tools/ipc_frame_ug.cpp index b79776ad3..3429b6fe3 100644 --- a/tools/ipc_frame_ug.cpp +++ b/tools/ipc_frame_ug.cpp @@ -104,6 +104,15 @@ bool ipc_frame_from_ug_frame_hq(struct Ipc_frame *dst, return true; } +Ipc_frame_color_spec ipc_frame_color_spec_from_ug(codec_t codec){ + switch(codec){ + case RGBA: return IPC_FRAME_COLOR_RGBA; + case RGB: return IPC_FRAME_COLOR_RGB; + case UYVY: return IPC_FRAME_COLOR_UYVY; + default: return IPC_FRAME_COLOR_NONE; + } +} + bool ipc_frame_from_ug_frame(struct Ipc_frame *dst, const struct video_frame *src, codec_t codec, @@ -125,7 +134,7 @@ bool ipc_frame_from_ug_frame(struct Ipc_frame *dst, dst->header.width = src->tiles[0].width; dst->header.height = src->tiles[0].height; - dst->header.color_spec = static_cast(codec); + dst->header.color_spec = ipc_frame_color_spec_from_ug(codec); int dst_frame_to_allocate = 0; diff --git a/tools/ipc_frame_ug.h b/tools/ipc_frame_ug.h index f46582327..d4f6d7a85 100644 --- a/tools/ipc_frame_ug.h +++ b/tools/ipc_frame_ug.h @@ -33,4 +33,6 @@ bool ipc_frame_write_to_fd(const struct Ipc_frame *f, int fd); int ipc_frame_get_scale_factor(int src_w, int src_h, int target_w, int target_h); +Ipc_frame_color_spec ipc_frame_color_spec_from_ug(codec_t codec); + #endif //IPC_FRAME_UG_32ee5c748f3e From 61261a39dfd5556b3dc332fc9c728640dfb72709 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 17 Jul 2025 15:30:45 +0200 Subject: [PATCH 003/100] create-appimage.sh: try curl prior to wget In CI, the attempt to download mkappimage sometimes fail with some cryptic: ``` ERROR 403: Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.. ``` --- data/scripts/Linux-AppImage/create-appimage.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/scripts/Linux-AppImage/create-appimage.sh b/data/scripts/Linux-AppImage/create-appimage.sh index 0343bd9f9..f17e69831 100755 --- a/data/scripts/Linux-AppImage/create-appimage.sh +++ b/data/scripts/Linux-AppImage/create-appimage.sh @@ -117,13 +117,13 @@ fi add_fonts -if command -v wget >/dev/null && wget -V | grep -q https; then +if command -v curl >/dev/null; then dl() { - wget -O - ${GITHUB_TOKEN+--header "Authorization: token $GITHUB_TOKEN"} "$1" + curl --fail -sSL ${GITHUB_TOKEN+-H "Authorization: token $GITHUB_TOKEN"} "$1" } -elif command -v curl >/dev/null; then +elif command -v wget >/dev/null && wget -V | grep -q https; then dl() { - curl --fail -sSL ${GITHUB_TOKEN+-H "Authorization: token $GITHUB_TOKEN"} "$1" + wget -O - ${GITHUB_TOKEN+--header "Authorization: token $GITHUB_TOKEN"} "$1" } else echo "Neither wget nor curl was found - if one needed later, it will " \ From 2e39c4e5124b6ef5d412951e0e7d2559184547a2 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 21 Jul 2025 14:45:36 +0200 Subject: [PATCH 004/100] jpeg_reader: fixed a typo --- src/utils/jpeg_reader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/jpeg_reader.c b/src/utils/jpeg_reader.c index 999a88cc2..92e51ff9d 100644 --- a/src/utils/jpeg_reader.c +++ b/src/utils/jpeg_reader.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2018-2024 CESNET, z. s. p. o. + * Copyright (c) 2018-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -289,7 +289,7 @@ color_space_get_name(enum jpeg_color_spec color_space) case JPEG_COLOR_SPEC_RGB: return "RGB"; case JPEG_COLOR_SPEC_YCBCR_601: - return "YCbCr BT.601 (limtted range)"; + return "YCbCr BT.601 (limitted range)"; case JPEG_COLOR_SPEC_YCBCR_JPEG: return "YCbCr BT.601 256 Levels (YCbCr JPEG)"; case JPEG_COLOR_SPEC_YCBCR_709: From 022942ae131547dabcba226f267c440845024d3b Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 30 Jul 2025 09:18:01 +0200 Subject: [PATCH 005/100] pam: sync with upstream changes support for pitch (not used by UG) --- src/utils/pam.c | 15 +++++++++++++-- src/utils/pam.h | 5 +++-- src/video_frame.c | 6 ++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/utils/pam.c b/src/utils/pam.c index 1845720f6..cfec55bba 100644 --- a/src/utils/pam.c +++ b/src/utils/pam.c @@ -227,7 +227,10 @@ bool pam_read(const char *filename, struct pam_metadata *info, unsigned char **d return true; } -bool pam_write(const char *filename, unsigned int width, unsigned int height, int ch_count, int maxval, const unsigned char *data, bool pnm) { +bool pam_write(const char *filename, unsigned int width, unsigned int pitch, + unsigned int height, int ch_count, int maxval, + const unsigned char *data, bool pnm) +{ errno = 0; #ifdef _WIN32 FILE *file = _wfopen(mbs_to_wstr(filename), L"wb"); @@ -269,7 +272,15 @@ bool pam_write(const char *filename, unsigned int width, unsigned int height, in } size_t len = (size_t) width * height * ch_count * (maxval <= 255 ? 1 : 2); errno = 0; - size_t bytes_written = fwrite((const char *) data, 1, len, file); + size_t bytes_written = 0; + if (width == pitch) { + bytes_written = fwrite((const char *) data, 1, len, file); + } else { + for (unsigned y = 0; y < height; ++y) { + bytes_written += fwrite( + (const char *)data + (y * pitch), 1, width, file); + } + } if (bytes_written != len) { fprintf(stderr, "Unable to write PAM/PNM data - length %zd, written %zd: %s\n", len, bytes_written, strerror(errno)); diff --git a/src/utils/pam.h b/src/utils/pam.h index 351e3c00e..84894b79b 100644 --- a/src/utils/pam.h +++ b/src/utils/pam.h @@ -59,8 +59,9 @@ struct pam_metadata { }; bool pam_read(const char *filename, struct pam_metadata *info, unsigned char **data, void *(*allocator)(size_t)); -bool pam_write(const char *filename, unsigned int width, unsigned int height, - int ch_count, int maxval, const unsigned char *data, bool pnm); +bool pam_write(const char *filename, unsigned int width, unsigned int pitch, + unsigned int height, int ch_count, int maxval, + const unsigned char *data, bool pnm); #ifdef __cplusplus } // extern "C" diff --git a/src/video_frame.c b/src/video_frame.c index f2eb1cdff..ef4a476a7 100644 --- a/src/video_frame.c +++ b/src/video_frame.c @@ -12,7 +12,7 @@ * @brief This file contains video frame manipulation functions. */ /* - * Copyright (c) 2005-2023 CESNET z.s.p.o. + * Copyright (c) 2005-2025 CESNET * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions @@ -481,7 +481,9 @@ bool save_video_frame_as_pnm(struct video_frame *frame, const char *name) return false; } - pam_write(name, tile->width, tile->height, 3, (1<color_spec)) - 1, data, true); + pam_write(name, tile->width, tile->width, tile->height, 3, + (1 << get_bits_per_component(frame->color_spec)) - 1, data, + true); free(tmp_data); return true; From f2102361c455191580f72bd9914648bd6ce3dbe6 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 30 Jul 2025 15:30:47 +0200 Subject: [PATCH 006/100] pam: fix writing 3 channels --- src/utils/pam.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/utils/pam.c b/src/utils/pam.c index cfec55bba..ce1c2d65d 100644 --- a/src/utils/pam.c +++ b/src/utils/pam.c @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -270,15 +271,17 @@ bool pam_write(const char *filename, unsigned int width, unsigned int pitch, "ENDHDR\n", width, height, ch_count, maxval, tuple_type); } - size_t len = (size_t) width * height * ch_count * (maxval <= 255 ? 1 : 2); + const size_t linesize = (size_t) height * ch_count * (maxval <= 255 ? 1 : 2); + const size_t len = width * linesize; errno = 0; size_t bytes_written = 0; if (width == pitch) { bytes_written = fwrite((const char *) data, 1, len, file); } else { for (unsigned y = 0; y < height; ++y) { - bytes_written += fwrite( - (const char *)data + (y * pitch), 1, width, file); + bytes_written += fwrite((const char *)data + + ((size_t)y * pitch), + 1, linesize, file); } } if (bytes_written != len) { From ef0a54f6f3127f3000d0292df28faf90a698574d Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 31 Jul 2025 08:18:28 +0200 Subject: [PATCH 007/100] pam: add PAM_PITCH_CONTINUOUS placeholder for pitch If the value of pitch is "default", it ti actually a bit tricky to set the value explicitly. Also the value was set incorrectly in HEAD^^ - pitch equals width only for single channel + 8-bit. added doxy --- src/utils/pam.c | 2 +- src/utils/pam.h | 46 ++++++++++++++++++++++++++++++++++++++++------ src/video_frame.c | 2 +- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/utils/pam.c b/src/utils/pam.c index ce1c2d65d..a5d602b7d 100644 --- a/src/utils/pam.c +++ b/src/utils/pam.c @@ -275,7 +275,7 @@ bool pam_write(const char *filename, unsigned int width, unsigned int pitch, const size_t len = width * linesize; errno = 0; size_t bytes_written = 0; - if (width == pitch) { + if (pitch == PAM_PITCH_CONTINUOUS || width == pitch) { bytes_written = fwrite((const char *) data, 1, len, file); } else { for (unsigned y = 0; y < height; ++y) { diff --git a/src/utils/pam.h b/src/utils/pam.h index 84894b79b..0021ca7ea 100644 --- a/src/utils/pam.h +++ b/src/utils/pam.h @@ -49,16 +49,50 @@ extern "C" { #endif +/// metadata read from file struct pam_metadata { - int width; - int height; - int ch_count; - int maxval; - bool bitmap_pbm; // bitmap data is stored in PBM format (1 bit per pixel, line aligned to whole byte, 1 is black /"ink on"/), - // otherwise 1 byte per pixel, 1 is white "light on"); if .depth != 1 || .maxval != 1, this value is undefined + int width; ///< image width + int height; ///< image height + int ch_count; ///< number of channels + int maxval; ///< sample maximal value (typically but not necessarily + ///< 255) + bool bitmap_pbm; ///< bitmap data is stored in PBM format (1 bit per + ///< pixel, line aligned to whole byte, 1 is black + ///< /"ink on"/), otherwise 1 byte per pixel, 1 is + ///< white "light on"); if .depth != 1 || .maxval != 1, + ///< this value is undefined }; +/** + * read PAM/PNM file + * + * @param filename file name + * @param[out] info pointer to metadata struct + * @param[out] data pointer to byte array, can be 0, in which case no data + * are written (only metadata read ) + * @param[out] allocaltor allocator to alloc @ref data; if 0, no data are + * read/allocated, only @ref info set + */ bool pam_read(const char *filename, struct pam_metadata *info, unsigned char **data, void *(*allocator)(size_t)); + +enum { + PAM_PITCH_CONTINUOUS = 0, +}; + +/** + * write PAM or PNM file + * + * @param filename file name to be written to + * @param width image width + * @param pitch input line pitch in bytes; PAM_PITCH_CONTINUOUS can be used + * if input pitch == width * ch_count * (maxval <= 255 ? 1 : 2) + * @param height image height + * @param ch_count image channel count (1-4 for output PAM, 1 or 3 for PNM, see + * @ref pnm) + * @param maxval maximal sample value, typically 255 for 8-bit + * @param data bytes to be written + * @param pnm use PNM file (instead of PAM) + */ bool pam_write(const char *filename, unsigned int width, unsigned int pitch, unsigned int height, int ch_count, int maxval, const unsigned char *data, bool pnm); diff --git a/src/video_frame.c b/src/video_frame.c index ef4a476a7..6e1ea029c 100644 --- a/src/video_frame.c +++ b/src/video_frame.c @@ -481,7 +481,7 @@ bool save_video_frame_as_pnm(struct video_frame *frame, const char *name) return false; } - pam_write(name, tile->width, tile->width, tile->height, 3, + pam_write(name, tile->width, PAM_PITCH_CONTINUOUS, tile->height, 3, (1 << get_bits_per_component(frame->color_spec)) - 1, data, true); free(tmp_data); From 39d76ee4bef80a21eb6a8f401cc4093a98a06b15 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Tue, 5 Aug 2025 09:52:28 +0200 Subject: [PATCH 008/100] configure: refuse sdl3_mixer SDL_mixer 3.0 rwmoved support for native MIDI [1], which was the reason it was used for so refuse that. Note that at this this moment, the latest released SDL_mixer version is 2.8.1, anyways. But it can be installed from Git. [1]: https://wiki.libsdl.org/SDL3_mixer/README-migration --- configure.ac | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 420d9bc30..845a41d3a 100644 --- a/configure.ac +++ b/configure.ac @@ -3393,9 +3393,8 @@ then if test "$sdl_version" -lt 3; then PKG_CHECK_MODULES([SDL_MIXER], [SDL${sdl_ver_suffix}_mixer], [found_sdl_mixer=yes], [found_sdl_mixer=no]) - else - PKG_CHECK_MODULES([SDL_MIXER], [sdl${sdl_ver_suffix}-mixer], - [found_sdl_mixer=yes], [found_sdl_mixer=no]) + elif test "$sdl_mixer_req" = yes; then + AC_MSG_ERROR([SDL_mixer 3 no longer supports MIDI playback so it can be no longer used!]); fi if test "$found_sdl_mixer" = yes; then SDL_MIXER_LIBS=$(remove_mwindows "$SDL_MIXER_LIBS") From f48a685a051ef1f1e0c1e9734f41c18ca08aeca9 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 6 Aug 2025 14:29:20 +0200 Subject: [PATCH 009/100] 3d-interlaced: add help documentation --- src/vo_postprocess/3d-interlaced.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vo_postprocess/3d-interlaced.c b/src/vo_postprocess/3d-interlaced.c index f154f4228..d9e4da3b8 100644 --- a/src/vo_postprocess/3d-interlaced.c +++ b/src/vo_postprocess/3d-interlaced.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2011-2015 CESNET, z. s. p. o. + * Copyright (c) 2011-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,6 +43,7 @@ #include "debug.h" #include "lib_common.h" +#include "utils/color_out.h" #include "video.h" #include "video_display.h" /* DISPLAY_PROPERTY_VIDEO_SEPARATE_FILES */ #include "vo_postprocess.h" @@ -66,6 +67,13 @@ static bool interlaced_3d_get_property(void *state, int property, void *val, siz static void * interlaced_3d_init(const char *config) { if (strcmp(config, "help") == 0) { + color_printf( + TBOLD("interlaced_3d") " postprocessor merges left and " + "right eye into one single where " + "the eyes are line-interleaved.\n\n"); + printf("Usage:\n\t-p interlaced_3d\n\n"); + } + if (strlen(config) > 0) { printf("3d-interlaced takes no parameters.\n"); return NULL; } From 03d4d69dbb818ac7f6596951126a545352ba11f2 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 6 Aug 2025 15:08:40 +0200 Subject: [PATCH 010/100] capture_filter/*: IWYU --- src/capture_filter/blank.c | 18 +++++++----- src/capture_filter/color.c | 15 +++++----- src/capture_filter/display.c | 13 +++++---- src/capture_filter/disrupt.c | 14 ++++----- src/capture_filter/every.c | 15 +++++----- src/capture_filter/flip.c | 14 +++++---- src/capture_filter/gamma.cpp | 20 +++++++------ src/capture_filter/grayscale.c | 14 ++++----- src/capture_filter/mirror.c | 13 ++++----- src/capture_filter/override_prop.c | 13 ++++----- src/capture_filter/preview.cpp | 32 ++++++++++----------- src/capture_filter/ratelimit.c | 16 +++++------ src/capture_filter/split.c | 18 ++++++------ src/vo_postprocess/capture_filter_wrapper.h | 3 +- 14 files changed, 110 insertions(+), 108 deletions(-) diff --git a/src/capture_filter/blank.c b/src/capture_filter/blank.c index 27acc8a3d..d42af3752 100644 --- a/src/capture_filter/blank.c +++ b/src/capture_filter/blank.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2013-2023 CESNET, z. s. p. o. + * Copyright (c) 2013-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,19 +35,23 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ +#include // for assert +#include // for bool, false, true +#include // for uint8_t +#include // for printf, fprintf, stderr +#include // for free, atof, atoi, calloc, malloc +#include // for memset, strtok_r, memcpy, strchr +#include "libavutil/pixfmt.h" // for AVPixelFormat +#include "types.h" // for tile, video_frame, video_desc, BGR +#include "video_frame.h" // for video_desc_from_frame, video_desc_eq #include "capture_filter.h" +#include "compat/strings.h" // for strcasecmp #include "lib_common.h" #include "libavcodec/utils.h" #include "messaging.h" #include "module.h" -#include "video.h" #include "video_codec.h" #include diff --git a/src/capture_filter/color.c b/src/capture_filter/color.c index 814bd96dd..c52fe96d9 100644 --- a/src/capture_filter/color.c +++ b/src/capture_filter/color.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2022 CESNET, z. s. p. o. + * Copyright (c) 2022-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,21 +35,22 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ + +#include // for NULL, calloc, free +#include // for memcpy, strcmp #include "capture_filter.h" #include "debug.h" #include "lib_common.h" #include "utils/color_out.h" #include "pixfmt_conv.h" -#include "video.h" +#include "types.h" // for tile, video_frame #include "video_codec.h" +#include "video_frame.h" // for vf_alloc_desc #include "vo_postprocess/capture_filter_wrapper.h" +struct module; + #define MOD_NAME "[color] " struct state_capture_filter_color { diff --git a/src/capture_filter/display.c b/src/capture_filter/display.c index b1aa72fca..ba9059a57 100644 --- a/src/capture_filter/display.c +++ b/src/capture_filter/display.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2022-2023 CESNET z.s.p.o. + * Copyright (c) 2022-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,16 +39,17 @@ * * the data is 2x copied */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ +#include // for pthread_mutex_unlock, pthread_mutex_lock +#include // for NULL, calloc, free, size_t +#include // for strchr, strcmp, strlen, strdupa #include "capture_filter.h" #include "compat/strings.h" // strdupa #include "debug.h" +#include "host.h" // for exit_uv #include "lib_common.h" +#include "pixfmt_conv.h" // for get_best_decoder_from, get_decoder_from_to +#include "types.h" // for tile, video_desc, video_frame, codec_t #include "utils/color_out.h" #include "utils/list.h" #include "utils/text.h" diff --git a/src/capture_filter/disrupt.c b/src/capture_filter/disrupt.c index 7bba1f344..6f2f99510 100644 --- a/src/capture_filter/disrupt.c +++ b/src/capture_filter/disrupt.c @@ -6,7 +6,7 @@ * occur during regular use like jitter between frames. */ /* - * Copyright (c) 2021 CESNET, z. s. p. o. + * Copyright (c) 2021-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,11 +38,10 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ + +#include // for printf, NULL +#include // for free, atof, calloc +#include // for strchr, strcmp, strlen, strstr #include "capture_filter.h" @@ -51,12 +50,11 @@ #include "lib_common.h" #include "utils/color_out.h" #include "utils/random.h" -#include "video.h" -#include "video_codec.h" #define MOD_NAME "[disrupt c. f.] " struct module; +struct video_frame; static int init(struct module *parent, const char *cfg, void **state); static void done(void *state); diff --git a/src/capture_filter/every.c b/src/capture_filter/every.c index 83cee40e2..ba658f2fb 100644 --- a/src/capture_filter/every.c +++ b/src/capture_filter/every.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2013-2019 CESNET, z. s. p. o. + * Copyright (c) 2013-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,19 +35,18 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ +#include // for printf +#include // for atoi, calloc, free +#include // for strchr, memcpy, strlen +#include // for strcasecmp #include "capture_filter.h" #include "debug.h" #include "lib_common.h" +#include "types.h" // for video_frame, video_frame_callbacks, tile #include "utils/color_out.h" -#include "video.h" -#include "video_codec.h" +#include "video_frame.h" // for VIDEO_FRAME_DISPOSE, vf_alloc_desc, vf_... struct module; diff --git a/src/capture_filter/flip.c b/src/capture_filter/flip.c index b8e62c7b4..f6781e7d6 100644 --- a/src/capture_filter/flip.c +++ b/src/capture_filter/flip.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2015-2023 CESNET, z. s. p. o. + * Copyright (c) 2015-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,11 +35,13 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ +#include // for assert +#include // for sws_freeContext, sws_getContext, sws... +#include // for bool, false, true +#include // for uint8_t +#include // for printf, fprintf, NULL, stderr, size_t +#include // for free, atof, atoi, calloc, malloc +#include // for memset, strtok_r, memcpy, strchr #include "capture_filter.h" #include "debug.h" diff --git a/src/capture_filter/gamma.cpp b/src/capture_filter/gamma.cpp index 8aef303bf..12ce46c3f 100644 --- a/src/capture_filter/gamma.cpp +++ b/src/capture_filter/gamma.cpp @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2020 CESNET, z. s. p. o. + * Copyright (c) 2020-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,14 +35,15 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ - +#include // for errno +#include // for CHAR_BIT +#include // for pow +#include // for uint16_t, uint8_t +#include // for size_t, malloc +#include // for strcmp, strlen +#include // for exception #include -#include +#include // for numeric_limits #include #include @@ -51,8 +52,9 @@ #include "lib_common.h" #include "utils/color_out.h" #include "utils/worker.h" -#include "video.h" +#include "types.h" // for tile, video_frame #include "video_codec.h" +#include "video_frame.h" // for vf_free, VIDEO_FR... #include "vo_postprocess/capture_filter_wrapper.h" constexpr const char *MOD_NAME = "[gamma cap. f.] "; diff --git a/src/capture_filter/grayscale.c b/src/capture_filter/grayscale.c index 8fd22dfb1..b5982de0d 100644 --- a/src/capture_filter/grayscale.c +++ b/src/capture_filter/grayscale.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2015-2023 CESNET, z. s. p. o. + * Copyright (c) 2015-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,19 +35,17 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ +#include // for strcmp, strlen, NULL +#include // for calloc, free, malloc #include "capture_filter.h" #include "debug.h" #include "lib_common.h" +#include "types.h" // for tile, video_frame #include "utils/color_out.h" -#include "video.h" -#include "video_codec.h" +#include "video_frame.h" // for vf_alloc_desc #include "vo_postprocess/capture_filter_wrapper.h" +struct module; static int init(struct module *parent, const char *cfg, void **state); static void done(void *state); diff --git a/src/capture_filter/mirror.c b/src/capture_filter/mirror.c index 192fa92c2..d093e5e3b 100644 --- a/src/capture_filter/mirror.c +++ b/src/capture_filter/mirror.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2015-2023 CESNET, z. s. p. o. + * Copyright (c) 2015-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,19 +35,18 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ +#include // for calloc, free, malloc +#include // for strcmp, strlen #include "capture_filter.h" #include "debug.h" #include "lib_common.h" +#include "types.h" // for tile, video_frame #include "utils/color_out.h" -#include "video.h" #include "video_codec.h" +#include "video_frame.h" // for vf_alloc_desc #include "vo_postprocess/capture_filter_wrapper.h" +struct module; static int init(struct module *parent, const char *cfg, void **state); static void done(void *state); diff --git a/src/capture_filter/override_prop.c b/src/capture_filter/override_prop.c index 8b6613426..d392ae154 100644 --- a/src/capture_filter/override_prop.c +++ b/src/capture_filter/override_prop.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2023 CESNET, z. s. p. o. + * Copyright (c) 2023-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,21 +35,20 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ +#include // for printf +#include // for atoi, calloc, free +#include // for strchr, NULL, strstr #include "capture_filter.h" #include "compat/strings.h" // for strdupa #include "debug.h" #include "lib_common.h" +#include "types.h" // for video_frame, vide... #include "utils/color_out.h" #include "utils/text.h" -#include "video.h" #include "video_codec.h" +#include "video_frame.h" // for parse_fps, vf_all... #include "vo_postprocess/capture_filter_wrapper.h" #define MOD_NAME "[override_prop] " diff --git a/src/capture_filter/preview.cpp b/src/capture_filter/preview.cpp index fad312190..4b8453242 100644 --- a/src/capture_filter/preview.cpp +++ b/src/capture_filter/preview.cpp @@ -3,7 +3,7 @@ * @author Martin Piatka */ /* - * Copyright (c) 2022 CESNET, z. s. p. o. + * Copyright (c) 2022-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,31 +35,31 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -#include -#include -#include +#include // for sleep + +#include // for max +#include // for atomic +#include // for assert #include +#include // for condition_variable +#include // for make_unique, unique_ptr +#include // for mutex, lock_guard, unique_lock +#include // for queue +#include // for char_traits, basic_string +#include // for operator==, basic_string_view +#include // for thread +#include // for move +#include // for vector #include "capture_filter.h" #include "debug.h" #include "lib_common.h" +#include "types.h" // for tile, video_frame, RGB, codec_t #include "utils/color_out.h" #include "utils/fs.h" -#include "utils/misc.h" #include "utils/macros.h" #include "utils/string_view_utils.hpp" -#include "video.h" -#include "video_codec.h" #include "../tools/ipc_frame.h" #include "../tools/ipc_frame_unix.h" #include "../tools/ipc_frame_ug.h" diff --git a/src/capture_filter/ratelimit.c b/src/capture_filter/ratelimit.c index f77c5624f..2eec70640 100644 --- a/src/capture_filter/ratelimit.c +++ b/src/capture_filter/ratelimit.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2023 CESNET, z. s. p. o. + * Copyright (c) 2023-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,20 +35,18 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ +#include // for NULL, printf +#include // for calloc, free, strtod +#include // for strlen, memcpy #include "capture_filter.h" - +#include "compat/strings.h" // for strcasecmp #include "debug.h" #include "lib_common.h" #include "tv.h" +#include "types.h" // for video_frame, video_frame_callbacks, tile #include "utils/color_out.h" -#include "video.h" -#include "video_codec.h" +#include "video_frame.h" // for VIDEO_FRAME_DISPOSE, vf_alloc_desc, vf_... struct module; diff --git a/src/capture_filter/split.c b/src/capture_filter/split.c index b9c77661f..cb615ffd8 100644 --- a/src/capture_filter/split.c +++ b/src/capture_filter/split.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2019 CESNET, z. s. p. o. + * Copyright (c) 2019-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,20 +35,20 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ -#include "capture_filter.h" +#include // for assert +#include // for printf, NULL +#include // for atoi, calloc, free +#include // for strchr +#include "capture_filter.h" +#include "compat/strings.h" // for strcasecmp #include "debug.h" #include "lib_common.h" +#include "types.h" // for video_desc, video_frame, video_frame_ca... #include "utils/color_out.h" #include "utils/vf_split.h" -#include "video.h" -#include "video_codec.h" +#include "video_frame.h" // for vf_alloc_desc_data, video_desc_from_frame #define MAX_TILES 16 diff --git a/src/vo_postprocess/capture_filter_wrapper.h b/src/vo_postprocess/capture_filter_wrapper.h index d5d990bd7..1be217d99 100644 --- a/src/vo_postprocess/capture_filter_wrapper.h +++ b/src/vo_postprocess/capture_filter_wrapper.h @@ -8,7 +8,7 @@ * output buffer to decode to. */ /* - * Copyright (c) 2020 CESNET, z. s. p. o. + * Copyright (c) 2020-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,6 +48,7 @@ #include "capture_filter.h" #include "utils/macros.h" +#include "video_codec.h" // for vc_get_linesize #include "video_display.h" #include "vo_postprocess.h" From 6a91cf000ee5c5af84e39edfde21c91a12a25124 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 6 Aug 2025 16:30:30 +0200 Subject: [PATCH 011/100] add vo_cf/temporal_3d --- configure.ac | 1 + src/capture_filter/temporal_3d.c | 139 +++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 src/capture_filter/temporal_3d.c diff --git a/configure.ac b/configure.ac index 845a41d3a..2314e9391 100644 --- a/configure.ac +++ b/configure.ac @@ -3463,6 +3463,7 @@ if test "$build_default" != no || test "$req_files" = all; then src/capture_filter/preview.o src/capture_filter/ratelimit.o src/capture_filter/split.o + src/capture_filter/temporal_3d.o src/video_capture/aggregate.o src/video_capture/import.o src/video_capture/switcher.o diff --git a/src/capture_filter/temporal_3d.c b/src/capture_filter/temporal_3d.c new file mode 100644 index 000000000..2c8d30ad3 --- /dev/null +++ b/src/capture_filter/temporal_3d.c @@ -0,0 +1,139 @@ +/** + * @file capture_filter/temporal_3d.c + * @author Martin Pulec + */ +/* + * Copyright (c) 2025 CESNET + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include // for assert +#include // for uint32_t +#include // for printf, NULL +#include // for free, calloc, malloc +#include // for memcpy, strlen + +#include "capture_filter.h" // for CAPTURE_FILTER_ABI_VERSION, capture_fil... +#include "compat/strings.h" // for strcasecmp +#include "lib_common.h" // for REGISTER_MODULE, library_class +#include "types.h" // for tile, video_frame, video_frame_callbacks +#include "utils/color_out.h" // for color_printf, TBOLD +#include "utils/macros.h" // for to_fourcc +#include "video_frame.h" // for VIDEO_FRAME_DISPOSE, vf_alloc_desc, vf_... + +struct module; + +#define MAGIC to_fourcc('c', 'f', 't', '3') + +struct state_temporal_3d { + uint32_t magic; + struct video_frame *f; +}; + +static void usage() { + color_printf("Combines temporarily-interlaced 3D.\n\n"); + color_printf(TBOLD("temporal_3d") " usage:\n"); + printf("\ttemporal_3d\n\n"); + printf("(takes no arguments)\n"); +} + +static int init(struct module *parent, const char *cfg, void **state) +{ + (void) parent; + + if(strcasecmp(cfg, "help") == 0) { + usage(); + return 1; + } + + if(strlen(cfg) > 0) { + usage(); + return -1; + } + + struct state_temporal_3d *s = calloc(1 ,sizeof *s); + s->magic = MAGIC; + *state = s; + return 0; +} + +static void dispose_frame(struct video_frame *f) { + VIDEO_FRAME_DISPOSE((struct video_frame *) f->callbacks.dispose_udata); + free(f->tiles[0].data); // f->data_delter is not set so vf_free doesn't + // delete... + vf_free(f); +} + +static void done(void *state) +{ + struct state_temporal_3d *s = state; + assert(s->magic == MAGIC); + if (s->f) { + dispose_frame(s->f); + } + free(s); +} + +static struct video_frame *filter(void *state, struct video_frame *in) +{ + struct state_temporal_3d *s = state; + assert(s->magic == MAGIC); + + assert(in->tile_count == 1); + + if (s->f == NULL) { + struct video_desc desc = video_desc_from_frame(in); + desc.tile_count = 2; + desc.fps /= 2; + s->f = vf_alloc_desc(desc); + s->f->tiles[0].data = malloc(s->f->tiles[0].data_len); + memcpy(s->f->tiles[0].data, in->tiles[0].data, in->tiles[0].data_len); + VIDEO_FRAME_DISPOSE(in); + return NULL; + } + + struct video_frame *f = s->f; + s->f->tiles[1].data = in->tiles[0].data; + f->callbacks.dispose_udata = in; + f->callbacks.dispose = dispose_frame; + s->f = NULL; + + return f; +} + +static const struct capture_filter_info capture_filter_temporal_3d = { + .init = init, + .done = done, + .filter = filter, +}; + +REGISTER_MODULE(temporal_3d, &capture_filter_temporal_3d, LIBRARY_CLASS_CAPTURE_FILTER, CAPTURE_FILTER_ABI_VERSION); + From cd77aa367d297e35c04dc676b5a4af1a059d463a Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 7 Aug 2025 08:44:17 +0200 Subject: [PATCH 012/100] utils/opencl: set CL_TAERGET_OPENCL_VERSION=120 CL headers issue warning when not set and default to eg. 300 (3.0). Not sure if this influences just this code but it seems that this may just limit API availability provided by hdrs? So we can get along with !.2 (for CL_DEVICE_TYPE_CUSTOM). We currently only do device listing to pass further (Cmpto J2K codec) so AFAIK we don't have initialization of the device so this shouldn't matter (at most we'll allow device with unsupported OpenCL version but perhaps cmpto codec could catch this) --- src/utils/opencl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/opencl.c b/src/utils/opencl.c index 32d43468f..7d97859bf 100644 --- a/src/utils/opencl.c +++ b/src/utils/opencl.c @@ -50,6 +50,9 @@ ADD_TO_PARAM(PARAM_NAME, PARAM_HELP); #ifdef HAVE_OPENCL +#ifndef CL_TARGET_OPENCL_VERSION +#define CL_TARGET_OPENCL_VERSION 120 // for CL_DEVICE_TYPE_CUSTOM +#endif #include #include #include From 6e1277a08f1b431c776f489a5f6138fed289f1bd Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 7 Aug 2025 10:16:06 +0200 Subject: [PATCH 013/100] types.h: set RGBA as "primary", not alias At least gdb prints VIDEO_CODEC_FIRST for value `(codec_t) 1`, which is unfortunate. So assuming that the first identifier is taken as a "primary" if more have the same value. --- src/types.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types.h b/src/types.h index d879b38fa..42b3df539 100644 --- a/src/types.h +++ b/src/types.h @@ -62,9 +62,9 @@ extern "C" { typedef enum { VIDEO_CODEC_NONE = 0, ///< dummy color spec VC_NONE = VIDEO_CODEC_NONE, // shortcut - VIDEO_CODEC_FIRST, - VC_FIRST = VIDEO_CODEC_FIRST, - RGBA = VIDEO_CODEC_FIRST, ///< RGBA 8-bit, big-endian + RGBA, ///< RGBA 8-bit, big-endian + VIDEO_CODEC_FIRST = RGBA, + VC_FIRST = VIDEO_CODEC_FIRST, UYVY, ///< YCbCr 422 8-bit - Cb Y0 Cr Y1 YUYV, ///< YCbCr 422 8-bit - Y0 Cb Y1 Cr VUYA, ///< YCbCr 444 8-bit - Cr Cb Y0 Al From b80eb8213dfd2750bcca93e319a588e47e578c1d Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 7 Aug 2025 11:04:21 +0200 Subject: [PATCH 014/100] vdisp/sdl3 probe: do output codec_t multiple times It will actually cause problems to callers those count the occurences like vdisp/aggregate. --- src/video_display/sdl3.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/video_display/sdl3.c b/src/video_display/sdl3.c index 0e06ad2ce..aae7476b9 100644 --- a/src/video_display/sdl3.c +++ b/src/video_display/sdl3.c @@ -605,9 +605,14 @@ get_ug_to_sdl_format(const struct fmt_data *supp_fmts, codec_t ug_codec) static int get_supported_pfs(const struct fmt_data *supp_fmts, codec_t *codecs) { + bool codec_set[VC_COUNT]= {}; int i = 0; for (; supp_fmts[i].ug_codec != VC_NONE; ++i) { + if (codec_set[supp_fmts[i].ug_codec]) { + continue; + } codecs[i] = supp_fmts[i].ug_codec; + codec_set[supp_fmts[i].ug_codec] = true; } return i; } From 7b65da8cf5cb99bc73ad8a4418c6fe8213f41069 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 7 Aug 2025 11:19:38 +0200 Subject: [PATCH 015/100] vdisp/aggregate codec probe: break if found no need to iterate further if match found If display misbehaves (outputs one codec multiple times), it causes that codec to be elimited later (if the count != 1 for 2 devices) or more in case of eg. 3 devices and the codec being listed 2-times for one and no for another, it will be falsely accepted. --- src/video_display/aggregate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/video_display/aggregate.c b/src/video_display/aggregate.c index 89df19601..2c8c7f14f 100644 --- a/src/video_display/aggregate.c +++ b/src/video_display/aggregate.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2011-2023 CESNET, z. s. p. o. + * Copyright (c) 2011-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -289,6 +289,7 @@ static bool display_aggregate_get_property(void *state, int property, void *val, for(sub_codec = 0; sub_codec < lens[i] / sizeof(codec_t); ++sub_codec) { if(examined == codecs[i][sub_codec]) { ++found; + break; } } } From 195d6b2d8f28c7411dcc687d4772e82bfd9fe497 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Fri, 8 Aug 2025 08:34:16 +0200 Subject: [PATCH 016/100] displays: DISPLAY_PROPERTY_VIDEO_MODE check/set len --- src/video_display.c | 5 ++++- src/video_display/aggregate.c | 1 + src/video_display/bluefish444.cpp | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/video_display.c b/src/video_display.c index 8c6fd4d13..dabddbbb3 100644 --- a/src/video_display.c +++ b/src/video_display.c @@ -14,7 +14,7 @@ */ /* * Copyright (c) 2001-2003 University of Southern California - * Copyright (c) 2005-2023 CESNET z.s.p.o. + * Copyright (c) 2005-2025 CESNET * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions @@ -583,6 +583,9 @@ bool display_ctl_property(struct display *d, int property, void *val, size_t *le return false; } break; + case DISPLAY_PROPERTY_VIDEO_MODE: + assert(*len >= sizeof(int)); + return d->funcs->ctl_property(d->state, property, val, len); default: return d->funcs->ctl_property(d->state, property, val, len); } diff --git a/src/video_display/aggregate.c b/src/video_display/aggregate.c index 2c8c7f14f..cb4dbd838 100644 --- a/src/video_display/aggregate.c +++ b/src/video_display/aggregate.c @@ -344,6 +344,7 @@ static bool display_aggregate_get_property(void *state, int property, void *val, } case DISPLAY_PROPERTY_VIDEO_MODE: + *len = sizeof(int); if(s->devices_cnt == 1) *(int *) val = DISPLAY_PROPERTY_VIDEO_MERGED; else diff --git a/src/video_display/bluefish444.cpp b/src/video_display/bluefish444.cpp index 45cb93cc5..b9ad94d68 100644 --- a/src/video_display/bluefish444.cpp +++ b/src/video_display/bluefish444.cpp @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2013-2023 CESNET, z. s. p. o. + * Copyright (c) 2013-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -969,6 +969,7 @@ static bool display_bluefish444_get_property(void *state, int property, void *va *len = sizeof(supported_il_modes); break; case DISPLAY_PROPERTY_VIDEO_MODE: + *len = sizeof(int); *(int *) val = DISPLAY_PROPERTY_VIDEO_SEPARATE_TILES; break; case DISPLAY_PROPERTY_AUDIO_FORMAT: From 129f51164b8af39358128206224ac61e23093137 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 11 Aug 2025 07:56:12 +0200 Subject: [PATCH 017/100] lavc: add magic tiled video compress actually seems to pass invalid ponter as for: uv -F split:2:1 -t testcard:fps=30p -c lavc (not the problem of lavc itself, however) note that module_data is now not first so returning `s` from _init --- src/video_compress/libavcodec.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/video_compress/libavcodec.cpp b/src/video_compress/libavcodec.cpp index c204d669b..a068f0fa3 100644 --- a/src/video_compress/libavcodec.cpp +++ b/src/video_compress/libavcodec.cpp @@ -91,6 +91,7 @@ extern "C"{ #endif #define MOD_NAME "[lavc] " +#define MAGIC to_fourcc('l', 'a', 'v', 'c') using std::array; using std::clamp; @@ -277,6 +278,7 @@ struct state_video_compress_libav { to_lavc_vid_conv_destroy(&pixfmt_conversion); } + uint32_t magic = MAGIC; struct module module_data; struct video_desc saved_desc{}; @@ -713,7 +715,7 @@ void* libavcodec_compress_init(struct module *parent, const char *opts) return ret > 0 ? INIT_NOERR : NULL; } - return &s->module_data; + return s; } #ifdef HWACC_VULKAN @@ -1513,6 +1515,7 @@ receive_packet(state_video_compress_libav *s) static shared_ptr libavcodec_compress_tile(void *state, shared_ptr tx) { auto *s = (state_video_compress_libav *) state; + assert(s->magic == MAGIC); list> cleanup_callbacks; // at function exit handlers libavcodec_check_messages(s); @@ -1619,6 +1622,7 @@ static void cleanup(struct state_video_compress_libav *s) static void libavcodec_compress_done(void *state) { auto *s = (struct state_video_compress_libav *) state; + assert(s->magic == MAGIC); cleanup(s); From b77f1de0719ff36c1d51989757b4781dc8466ded Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 11 Aug 2025 08:54:27 +0200 Subject: [PATCH 018/100] vcompress: fix multi-tile lavc fixes the commit 69024468 (from 2022-08-18) that has broken: `uv -F split:2:1 -t testcard:fps=30p -c lavc` The problem is that the `tile_cnt` was set _prior_ to the vcompress state reconfigure, which will yield 2 states. But prior that, the count was 1. The task was started for tile_cnt (== 1) but waited upon for separate_tiles.size() (== 2)! --- src/video_compress.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/video_compress.cpp b/src/video_compress.cpp index 0471b7965..fb1a687c0 100644 --- a/src/video_compress.cpp +++ b/src/video_compress.cpp @@ -434,7 +434,6 @@ static shared_ptr compress_frame_tiles(struct compress_state *proxy shared_ptr frame) { struct compress_state_real *s = proxy->ptr; - const int tile_cnt = (int) proxy->ptr->state.size(); vector> separate_tiles; if (frame) { if (!check_state_count(frame->tile_count, proxy)) { @@ -442,12 +441,13 @@ static shared_ptr compress_frame_tiles(struct compress_state *proxy } separate_tiles = vf_separate_tiles(frame); } else { - separate_tiles.resize(tile_cnt); + separate_tiles.resize(proxy->ptr->state.size()); } // frame pointer may no longer be valid frame = NULL; + const int tile_cnt = (int) proxy->ptr->state.size(); vector task_handle(tile_cnt); vector data_tile(tile_cnt); @@ -463,7 +463,7 @@ static shared_ptr compress_frame_tiles(struct compress_state *proxy vector> compressed_tiles(separate_tiles.size()); bool failed = false; - for(unsigned int i = 0; i < separate_tiles.size(); ++i) { + for (int i = 0; i < tile_cnt; ++i) { struct compress_worker_data *data = (struct compress_worker_data *) wait_task(task_handle[i]); From c725630e1c84f962b7de51daae673f95d07fe8fa Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 11 Aug 2025 07:54:23 +0200 Subject: [PATCH 019/100] acap/{none,sdi}: IWYU --- src/audio/capture/none.c | 22 +++++++++------------- src/audio/capture/sdi.cpp | 39 ++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/audio/capture/none.c b/src/audio/capture/none.c index 23df3d668..29941da2b 100644 --- a/src/audio/capture/none.c +++ b/src/audio/capture/none.c @@ -8,7 +8,7 @@ * Dalibor Matura <255899@mail.muni.cz> * Ian Wesley-Smith * - * Copyright (c) 2005-2023 CESNET z.s.p.o. + * Copyright (c) 2005-2025 CESNET * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions @@ -46,18 +46,14 @@ * */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif - -#include "audio/audio_capture.h" -#include "debug.h" -#include "lib_common.h" -#include -#include -#include +#include // for assert +#include // for uint32_t +#include // for free, NULL, malloc +#include "audio/audio_capture.h" // for AUDIO_CAPTURE_ABI_VERSION, audio_ca... +#include "debug.h" // for UNUSED +#include "lib_common.h" // for REGISTER_MODULE, library_class +struct device_info; +struct module; #define AUDIO_CAPTURE_NONE_MAGIC 0x43fb99ccu diff --git a/src/audio/capture/sdi.cpp b/src/audio/capture/sdi.cpp index ecbc2837a..3f58a6fe9 100644 --- a/src/audio/capture/sdi.cpp +++ b/src/audio/capture/sdi.cpp @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2011-2023 CESNET, z. s. p. o. + * Copyright (c) 2011-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,27 +35,24 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif // HAVE_CONFIG_H - -#include "audio/audio_capture.h" #include "audio/capture/sdi.h" -#include "audio/types.h" -#include "debug.h" -#include "host.h" -#include "lib_common.h" -#include "types.h" - -#include -#include -#include -#include -#include -#include -#include + +#include // for milliseconds +#include // for condition_variable +#include // for printf, snprintf, NULL +#include // for free, calloc, malloc +#include // for memcpy, strcmp, strncpy +#include // for mutex, unique_lock +#include // for basic_ostream, operator<<, basic_ios +#include // for basic_string, char_traits, hash +#include // for unordered_map, operator!= + +#include "audio/audio_capture.h" // for AUDIO_CAPTURE_ABI_VERSION, audio_ca... +#include "audio/types.h" // for audio_frame +#include "debug.h" // for LOG, LOG_LEVEL_WARNING, UNUSED +#include "host.h" // for commandline_params, INIT_NOERR +#include "lib_common.h" // for REGISTER_MODULE, library_class +#include "types.h" // for device_info, frame_flags_common #define DEFAULT_BUF_SIZE_MS 100L From da7893026b043d6d30c105d00b94fdaa2acf0631 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 11 Aug 2025 09:13:29 +0200 Subject: [PATCH 020/100] vdecoders: reconf display to correct mode If decoder (== received) video mode doesn't match display, namely if display doesn't support separate tiles, set VIDEO_NORMAL. --- src/rtp/video_decoders.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rtp/video_decoders.cpp b/src/rtp/video_decoders.cpp index a9238ed9e..6cf4311c2 100644 --- a/src/rtp/video_decoders.cpp +++ b/src/rtp/video_decoders.cpp @@ -1238,6 +1238,7 @@ static bool reconfigure_decoder(struct state_video_decoder *decoder, struct video_desc display_desc = desc; int display_mode = DISPLAY_PROPERTY_VIDEO_MERGED; // default + enum video_mode display_video_mode = decoder->video_mode; size_t len = sizeof(int); if (!display_ctl_property(decoder->display, DISPLAY_PROPERTY_VIDEO_MODE, @@ -1254,6 +1255,7 @@ static bool reconfigure_decoder(struct state_video_decoder *decoder, display_desc.width *= get_video_mode_tiles_x(decoder->video_mode); display_desc.height *= get_video_mode_tiles_y(decoder->video_mode); display_desc.tile_count = 1; + display_video_mode = VIDEO_NORMAL; } decoder->change_il = select_il_func(desc.interlacing, decoder->disp_supported_il, @@ -1263,7 +1265,8 @@ static bool reconfigure_decoder(struct state_video_decoder *decoder, display_desc.color_spec = out_codec; if (out_codec != VIDEO_CODEC_END && !video_desc_eq(decoder->display_desc, display_desc)) { /* reconfigure VO and give it opportunity to pass us pitch */ - bool ret = display_reconfigure(decoder->display, display_desc, decoder->video_mode); + bool ret = display_reconfigure(decoder->display, display_desc, + display_video_mode); if(!ret) { return false; } From 8c66766be41f007f93ede5ecb8e6994cefe1886f Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 11 Aug 2025 09:50:42 +0200 Subject: [PATCH 021/100] vo_pp video_mode changes: get the mode from PP - try to get the mode from PP with the new VO_PP_VIDEO_MODE - if false returned, then PP should support all and ask display - deprecate/don't use VO_PP_DOES_CHANGE_TILING_MODE, see below See the commits 5197a11c (2016-07-22) and 97454acc (2012-08-20) where added. The tiling mode is set to true for pp/split only, for that it won't do anything. Otherwise, for all other PPs the desc will be converted to single tile, which doesn't seem to be legit (the actual input desc is given by the `desc` argument to display_reconfigure). --- src/video_display.c | 30 ++++++++++++++++++++++++------ src/vo_postprocess.h | 8 ++++++-- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/video_display.c b/src/video_display.c index dabddbbb3..7b3d37c9a 100644 --- a/src/video_display.c +++ b/src/video_display.c @@ -454,11 +454,12 @@ bool display_reconfigure(struct display *d, struct video_desc desc, enum video_m } struct video_desc pp_desc = desc; - if (!pp_does_change_tiling_mode) { - pp_desc.width *= get_video_mode_tiles_x(video_mode); - pp_desc.height *= get_video_mode_tiles_y(video_mode); - pp_desc.tile_count = 1; - } + /// @todo shouldn't be VO_PP_DOES_CHANGE_TILING_MODE actually removed? + // if (!pp_does_change_tiling_mode) { + // pp_desc.width *= get_video_mode_tiles_x(video_mode); + // pp_desc.height *= get_video_mode_tiles_y(video_mode); + // pp_desc.tile_count = 1; + // } if (!vo_postprocess_reconfigure(d->postprocess, pp_desc)) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to reconfigure video " "postprocess.\n"); @@ -526,6 +527,22 @@ restrict_returned_codecs(struct vo_postprocess_state *postprocess, new_disp_codec_count * sizeof(codec_t)); } +static int +get_video_mode(struct display *d) +{ + int video_mode = 0; + size_t len = sizeof(video_mode); + if (d->postprocess != NULL) { + if (vo_postprocess_get_property( + d->postprocess, VO_PP_VIDEO_MODE, &video_mode, &len)) { + return video_mode; + } + } + const bool success = d->funcs->ctl_property( + d->state, DISPLAY_PROPERTY_VIDEO_MODE, &video_mode, &len); + return success ? video_mode : DISPLAY_PROPERTY_VIDEO_MERGED; +} + /** * @brief Gets property from video display. * @param[in] d video display state @@ -585,7 +602,8 @@ bool display_ctl_property(struct display *d, int property, void *val, size_t *le break; case DISPLAY_PROPERTY_VIDEO_MODE: assert(*len >= sizeof(int)); - return d->funcs->ctl_property(d->state, property, val, len); + *(int *) val = get_video_mode(d); + return true; default: return d->funcs->ctl_property(d->state, property, val, len); } diff --git a/src/vo_postprocess.h b/src/vo_postprocess.h index b3dfbab3d..49d484122 100644 --- a/src/vo_postprocess.h +++ b/src/vo_postprocess.h @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2011-2023 CESNET, z. s. p. o. + * Copyright (c) 2011-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,9 +46,13 @@ extern "C" { #include #endif -/* property type default */ +/* property type default (if false returned) */ #define VO_PP_PROPERTY_CODECS 0 /* codec_t[] all uncompressed */ +/// @todo should be possibly removed - for PP input, VO_PP_VIDEO_MODE is used, +/// the output (display input) desc is obeyed with vo_postprocess_get_out_desc() #define VO_PP_DOES_CHANGE_TILING_MODE 1 /* bool false */ +/// accepeted input video mode, should process all if false returned +#define VO_PP_VIDEO_MODE 2 /* int (enum video_mode) any (should process both) */ #define VO_PP_ABI_VERSION 7 From d31e075e92ee78fece95c5c4277ed358bc7cd21f Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 11 Aug 2025 10:35:23 +0200 Subject: [PATCH 022/100] vo_postprocess.h: deprecate also in_tile_mode + fix the info - brief (actually really just ontput format) + missing doxy block asterisk --- src/vo_postprocess.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vo_postprocess.h b/src/vo_postprocess.h index 49d484122..c26d5e816 100644 --- a/src/vo_postprocess.h +++ b/src/vo_postprocess.h @@ -72,8 +72,11 @@ typedef void *(*vo_postprocess_init_t)(const char *cfg); */ typedef bool (*vo_postprocess_reconfigure_t)(void *state, struct video_desc desc); typedef struct video_frame * (*vo_postprocess_getf_t)(void *state); -/* - * Returns various information about postprocessor format not only output (legacy name). +/** + * Returns information about postprocessor output format + * + * @todo @c in_tile_mode should be perhaps removed - unused and supersedded by + * @ref VO_PP_VIDEO_MODE * * @param s postprocessor state * @param out output video description according to input parameters From 4979b8c22ab566a9ade964066910795c55dd3f2c Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 11 Aug 2025 10:03:34 +0200 Subject: [PATCH 023/100] add vo_pp/temporal_3d refers to GH-440 --- configure.ac | 1 + src/vo_postprocess/temporal_3d.c | 193 +++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 src/vo_postprocess/temporal_3d.c diff --git a/configure.ac b/configure.ac index 2314e9391..bf74f8b0e 100644 --- a/configure.ac +++ b/configure.ac @@ -3486,6 +3486,7 @@ if test "$build_default" != no || test "$req_files" = all; then src/vo_postprocess/interlace.o src/vo_postprocess/split.o src/vo_postprocess/temporal-deint.o + src/vo_postprocess/temporal_3d.o " fi diff --git a/src/vo_postprocess/temporal_3d.c b/src/vo_postprocess/temporal_3d.c new file mode 100644 index 000000000..fcac93334 --- /dev/null +++ b/src/vo_postprocess/temporal_3d.c @@ -0,0 +1,193 @@ +/** + * @file vo_postprocess/temporal_3d.c + * @author Martin Pulec + */ +/* + * Copyright (c) 2025 CESNET + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include // for assert +#include // for bool, true, false +#include // for uint32_t +#include // for printf, NULL, size_t +#include // for free, malloc, calloc +#include // for strcmp, strlen + +#include "compat/usleep.h" // for usleep +#include "lib_common.h" // for REGISTER_MODULE, library_class +#include "tv.h" // for time_ns_t +#include "types.h" // for tile, video_desc, video_frame +#include "utils/color_out.h" // for color_printf, TBOLD +#include "utils/macros.h" // for to_fourcc +#include "video_codec.h" // for vc_get_linesize +#include "video_display.h" // for display_prop_vid_mode +#include "video_frame.h" // for vf_get_tile, vf_free +#include "vo_postprocess.h" // for VO_PP_ABI_VERSION, vo_postprocess_info + +// #include "vo_postprocess.h" + +#define MAGIC to_fourcc('v', 'p', 't', '3') +#define MOD_NAME "[temporal_3d] " + +struct state_temporal_3d { + uint32_t magic; + struct video_frame *in; + time_ns_t first_tile_time; +}; + +static bool +temporal_3d_get_property(void *state, int property, void *val, size_t *len) +{ + (void) state; + if (property == VO_PP_VIDEO_MODE) { + assert(*len >= sizeof(int)); + *len = sizeof(int); + *(int *) val = DISPLAY_PROPERTY_VIDEO_SEPARATE_3D; + return true; + } + + return false; +} + +static void * +temporal_3d_init(const char *config) +{ + if (strcmp(config, "help") == 0) { + color_printf(TBOLD( + "temporal_3d") " postprocessor interleaves left and " + "right eye temporarily into a single stream " + "with double FPS.\n\n"); + color_printf("Usage:\n\t" TBOLD(TRED("-p temporal_3d")) "\n\n"); + } + if (strlen(config) > 0) { + printf("3d-interlaced takes no parameters.\n"); + return NULL; + } + + struct state_temporal_3d *s = calloc(1, sizeof *s); + s->magic = MAGIC; + return s; +} + +static bool +temporal_3d_postprocess_reconfigure(void *state, struct video_desc desc) +{ + struct state_temporal_3d *s = state; + assert(s->magic == MAGIC); + assert(desc.tile_count == 2); + s->in = vf_alloc_desc_data(desc); + + return true; +} + +static struct video_frame * +temporal_3d_getf(void *state) +{ + struct state_temporal_3d *s = state; + + return s->in; +} + +/** + * Creates from 2 tiles (left and right eye) one in interlaced format. + * + * @param[in] state postprocessor state + * @param[in] in input frame. Must contain exactly 2 tiles + * @param[out] out output frame to be written to. Should have only one + * tile + * @param[in] req_pitch requested pitch in buffer + */ +static bool +temporal_3d_postprocess(void *state, struct video_frame *in, + struct video_frame *out, int req_pitch) +{ + struct state_temporal_3d *s = state; + assert(in == NULL || in == s->in); + assert(out->tile_count == 1); + + if (in != NULL) { + s->first_tile_time = get_time_in_ns(); + } + + struct tile *in_tile = &s->in->tiles[in == NULL ? 1 : 0]; + const int linesize = vc_get_linesize(in_tile->width, s->in->color_spec); + for (size_t y = 0; y < out->tiles[0].height; ++y) { + memcpy(out->tiles[0].data + (y * req_pitch), + in_tile->data + (y * linesize), linesize); + } + + // delay the other tile for correct timing + if (in == NULL) { + time_ns_t t1 = get_time_in_ns(); + long long since_first_tile_us = + NS_TO_US(t1 - s->first_tile_time); + long long sleep_us = + US_IN_SEC / s->in->fps - since_first_tile_us; + if (sleep_us > 0) { + usleep(sleep_us); + } + } + + return true; +} + +static void +temporal_3d_done(void *state) +{ + struct state_temporal_3d *s = state; + vf_free(s->in); + free(state); +} + +static void +temporal_3d_get_out_desc(void *state, struct video_desc *out, + int *in_display_mode, int *out_frames) +{ + struct state_temporal_3d *s = (struct state_temporal_3d *) state; + + *out = video_desc_from_frame(s->in); + out->fps *= 2; + out->tile_count = 1; + + *in_display_mode = DISPLAY_PROPERTY_VIDEO_SEPARATE_TILES; + *out_frames = 2; +} + +static const struct vo_postprocess_info vo_pp_temporal_3d_info = { + temporal_3d_init, temporal_3d_postprocess_reconfigure, + temporal_3d_getf, temporal_3d_get_out_desc, + temporal_3d_get_property, temporal_3d_postprocess, + temporal_3d_done, +}; + +REGISTER_MODULE(temporal_3d, &vo_pp_temporal_3d_info, + LIBRARY_CLASS_VIDEO_POSTPROCESS, VO_PP_ABI_VERSION); From 945d307dc0381104a559604a572ac6353339e0f6 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 11 Aug 2025 11:34:53 +0200 Subject: [PATCH 024/100] vo_pp/temporal_3d: add nodelay option same meaning and behavior as for vo_pp/temporal-deint: 1. both frames will be output in a burst 2. in order not to be dropped immediately, set the display blocking behavior (may or may not work correctly) --- src/vo_postprocess/temporal_3d.c | 49 +++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/src/vo_postprocess/temporal_3d.c b/src/vo_postprocess/temporal_3d.c index fcac93334..caa93fcd7 100644 --- a/src/vo_postprocess/temporal_3d.c +++ b/src/vo_postprocess/temporal_3d.c @@ -43,6 +43,7 @@ #include // for strcmp, strlen #include "compat/usleep.h" // for usleep +#include "debug.h" // for MSG #include "lib_common.h" // for REGISTER_MODULE, library_class #include "tv.h" // for time_ns_t #include "types.h" // for tile, video_desc, video_frame @@ -57,13 +58,17 @@ #define MAGIC to_fourcc('v', 'p', 't', '3') #define MOD_NAME "[temporal_3d] " +#define TIMEOUT "20ms" struct state_temporal_3d { uint32_t magic; struct video_frame *in; time_ns_t first_tile_time; + bool disable_timing; ///< issue the second eye right after the first }; +static void temporal_3d_done(void *state); + static bool temporal_3d_get_property(void *state, int property, void *val, size_t *len) { @@ -78,23 +83,45 @@ temporal_3d_get_property(void *state, int property, void *val, size_t *len) return false; } +static void +usage() +{ + color_printf( + TBOLD("temporal_3d") " postprocessor interleaves left and " + "right eye temporarily into a single stream " + "with double FPS.\n\n"); + color_printf( + "Usage:\n\t" TBOLD(TRED("-p temporal_3d") "[:nodelay]") "\n\n"); + printf("Parameters:\n"); + printf( + "\tnodelay - disable timing, pass right eye right after first\n"); + printf("\t (may help performance)\n"); +} + static void * temporal_3d_init(const char *config) { if (strcmp(config, "help") == 0) { - color_printf(TBOLD( - "temporal_3d") " postprocessor interleaves left and " - "right eye temporarily into a single stream " - "with double FPS.\n\n"); - color_printf("Usage:\n\t" TBOLD(TRED("-p temporal_3d")) "\n\n"); - } - if (strlen(config) > 0) { - printf("3d-interlaced takes no parameters.\n"); + usage(); return NULL; } - struct state_temporal_3d *s = calloc(1, sizeof *s); s->magic = MAGIC; + if (strcmp(config, "nodelay") == 0) { + s->disable_timing = true; + if (get_commandline_param("decoder-drop-policy") == NULL) { + MSG(NOTICE, + "nodelay option used, setting drop policy to %s " + "timeout.\n", + TIMEOUT); + set_commandline_param("decoder-drop-policy", TIMEOUT); + } + } else { + MSG(ERROR, "Unknown option: %s!\n", config); + temporal_3d_done(s); + return NULL; + } + return s; } @@ -146,12 +173,12 @@ temporal_3d_postprocess(void *state, struct video_frame *in, } // delay the other tile for correct timing - if (in == NULL) { + if (!s->disable_timing && in == NULL) { time_ns_t t1 = get_time_in_ns(); long long since_first_tile_us = NS_TO_US(t1 - s->first_tile_time); long long sleep_us = - US_IN_SEC / s->in->fps - since_first_tile_us; + (US_IN_SEC / s->in->fps) - (double) since_first_tile_us; if (sleep_us > 0) { usleep(sleep_us); } From dc9f09f3bd9dc53433d439bc2b5f2ac75399733d Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 11 Aug 2025 15:22:12 +0200 Subject: [PATCH 025/100] configure.ac: accept also cygwin as Windows The variable #MSYSTEM_CHOST is taken as the base (/etc/config.site) and contains now in MSYS x86_64-pc-cygwin resulting in $host_os=cygwin. Not sure where the variable is defined, anyways... --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index bf74f8b0e..93189e0dc 100644 --- a/configure.ac +++ b/configure.ac @@ -126,7 +126,7 @@ if test "$host_vendor" = "apple"; then AC_DEFINE([HAVE_MACOSX], [1], [This is Mac X OS]) APPEXT=.app elif expr "x$host_os" : "x.*mingw32.*" > /dev/null || -expr "x$host_os" : "x.*msys.*" > /dev/null; then + expr "x$host_os" : "x.*msys.*" > /dev/null || test "$host_os" = cygwin; then system=Windows APPEXT=.exe AC_DEFINE([WIN32], [1], [This is an Windows OS]) From 2007a16d01c0f109735f9a0ad9d93f4b19a03c92 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 11 Aug 2025 16:10:09 +0200 Subject: [PATCH 026/100] sdl3: fix b80eb821 The actual count should be returned **without** duplicites. The fixed fix fixed just the content of the array but not the count. This would trigger assertion in video_decoder_order_output_codecs()->get_pixfmt_desc(VIDEO_CODEC_NONE). --- src/video_codec.c | 3 ++- src/video_display/sdl3.c | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/video_codec.c b/src/video_codec.c index ba096d295..d3646d78e 100644 --- a/src/video_codec.c +++ b/src/video_codec.c @@ -1130,7 +1130,8 @@ i444_8_to_uyvy(int width, int height, const unsigned char *in, struct pixfmt_desc get_pixfmt_desc(codec_t pixfmt) { - assert(pixfmt >= VIDEO_CODEC_FIRST && pixfmt < VIDEO_CODEC_END); + assert(pixfmt >= VIDEO_CODEC_FIRST); + assert(pixfmt <= VIDEO_CODEC_END); struct pixfmt_desc ret = { 0 }; ret.depth = codec_info[pixfmt].bits_per_channel; ret.subsampling = codec_info[pixfmt].subsampling; diff --git a/src/video_display/sdl3.c b/src/video_display/sdl3.c index aae7476b9..8ac00a3f2 100644 --- a/src/video_display/sdl3.c +++ b/src/video_display/sdl3.c @@ -605,16 +605,16 @@ get_ug_to_sdl_format(const struct fmt_data *supp_fmts, codec_t ug_codec) static int get_supported_pfs(const struct fmt_data *supp_fmts, codec_t *codecs) { - bool codec_set[VC_COUNT]= {}; - int i = 0; - for (; supp_fmts[i].ug_codec != VC_NONE; ++i) { + bool codec_set[VC_COUNT] = {}; + int count = 0; + for (int i = 0; supp_fmts[i].ug_codec != VC_NONE; ++i) { if (codec_set[supp_fmts[i].ug_codec]) { continue; } - codecs[i] = supp_fmts[i].ug_codec; + codecs[count++] = supp_fmts[i].ug_codec; codec_set[supp_fmts[i].ug_codec] = true; } - return i; + return count; } static void From 8126c1fbaf6671de2b01f5d84a86260f2ca498e7 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 11 Aug 2025 16:58:22 +0200 Subject: [PATCH 027/100] utils/windows: backtrace: print fail if not resolved --- src/utils/windows.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/utils/windows.c b/src/utils/windows.c index 52cc1def7..9b2853bae 100644 --- a/src/utils/windows.c +++ b/src/utils/windows.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2019-2023 CESNET + * Copyright (c) 2019-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -312,10 +312,16 @@ print_stacktrace_win() char backtrace_msg[] = "Backtrace:\n"; _write(STDERR_FILENO, backtrace_msg, strlen(backtrace_msg)); for (unsigned short i = 0; i < frames; i++) { - SymFromAddr(process, (DWORD64) (stack[i]), 0, symbol); + BOOL ret = SymFromAddr(process, (DWORD64) (stack[i]), 0, symbol); char buf[STR_LEN]; - snprintf_ch(buf, "%i (%p): %s - 0x%0llX\n", frames - i - 1, - stack[i], symbol->Name, symbol->Address); + if (ret == TRUE) { + snprintf_ch(buf, "%i (%p): %s - 0x%0llX\n", + frames - i - 1, stack[i], symbol->Name, + symbol->Address); + } else { + snprintf_ch(buf, "%i (%p): (cannot resolve)\n", + frames - i - 1, stack[i]); + } _write(STDERR_FILENO, buf, strlen(buf)); } From f1e2c9b568626b5fec0e1f3758956e47910b39cb Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Tue, 12 Aug 2025 07:58:36 +0200 Subject: [PATCH 028/100] CI Win: pthreads are now provided with a new pkg --- .github/scripts/Windows/prepare_msys.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/Windows/prepare_msys.sh b/.github/scripts/Windows/prepare_msys.sh index 0892b2172..4afea4722 100644 --- a/.github/scripts/Windows/prepare_msys.sh +++ b/.github/scripts/Windows/prepare_msys.sh @@ -46,7 +46,7 @@ PACMAN_INSTALL='pacman -Sy --needed --noconfirm --disable-download-timeout' MINGW_PACKAGE_PREFIX=mingw-w64-clang-x86_64 m=$MINGW_PACKAGE_PREFIX $PACMAN_INSTALL automake autoconf git make pkgconf \ - $m-clang $m-winpthreads-git \ + $m-clang $m-winpthreads \ $m-gcc-compat \ unzip zip $PACMAN_INSTALL $m-asciidoc \ From 3a940a586862e3f6fd4e4683954acca69c99d763 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Tue, 12 Aug 2025 08:16:53 +0200 Subject: [PATCH 029/100] configure.ac: fix unbound GL vars --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index 93189e0dc..34107ea06 100644 --- a/configure.ac +++ b/configure.ac @@ -645,6 +645,8 @@ AC_CHECK_LIB(glew32, glewInit) AC_CHECK_LIB(X11, XCreateWindow) LIBS=$SAVED_LIBS CFLAGS=$SAVED_CFLAGS +HAVE_GL=no +HAVE_GLEW=no GL_COMMON_OBJ="src/gl_context.o" From d3179770d9208e8947ba42eb1f9d6ade410d0e34 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Tue, 12 Aug 2025 09:04:19 +0200 Subject: [PATCH 030/100] add missing includes --- src/audio/audio.cpp | 1 + src/utils/color_out.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 1dc922b07..1bbb92217 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -50,6 +50,7 @@ #include #include #include +#include // for log10 #include #include #include diff --git a/src/utils/color_out.cpp b/src/utils/color_out.cpp index 51b0f4823..17f52dc0a 100644 --- a/src/utils/color_out.cpp +++ b/src/utils/color_out.cpp @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2018-2024 CESNET + * Copyright (c) 2018-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,6 +44,7 @@ #include // for getenv #include // for strcmp, strlen #include // for back_insert_iterator, back_inserter +#include // for unique_ptr #include // for isatty #include "debug.h" From cc2e7103b16c40dae5fdff745966860f6d45b480 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 11 Aug 2025 16:58:52 +0200 Subject: [PATCH 031/100] configure.ac: generate debug symbols for Win Set it only when linking, not for configure. --- .github/scripts/Windows/prepare_msys.sh | 2 +- Makefile.in | 9 ++++++++- configure.ac | 11 +++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/scripts/Windows/prepare_msys.sh b/.github/scripts/Windows/prepare_msys.sh index 4afea4722..a2b39dbcc 100644 --- a/.github/scripts/Windows/prepare_msys.sh +++ b/.github/scripts/Windows/prepare_msys.sh @@ -46,7 +46,7 @@ PACMAN_INSTALL='pacman -Sy --needed --noconfirm --disable-download-timeout' MINGW_PACKAGE_PREFIX=mingw-w64-clang-x86_64 m=$MINGW_PACKAGE_PREFIX $PACMAN_INSTALL automake autoconf git make pkgconf \ - $m-clang $m-winpthreads \ + $m-clang $m-lld $m-winpthreads \ $m-gcc-compat \ unzip zip $PACMAN_INSTALL $m-asciidoc \ diff --git a/Makefile.in b/Makefile.in index ea83bc891..c92f77272 100644 --- a/Makefile.in +++ b/Makefile.in @@ -241,7 +241,8 @@ src/dir-stamp: $(TARGET): src/dir-stamp $(ULTRAGRID_OBJS) $(GENERATED_HEADERS) $(BIN_DEPS) $(MKDIR_P) $$(dirname $@) - $(LINKER) $(LDFLAGS) $(ULTRAGRID_OBJS) $(LIBS) $(ULTRAGRID_LIBS) -o $(TARGET) + $(LINKER) $(LDFLAGS) @UV_LDFLAGS@ $(ULTRAGRID_OBJS) $(LIBS) $(ULTRAGRID_LIBS)\ + -o $(TARGET) @if [ -n '@DLL_LIBS@' ]; then $(INSTALL) -m 644 @DLL_LIBS@ bin; fi $(REFLECTOR_TARGET): src/dir-stamp $(REFLECTOR_OBJS) $(GENERATED_HEADERS) bin/hd-rum-av @@ -554,6 +555,7 @@ clean: $(COND_SILENCE)-rm -rf @TOREMOVE@ @MODULES@ @LIB_GENERATED_HEADERS@ $(COND_SILENCE)-rm -rf $(DEP_FILES) $(COND_SILENCE)-rm -rf bin/shaders + $(COND_SILENCE)-rm -f bin/uv.pdb $(COND_SILENCE)if [ -f "gui/QT/Makefile" ]; then make -C gui/QT/ distclean; fi distclean: clean @@ -690,6 +692,10 @@ install: all if [ -n "@VULKAN@" ]; then\ $(INSTALL) -D -m 644 "$(srcdir)/share/ultragrid/vulkan_shaders/"* -t "$(DESTDIR)$(datadir)/ultragrid/vulkan_shaders"; \ fi + if [ -f bin/uv.pdb ]; then\ + $(INSTALL) -m 644 bin/uv.pdb $(DESTDIR)$(bindir); \ + fi + uninstall: $(RM) $(DESTDIR)$(bindir)/uv $(RM) $(DESTDIR)$(bindir)/hd-rum-transcode @@ -710,5 +716,6 @@ uninstall: rmdir $(DESTDIR)$(datadir)/ultragrid/vulkan_shaders;\ rmdir $(DESTDIR)$(datadir)/ultragrid;\ fi + $(RM) $(DESTDIR)$(bindir)/uv.pdb # vim: set noexpandtab diff --git a/configure.ac b/configure.ac index 34107ea06..d4318216e 100644 --- a/configure.ac +++ b/configure.ac @@ -174,12 +174,23 @@ if test $system = Windows; then LIBS="$LIBS -lsetupapi -lws2_32 -liphlpapi -lole32 -loleaut32" LIBS="$LIBS -ldbghelp" AC_CHECK_FUNCS(SetThreadDescription) + if $CXX -dM -E - /dev/null; then + AC_CHECK_PROG([LLD], [lld], [yes]) + if test "$LLD" = yes; then + COMMON_FLAGS="${COMMON_FLAGS:+$COMMON_FLAGS }-gcodeview" + UV_LDFLAGS="-fuse-ld=lld -g -Wl,--pdb=bin/uv.pdb" + fi + fi + if ! expr "x$COMMON_FLAGS" : '.*gcodeview' >/dev/null; then + UG_MSG_WARN([Windows stacktrace will not be available.]) + fi fi LINKER=$CXX AC_SUBST(LINKER) AC_SUBST(LDFLAGS) +AC_SUBST(UV_LDFLAGS) AC_SUBST(CXXFLAGS) AC_ARG_ENABLE(depends-version-check, From 38da0cd4bfafc3985df3394242dca9d82383885a Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Tue, 12 Aug 2025 11:42:08 +0200 Subject: [PATCH 032/100] configure: avoid excess spaces While the construction ${var+ $var} works if var is not defined, it will produce extra space if var is empty. Added ':' to '+' to avoid this. --- configure.ac | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/configure.ac b/configure.ac index d4318216e..69dc917f6 100644 --- a/configure.ac +++ b/configure.ac @@ -32,7 +32,7 @@ fi # ------------------------------------------------------------------------------------------------- POST_COMPILE_MSG="" -CXXFLAGS="${CXXFLAGS+$CXXFLAGS }-std=gnu++17" +CXXFLAGS="${CXXFLAGS:+$CXXFLAGS }-std=gnu++17" CUDA_FLAGS="$CUDA_FLAGS${CUDA_FLAGS:+${CUDAFLAGS:+ }}$CUDAFLAGS" INC="${INC-}" LDFLAGS="${LDFLAGS-}" @@ -150,7 +150,7 @@ fi if test $system = Linux; then AC_CHECK_HEADERS([linux/version.h]) LDFLAGS="$LDFLAGS -Wl,--dynamic-list-data" - COMMON_FLAGS="${COMMON_FLAGS+$COMMON_FLAGS }-D_GNU_SOURCE" + COMMON_FLAGS="${COMMON_FLAGS:+$COMMON_FLAGS }-D_GNU_SOURCE" fi if test $system = MacOSX; then @@ -225,9 +225,9 @@ test $ac_cv_sizeof_int_p -eq 4; } then LIBS="${LIBS:+$LIBS }-latomic" # needed for atomic_uint64_t fi -CFLAGS="$CFLAGS${ARCH+ $ARCH}" -CXXFLAGS="$CXXFLAGS${ARCH+ $ARCH}" -LDFLAGS="$LDFLAGS${ARCH+ $ARCH}" +CFLAGS="$CFLAGS${ARCH:+ $ARCH}" +CXXFLAGS="$CXXFLAGS${ARCH:+ $ARCH}" +LDFLAGS="$LDFLAGS${ARCH:+ $ARCH}" if expr "x$CFLAGS" : 'x.*-O' >/dev/null; then OFAST="" @@ -514,7 +514,7 @@ then fi DEFAULT_LIB_P=$($LINKER --verbose 2>&1 | awk \ 'BEGIN { ORS="" } /InstalledDir: / { print $2 }')/../lib - CUDA_LIB="${CUDA_LIB+$CUDA_LIB }-L$DEFAULT_LIB_P" + CUDA_LIB="${CUDA_LIB:+$CUDA_LIB }-L$DEFAULT_LIB_P" CUDA_LIB="$CUDA_LIB -L\"$cl_lib_path\"" FOUND_CUDA=yes fi @@ -545,7 +545,7 @@ then fi fi - CUDA_LIB="${CUDA_LIB+$CUDA_LIB }-L$CUDA_LIB_PATH -lcudart" + CUDA_LIB="${CUDA_LIB:+$CUDA_LIB }-L$CUDA_LIB_PATH -lcudart" CUDA_INC="-I$CUDA_PATH/include" CUDA_COMPILER="$NVCC" INC="$INC $CUDA_INC" @@ -3205,8 +3205,8 @@ then if test "${found_cl?}" = yes && test "${found_cl_h?}" = yes; then AC_DEFINE([HAVE_OPENCL], [1], [OpenCL is supported]) cmpto_libs="$cmpto_libs $OpenCL_LIBS" - CFLAGS="$CFLAGS${OpenCL_CFLAGS+ $OPENCL_CFLAGS}" - CFLAGS="$CFLAGS${OpenCL_Headers_CFLAGS+ $OPENCL_Headers_CFLAGS}" + CFLAGS="$CFLAGS${OpenCL_CFLAGS:+ $OPENCL_CFLAGS}" + CFLAGS="$CFLAGS${OpenCL_Headers_CFLAGS:+ $OPENCL_Headers_CFLAGS}" fi add_module vcompress_cmpto_j2k "src/video_compress/cmpto_j2k.o \ @@ -3413,7 +3413,7 @@ then SDL_MIXER_LIBS=$(remove_mwindows "$SDL_MIXER_LIBS") SDL_MIXER_CFLAGS=$($PKG_CONFIG --cflags-only-I SDL${sdl_ver_suffix}_mixer) add_module acap_sdl_mixer src/audio/capture/sdl_mixer.o "$SDL_MIXER_LIBS" - INC="$INC${SDL_MIXER_CFLAGS+ $SDL_MIXER_CFLAGS}" + INC="$INC${SDL_MIXER_CFLAGS:+ $SDL_MIXER_CFLAGS}" sdl_mixer=yes fi fi From ebca9e1211d45b6dbeb33e8f7f3fdac7f0cee971 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Tue, 12 Aug 2025 12:09:08 +0200 Subject: [PATCH 033/100] print_stacktrace_win: print also file/line and displacement, if present Also print common (frame number, stacktrace ptr) in one place. --- src/utils/windows.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/utils/windows.c b/src/utils/windows.c index 9b2853bae..04a489569 100644 --- a/src/utils/windows.c +++ b/src/utils/windows.c @@ -314,13 +314,26 @@ print_stacktrace_win() for (unsigned short i = 0; i < frames; i++) { BOOL ret = SymFromAddr(process, (DWORD64) (stack[i]), 0, symbol); char buf[STR_LEN]; + snprintf_ch(buf, "%i (%p): ", frames - i - 1, stack[i]); if (ret == TRUE) { - snprintf_ch(buf, "%i (%p): %s - 0x%0llX\n", - frames - i - 1, stack[i], symbol->Name, - symbol->Address); + snprintf(buf + strlen(buf), sizeof buf - strlen(buf), + "%s - 0x%0llX\n", symbol->Name, + symbol->Address); + + IMAGEHLP_LINE64 line = { .SizeOfStruct = sizeof line }; + DWORD displacementLine = 0; + if (SymGetLineFromAddr64(process, (DWORD64) (stack[i]), + &displacementLine, &line)) { + snprintf(buf + strlen(buf), + sizeof buf - strlen(buf), + "\tFile: %s, line: %lu, displacement: " + "%lu\n", + line.FileName, line.LineNumber, + displacementLine); + } } else { - snprintf_ch(buf, "%i (%p): (cannot resolve)\n", - frames - i - 1, stack[i]); + snprintf(buf + strlen(buf), sizeof buf - strlen(buf), + "(cannot resolve)\n"); } _write(STDERR_FILENO, buf, strlen(buf)); } From ec90eb0468cc0c60e4b843a88338585414e96390 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 13 Aug 2025 08:59:15 +0200 Subject: [PATCH 034/100] fix hd-rum-transcode crash crashes since the commit ab5d2a5ca (2025-06-17) That commit started to enforce existence of control socket, which is not true in case of the reflector. fixes ab5d2a5ca closes GH-461 --- src/audio/filter/controlport_stats.cpp | 6 ++---- src/control_socket.cpp | 20 ++++++++++++++++++++ src/control_socket.h | 4 ++-- src/rtp/audio_decoders.cpp | 5 +---- src/rtp/video_decoders.cpp | 9 ++++----- src/transmit.cpp | 6 +----- src/video_rxtx/ultragrid_rtp.cpp | 3 +-- 7 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/audio/filter/controlport_stats.cpp b/src/audio/filter/controlport_stats.cpp index afc2d3958..0cd8d149c 100644 --- a/src/audio/filter/controlport_stats.cpp +++ b/src/audio/filter/controlport_stats.cpp @@ -53,11 +53,9 @@ #include "lib_common.h" struct state_controlport_stats{ - state_controlport_stats(struct module *mod) : mod(MODULE_CLASS_DATA, mod, this) + explicit state_controlport_stats(struct module *mod) + : mod(MODULE_CLASS_DATA, mod, this), control(get_control_state(mod)) { - control = (control_state *) (get_module(get_root_module(mod), - "control")) - ->priv_data; } module_raii mod; diff --git a/src/control_socket.cpp b/src/control_socket.cpp index 019c6dd36..f42dd2622 100644 --- a/src/control_socket.cpp +++ b/src/control_socket.cpp @@ -1043,6 +1043,26 @@ int control_audio_ch_report_count(struct control_state *state){ return state ? state->audio_channel_report_count : 0; } +/** + * finds control socket state from module hierarchy + * + * @param mod module in the tree (doesn't need to be root), may be nullptr (nullptr is returned then) + * @returns found state; nullptr if state not found (or mod=0) + */ +struct control_state * +get_control_state(struct module *mod) +{ + if (mod == nullptr) { + return nullptr; + } + struct module *control_mod = + get_module(get_root_module(mod), "control"); + if (control_mod == nullptr) { + return nullptr; + } + return (struct control_state *) control_mod->priv_data; +} + static void print_control_help() { color_printf("Control internal commands:\n" TBOLD("\texit") "\n" diff --git a/src/control_socket.h b/src/control_socket.h index d02f2eab5..e94520943 100644 --- a/src/control_socket.h +++ b/src/control_socket.h @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2013-2021 CESNET, z. s. p. o. + * Copyright (c) 2013-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -51,6 +51,7 @@ struct module; * @retval 0 if success */ int control_init(int port, int connection_type, struct control_state **state, struct module *root_module, int force_ip_version); +struct control_state *get_control_state(struct module *mod); void control_start(struct control_state *state); void control_done(struct control_state *s); void control_report_stats(struct control_state *state, const std::string & stat_line); @@ -58,6 +59,5 @@ void control_report_event(struct control_state *state, const std::string & event bool control_stats_enabled(struct control_state *state); int control_audio_ch_report_count(struct control_state *state); - #endif // control_socket_h_ diff --git a/src/rtp/audio_decoders.cpp b/src/rtp/audio_decoders.cpp index 9a27410f4..167de4bd3 100644 --- a/src/rtp/audio_decoders.cpp +++ b/src/rtp/audio_decoders.cpp @@ -271,10 +271,7 @@ void *audio_decoder_init(char *audio_channel_map, const char *audio_scale, const s->packet_counter = packet_counter_init(0); s->audio_decompress = NULL; - - s->control = (struct control_state *) get_module( - get_root_module(parent), "control") - ->priv_data; + s->control = get_control_state(parent); if (strlen(encryption) > 0) { s->dec_funcs = static_cast(load_library("openssl_decrypt", diff --git a/src/rtp/video_decoders.cpp b/src/rtp/video_decoders.cpp index 6cf4311c2..cfccfce60 100644 --- a/src/rtp/video_decoders.cpp +++ b/src/rtp/video_decoders.cpp @@ -335,21 +335,20 @@ struct main_msg_reconfigure { */ struct state_video_decoder { - state_video_decoder(struct module *parent) { + explicit state_video_decoder(struct module *parent) + : control(get_control_state(parent)) + { module_init_default(&mod); mod.cls = MODULE_CLASS_DECODER; mod.priv_data = this; mod.new_message = decoder_process_message; module_register(&mod, parent); - control = (struct control_state *) get_module( - get_root_module(parent), "control") - ->priv_data; } ~state_video_decoder() { module_done(&mod); } struct module mod; - struct control_state *control = {}; + struct control_state *control; thread decompress_thread_id, fec_thread_id; diff --git a/src/transmit.cpp b/src/transmit.cpp index 323519d05..ac607a835 100644 --- a/src/transmit.cpp +++ b/src/transmit.cpp @@ -263,11 +263,7 @@ struct tx *tx_init(struct module *parent, unsigned mtu, enum tx_media_type media } tx->bitrate = bitrate; - - if (parent) { - tx->control = (struct control_state *) get_module( - get_root_module(parent), "control")->priv_data; - } + tx->control = get_control_state(parent); return tx; } diff --git a/src/video_rxtx/ultragrid_rtp.cpp b/src/video_rxtx/ultragrid_rtp.cpp index b0d1eb663..2d83bd7f2 100644 --- a/src/video_rxtx/ultragrid_rtp.cpp +++ b/src/video_rxtx/ultragrid_rtp.cpp @@ -92,8 +92,7 @@ ultragrid_rtp_video_rxtx::ultragrid_rtp_video_rxtx(const map &p throw ug_no_error(); } - m_control = (struct control_state *) get_module( - get_root_module(m_common.parent), "control")->priv_data; + m_control = get_control_state(m_common.parent); } ultragrid_rtp_video_rxtx::~ultragrid_rtp_video_rxtx() From 4a4716f6e762e9861189596cb972736006b38264 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 13 Aug 2025 09:35:47 +0200 Subject: [PATCH 035/100] CI mac: fix broken live555 test live555 HEAD commit is now f4a4e8fb0 testRTSPClient.cpp:64:24: error: copying variable of type 'EventLoopWatchVariable' (aka 'atomic') invokes deleted constructor 64 | EventLoopWatchVariable eventLoopWatchVariable = 0; --- .github/scripts/install-common-deps.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/scripts/install-common-deps.sh b/.github/scripts/install-common-deps.sh index 8044c6a7a..94fb6bee1 100755 --- a/.github/scripts/install-common-deps.sh +++ b/.github/scripts/install-common-deps.sh @@ -79,6 +79,12 @@ install_juice() { ) } +# fixes broken live555 test +live555_rm_tests() { + sed -e '/TESTPROGS_DIR.*MAKE/d' Makefile > Makefile.fix + mv -f Makefile.fix Makefile +} + download_build_live555() {( git clone --depth 1 https://github.com/xanview/live555/ cd live555 @@ -95,6 +101,7 @@ download_build_live555() {( make -j "$(nproc)" CPLUSPLUS_COMPILER="c++ -DNO_STD_LIB" else ./genMakefiles macosx-no-openssl + live555_rm_tests make -j "$(nproc)" CPLUSPLUS_COMPILER="c++ -std=c++11" fi )} From a5c54a94196442b4c0601525ed475327dc2fd400 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 13 Aug 2025 09:46:42 +0200 Subject: [PATCH 036/100] c_basicRTSPOnlyServer: mv rtsp_serv def to .cpp hide struct rtsp_serv definition by moving it to .cpp --- src/rtsp/c_basicRTSPOnlyServer.cpp | 7 +++++++ src/rtsp/c_basicRTSPOnlyServer.h | 8 +++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/rtsp/c_basicRTSPOnlyServer.cpp b/src/rtsp/c_basicRTSPOnlyServer.cpp index d81b92885..d04643362 100644 --- a/src/rtsp/c_basicRTSPOnlyServer.cpp +++ b/src/rtsp/c_basicRTSPOnlyServer.cpp @@ -4,6 +4,7 @@ * Gerard Castillo * * Copyright (c) 2005-2010 Fundació i2CAT, Internet I Innovació Digital a Catalunya + * Copyright (c) 2014-2025 CESNET * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions @@ -47,6 +48,12 @@ #include "rtsp/c_basicRTSPOnlyServer.h" #include "rtsp/BasicRTSPOnlyServer.hh" +struct rtsp_serv { + struct rtsp_server_parameters params; + pthread_t server_th; + uint8_t watch; +}; + rtsp_serv_t * c_start_server(struct rtsp_server_parameters params) { diff --git a/src/rtsp/c_basicRTSPOnlyServer.h b/src/rtsp/c_basicRTSPOnlyServer.h index 71cba1f28..e777bef4d 100644 --- a/src/rtsp/c_basicRTSPOnlyServer.h +++ b/src/rtsp/c_basicRTSPOnlyServer.h @@ -4,6 +4,7 @@ * Gerard Castillo * * Copyright (c) 2005-2010 Fundació i2CAT, Internet I Innovació Digital a Catalunya + * Copyright (c) 2014-2025 CESNET * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions @@ -74,11 +75,8 @@ struct rtsp_server_parameters { codec_t video_codec; }; -EXTERNC typedef struct rtsp_serv { - struct rtsp_server_parameters params; - pthread_t server_th; - uint8_t watch; -} rtsp_serv_t; +struct rtsp_serv; +typedef struct rtsp_serv rtsp_serv_t; EXTERNC rtsp_serv_t *c_start_server(struct rtsp_server_parameters params); From f2cecbd50a21b305d741b2aa19c093f223f8a9aa Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 13 Aug 2025 09:55:00 +0200 Subject: [PATCH 037/100] live555: fix API change --- src/rtsp/BasicRTSPOnlyServer.cpp | 7 ++++--- src/rtsp/c_basicRTSPOnlyServer.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rtsp/BasicRTSPOnlyServer.cpp b/src/rtsp/BasicRTSPOnlyServer.cpp index 6190bc11c..57e04a7ee 100644 --- a/src/rtsp/BasicRTSPOnlyServer.cpp +++ b/src/rtsp/BasicRTSPOnlyServer.cpp @@ -4,6 +4,7 @@ * Gerard Castillo * * Copyright (c) 2005-2010 Fundació i2CAT, Internet I Innovació Digital a Catalunya + * Copyright (c) 2014-2025 CESNET * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions @@ -143,9 +144,9 @@ BasicRTSPOnlyServer::init_server() } void *BasicRTSPOnlyServer::start_server(void *args){ - char* watch = (char*) args; - BasicRTSPOnlyServer* instance = getInstance(); - + auto *watch = (EventLoopWatchVariable *) args; + BasicRTSPOnlyServer *instance = getInstance(); + if (instance == NULL || instance->env == NULL || instance->rtspServer == NULL){ return NULL; } diff --git a/src/rtsp/c_basicRTSPOnlyServer.cpp b/src/rtsp/c_basicRTSPOnlyServer.cpp index d04643362..dd05251ca 100644 --- a/src/rtsp/c_basicRTSPOnlyServer.cpp +++ b/src/rtsp/c_basicRTSPOnlyServer.cpp @@ -51,7 +51,7 @@ struct rtsp_serv { struct rtsp_server_parameters params; pthread_t server_th; - uint8_t watch; + EventLoopWatchVariable watch; }; rtsp_serv_t * From 2b3ae39a4fff8931d8375a073c0443a4b222b96d Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 13 Aug 2025 11:36:01 +0200 Subject: [PATCH 038/100] support for symbolic trace with libbacktrace Linux (+ eventaully macOS) --- configure.ac | 25 ++++++++++++++ src/host.cpp | 81 ++++++++++++++++++++++++++++++++++++++++++++++ src/utils/string.c | 12 +++++++ src/utils/string.h | 5 ++- 4 files changed, 122 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 69dc917f6..ea4b6b29d 100644 --- a/configure.ac +++ b/configure.ac @@ -454,6 +454,30 @@ AC_ARG_VAR([GENICAM_GENTL64_PATH], [XIMEA SDK library path]) AC_ARG_VAR([NTV2_ROOT], [Directory to AJA NTV2 SDK (ends with ntv2projects).]) AC_ARG_VAR([SAGE_DIRECTORY], [Directory of your SAGE installation.]) +# ------------------------------------------------ +# libbacktrace +# ----------------------------------------------- +libbacktrace=no +AC_ARG_ENABLE(libbacktrace, + AS_HELP_STRING([--disable-libbacktrace], [disable libbacktrace]), + [libbacktrace_req=$enableval], + [libbacktrace_req=$build_default]) + +if test "${libbacktrace_req?}" != no; then + AC_CHECK_HEADER(backtrace.h) + AC_CHECK_LIB(backtrace, backtrace_pcinfo) + if test "${ac_cv_header_backtrace_h?}" = yes && \ + test "${ac_cv_lib_backtrace_backtrace_pcinfo?}" = yes; + then + LIBS="$LIBS -lbacktrace" + AC_DEFINE([HAVE_LIBBACKTRACE], [1], [we have libbacktrace]) + libbacktrace=yes + fi +fi + +ENSURE_FEATURE_PRESENT([$libbacktrace_req], [$libbacktrace], +[libbacktrace not found]) + # ------------------------------------------------------------------------------------------------- # CUDA stuff # @@ -3580,6 +3604,7 @@ RESULT=`add_column "$RESULT" "Debug output" $debug_output $?` RESULT=`add_column "$RESULT" "iHDTV support" $ihdtv $?` RESULT=`add_column "$RESULT" "IPv6 support" $ipv6 $?` RESULT=`add_column "$RESULT" "Library live555" $livemedia $?` +RESULT=`add_column "$RESULT" "Libbacktrace" $libbacktrace $?` RESULT=`add_column "$RESULT" "Manual pages" $man $?` RESULT=`add_column "$RESULT" "OpenCV" $opencv $?` RESULT=`add_column "$RESULT" "Profiling support" $profile $?` diff --git a/src/host.cpp b/src/host.cpp index d09ba9468..8ef9ad0b3 100644 --- a/src/host.cpp +++ b/src/host.cpp @@ -60,6 +60,12 @@ #include #endif +#ifdef HAVE_LIBBACKTRACE +#include +#else +struct backtrace_state {}; +#endif + #include // for max #include #include // for assert @@ -189,6 +195,8 @@ void *mainloop_udata; extern "C" int _fltused = 0; #endif +static struct backtrace_state *bs; + struct init_data { bool com_initialized = false; list opened_libs; @@ -428,6 +436,49 @@ static void echeck_unexpected_exit(void ) { fprintf(stderr, "exit() called unexpectedly! Maybe by some library?\n"); } +#ifdef HAVE_LIBBACKTRACE +static void +error_callback(void *data, const char *msg, int errnum) +{ + //fprintf(stderr, "libbacktrace error: %s (%d)\n", msg, errnum); + + int fd = *reinterpret_cast(data); + char buf[] = "libbacktrace error: "; + write_all(fd, sizeof buf - 1, buf); + write_all(fd, strlen(msg), msg); + write_all(fd, 2, " ("); + write_number(fd, errnum); + write_all(fd, 2, ")\n"); +} + +static int +full_callback(void *data, uintptr_t pc, const char *filename, int lineno, + const char *function) +{ + // printf(" %s at %s:%d [pc=%p]\n", function ? function : "??", + // filename ? filename : "??", lineno, (void *) pc); + + int fd = *reinterpret_cast(data); + write_all(fd, 2, " "); + if (function == nullptr) { + function = "??"; + } + write_all(fd, strlen(function), function); + write_all(fd, 4, " at "); + if (filename == nullptr) { + filename = "??"; + } + write_all(fd, strlen(filename), filename); + write_all(fd, 1, ":"); + write_number(fd, lineno); + write_all(fd, 7, " [pc=0x"); + write_number(fd, (uintmax_t) pc); + write_all(fd, 2, "]\n"); + + return 0; // continue +} +#endif // defined HAVE_LIBBACKTRACE + struct init_data *common_preinit(int argc, char *argv[]) { uv_argc = argc; @@ -527,6 +578,12 @@ struct init_data *common_preinit(int argc, char *argv[]) fec_init(); #endif +#ifdef HAVE_LIBBACKTRACE + int fd = STDERR_FILENO; + bs = backtrace_create_state(uv_argv[0], 1 /*thread safe*/, + error_callback, &fd); +#endif + atexit(echeck_unexpected_exit); return new init_data{ std::move(init) }; @@ -1225,6 +1282,30 @@ print_stacktrace_glibc() array addresses{}; const int num_symbols = backtrace(addresses.data(), addresses.size()); backtrace_symbols_fd(addresses.data(), num_symbols, fd); + +#ifdef HAVE_LIBBACKTRACE + char backtrace2_msg[] = "\nBacktrace symbolic:\n"; + write_all(fd, sizeof backtrace2_msg - 1, backtrace2_msg); + for (int i = 0; i < num_symbols; i++) { + char sym_nr[5]; + int num_tmp = i; + for (int i = 0; i < 3; ++i) { + if (num_tmp == 0 && i != 0) { + sym_nr[2 - i] = ' '; + } else { + sym_nr[2 - i] = '0' + (num_tmp % 10); + num_tmp /= 10; + } + } + sym_nr[3] = ':'; + sym_nr[4] = ' '; + write_all(fd, sizeof sym_nr, sym_nr); + // printf("%3d: ", i); + backtrace_pcinfo(bs, (uintptr_t) addresses[i], full_callback, + error_callback, &fd); + } +#endif + if (fd == STDERR_FILENO) { return; } diff --git a/src/utils/string.c b/src/utils/string.c index 49f460d59..adf7cead1 100644 --- a/src/utils/string.c +++ b/src/utils/string.c @@ -154,6 +154,18 @@ void write_all(int fd, size_t len, const char *msg) { } while (len > 0); } +void +write_number(int fd, uintmax_t num) +{ + char num_buf[100]; + int idx = sizeof num_buf; + num_buf[--idx] = '0' + num % 10; // write '0' if num=0 + while ((num /= 10) != 0) { + num_buf[--idx] = '0' + num % 10; + } + write_all(fd, sizeof num_buf - idx, num_buf + idx); +} + /** * Appends signal number description to ptr and moves ptr to end of the * appended string. The string is not NULL-terminated. diff --git a/src/utils/string.h b/src/utils/string.h index edf45eecc..68368d35d 100644 --- a/src/utils/string.h +++ b/src/utils/string.h @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2014-2024 CESNET z.s.p.o. + * Copyright (c) 2014-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,10 +40,12 @@ #ifdef __cplusplus #include +#include // for uintmax_t extern "C" { #else #include #include +#include // for uintmax_t #endif // functions documented at definition @@ -57,6 +59,7 @@ char *strrpbrk(char *s, const char *accept); void strappend(char **ptr, const char *ptr_end, const char *src); void append_sig_desc(char **ptr, const char *ptr_end, int signum); void write_all(int fd, size_t len, const char *msg); +void write_number(int fd, uintmax_t num); const char *pretty_print_fourcc(const void *fcc); #ifdef __cplusplus From 9a2956ef34f070b4c3ee80c3cc06fd176fa5bfee Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 13 Aug 2025 12:12:15 +0200 Subject: [PATCH 039/100] print_stacktrace_glibc: fixes - do not write trailing NULL byte - do not use snprintf - is not async-signal-safe - added a bit Doxy docu --- src/host.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/host.cpp b/src/host.cpp index 8ef9ad0b3..a92b4a768 100644 --- a/src/host.cpp +++ b/src/host.cpp @@ -1255,7 +1255,12 @@ bool running_in_debugger(){ } #if defined(__GLIBC__) -/// print stacktrace with backtrace_symbols_fd() (glibc or macOS) +/** + * print stacktrace with backtrace_symbols_fd() (glibc or macOS) + * + * ideally all functions should be async-signal-safe as defined by POSIX + * (glibc deviates sligntly, see also signal-safety(7)) + */ static void print_stacktrace_glibc() { @@ -1266,11 +1271,21 @@ print_stacktrace_glibc() #else char path[MAX_PATH_SIZE]; #ifdef __APPLE__ - const unsigned long tid = pthread_mach_thread_np(pthread_self()); + unsigned long tid = pthread_mach_thread_np(pthread_self()); #else - const unsigned long tid = syscall(__NR_gettid); + unsigned long tid = syscall(__NR_gettid); #endif - snprintf(path, sizeof path, "%s/ug-%lu", get_temp_dir(), tid); + // snprintf(path, sizeof path, "%s/ug-%lu", get_temp_dir(), tid); + strncpy(path, get_temp_dir(), sizeof path); + path[sizeof path - 1] = '\0'; + strncat(path + strlen(path), "/ug-bt-", sizeof path - strlen(path) - 1); + while (tid != 0 && strlen(path) < sizeof path - 1) { + // (tid will be actually printed in reversed order (123->321)) + size_t len = strlen(path); + path[len] = '0' + tid % 10; + path[len + 1] = '\0'; + tid /= 10; + } int fd = open(path, O_CLOEXEC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); unlink(path); #endif @@ -1278,7 +1293,7 @@ print_stacktrace_glibc() fd = STDERR_FILENO; } char backtrace_msg[] = "Backtrace:\n"; - write_all(fd, sizeof backtrace_msg, backtrace_msg); + write_all(fd, sizeof backtrace_msg - 1, backtrace_msg); array addresses{}; const int num_symbols = backtrace(addresses.data(), addresses.size()); backtrace_symbols_fd(addresses.data(), num_symbols, fd); From 1115d403de5da5f0477de3ef4ebbb8ca83dd03b7 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 13 Aug 2025 14:18:36 +0200 Subject: [PATCH 040/100] host: libbacktrace/print_stacktrace_glibc improves - rename some symbols (state bs->bt), callbacks prefix with libbt - assemble the line with strappend-like functions and write() just the result - flush the output after backtrace_symbols_fd() in case that the later calls fail to have at least something output - write_number->append_number for point 2 + tid write --- src/host.cpp | 133 ++++++++++++++++++++++++++------------------- src/utils/string.c | 8 ++- src/utils/string.h | 2 +- 3 files changed, 84 insertions(+), 59 deletions(-) diff --git a/src/host.cpp b/src/host.cpp index a92b4a768..f372748fc 100644 --- a/src/host.cpp +++ b/src/host.cpp @@ -195,7 +195,7 @@ void *mainloop_udata; extern "C" int _fltused = 0; #endif -static struct backtrace_state *bs; +static struct backtrace_state *bt; struct init_data { bool com_initialized = false; @@ -438,42 +438,52 @@ static void echeck_unexpected_exit(void ) { #ifdef HAVE_LIBBACKTRACE static void -error_callback(void *data, const char *msg, int errnum) +libbt_error_callback(void *data, const char *msg, int errnum) { + int fd = *reinterpret_cast(data); + char buf[STR_LEN]; + char *start = buf; + const char *const end = buf + sizeof buf; + //fprintf(stderr, "libbacktrace error: %s (%d)\n", msg, errnum); - int fd = *reinterpret_cast(data); - char buf[] = "libbacktrace error: "; - write_all(fd, sizeof buf - 1, buf); - write_all(fd, strlen(msg), msg); - write_all(fd, 2, " ("); - write_number(fd, errnum); - write_all(fd, 2, ")\n"); + strappend(&start, end, "libbacktrace error: "); + strappend(&start, end, msg); + strappend(&start, end, " ("); + append_number(&start, end, errnum); + + write_all(fd, start - buf, buf); } static int -full_callback(void *data, uintptr_t pc, const char *filename, int lineno, +libbt_full_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) { + int fd = *reinterpret_cast(data); + char buf[STR_LEN]; + char *start = buf; + const char *const end = buf + sizeof buf; + // printf(" %s at %s:%d [pc=%p]\n", function ? function : "??", // filename ? filename : "??", lineno, (void *) pc); - int fd = *reinterpret_cast(data); - write_all(fd, 2, " "); + strappend(&start, end, " "); if (function == nullptr) { function = "??"; } - write_all(fd, strlen(function), function); - write_all(fd, 4, " at "); + strappend(&start, end, function); + strappend(&start, end, " at "); if (filename == nullptr) { filename = "??"; } - write_all(fd, strlen(filename), filename); - write_all(fd, 1, ":"); - write_number(fd, lineno); - write_all(fd, 7, " [pc=0x"); - write_number(fd, (uintmax_t) pc); - write_all(fd, 2, "]\n"); + strappend(&start, end, filename); + strappend(&start, end, ":"); + append_number(&start, end, lineno); + strappend(&start, end, " [pc=0x"); + append_number(&start, end, (uintmax_t) pc); + strappend(&start, end, "]\n"); + + write_all(fd, start - buf, buf); return 0; // continue } @@ -580,8 +590,8 @@ struct init_data *common_preinit(int argc, char *argv[]) #ifdef HAVE_LIBBACKTRACE int fd = STDERR_FILENO; - bs = backtrace_create_state(uv_argv[0], 1 /*thread safe*/, - error_callback, &fd); + bt = backtrace_create_state(uv_argv[0], 1 /*thread safe*/, + libbt_error_callback, &fd); #endif atexit(echeck_unexpected_exit); @@ -1255,6 +1265,30 @@ bool running_in_debugger(){ } #if defined(__GLIBC__) +/// dumps output of fd (from start_off offset) to stderr +/// and keep the pointer at the end of the file +/// @retval size of the file pointed by fd (current pos) +static off_t +st_glibc_flush_output(int fd, off_t start_off) +{ + if (fd == STDERR_FILENO) { + return 0; + } + + lseek(fd, start_off, SEEK_SET); + char buf[STR_LEN]; + ssize_t rbytes = 0; + while ((rbytes = read(fd, buf, sizeof buf)) > 0) { + ssize_t written = 0; + ssize_t wbytes = 0; + while (written < rbytes && + (wbytes = write(STDERR_FILENO, buf + written, + rbytes - written)) > 0) { + written += wbytes; + } + } + return lseek(fd, 0, SEEK_CUR); +} /** * print stacktrace with backtrace_symbols_fd() (glibc or macOS) * @@ -1276,16 +1310,13 @@ print_stacktrace_glibc() unsigned long tid = syscall(__NR_gettid); #endif // snprintf(path, sizeof path, "%s/ug-%lu", get_temp_dir(), tid); - strncpy(path, get_temp_dir(), sizeof path); + char *start = path; path[sizeof path - 1] = '\0'; - strncat(path + strlen(path), "/ug-bt-", sizeof path - strlen(path) - 1); - while (tid != 0 && strlen(path) < sizeof path - 1) { - // (tid will be actually printed in reversed order (123->321)) - size_t len = strlen(path); - path[len] = '0' + tid % 10; - path[len + 1] = '\0'; - tid /= 10; - } + const char *const end = path + sizeof path - 1; + strappend(&start, end, get_temp_dir()); + strappend(&start, end, "/ug-bt-"); + append_number(&start, end, tid); + *start = '\0'; int fd = open(path, O_CLOEXEC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); unlink(path); #endif @@ -1298,46 +1329,36 @@ print_stacktrace_glibc() const int num_symbols = backtrace(addresses.data(), addresses.size()); backtrace_symbols_fd(addresses.data(), num_symbols, fd); + // in case that the below fails, try write at least something + off_t last_pos = st_glibc_flush_output(fd, 0); + #ifdef HAVE_LIBBACKTRACE char backtrace2_msg[] = "\nBacktrace symbolic:\n"; write_all(fd, sizeof backtrace2_msg - 1, backtrace2_msg); for (int i = 0; i < num_symbols; i++) { - char sym_nr[5]; + // printf("%2d: ", i); + enum { NDIGITS = 2 }; + char sym_nr[] = { 'X', 'X', ':', ' ' }; int num_tmp = i; - for (int i = 0; i < 3; ++i) { + for (int i = 0; i < NDIGITS; ++i) { if (num_tmp == 0 && i != 0) { - sym_nr[2 - i] = ' '; + sym_nr[NDIGITS - 1 - i] = ' '; } else { - sym_nr[2 - i] = '0' + (num_tmp % 10); + sym_nr[NDIGITS - 1 - i] = '0' + (num_tmp % 10); num_tmp /= 10; } } - sym_nr[3] = ':'; - sym_nr[4] = ' '; write_all(fd, sizeof sym_nr, sym_nr); - // printf("%3d: ", i); - backtrace_pcinfo(bs, (uintptr_t) addresses[i], full_callback, - error_callback, &fd); + // backtrace_pcinfo may not be async-signal-safe + backtrace_pcinfo(bt, (uintptr_t) addresses[i], libbt_full_callback, + libbt_error_callback, &fd); } + st_glibc_flush_output(fd, last_pos); #endif - if (fd == STDERR_FILENO) { - return; - } - - lseek(fd, 0, SEEK_SET); - char buf[STR_LEN]; - ssize_t rbytes = 0; - while ((rbytes = read(fd, buf, sizeof buf)) > 0) { - ssize_t written = 0; - ssize_t wbytes = 0; - while (written < rbytes && - (wbytes = write(STDERR_FILENO, buf + written, - rbytes - written)) > 0) { - written += wbytes; - } + if (fd != STDERR_FILENO) { + close(fd); } - close(fd); } #endif // defined(__GLIBC__) diff --git a/src/utils/string.c b/src/utils/string.c index adf7cead1..3afb5ea08 100644 --- a/src/utils/string.c +++ b/src/utils/string.c @@ -49,6 +49,7 @@ #include "compat/strings.h" #include "debug.h" +#include "utils/macros.h" // for MIN #include "utils/string.h" /** @@ -155,7 +156,7 @@ void write_all(int fd, size_t len, const char *msg) { } void -write_number(int fd, uintmax_t num) +append_number(char **ptr, const char *ptr_end, uintmax_t num) { char num_buf[100]; int idx = sizeof num_buf; @@ -163,7 +164,10 @@ write_number(int fd, uintmax_t num) while ((num /= 10) != 0) { num_buf[--idx] = '0' + num % 10; } - write_all(fd, sizeof num_buf - idx, num_buf + idx); + const size_t buflen = ptr_end - *ptr; + const size_t len = MIN(buflen, sizeof num_buf - idx); + strncpy(*ptr, num_buf + idx, len); + *ptr += len; } /** diff --git a/src/utils/string.h b/src/utils/string.h index 68368d35d..26bfff5bf 100644 --- a/src/utils/string.h +++ b/src/utils/string.h @@ -57,9 +57,9 @@ bool is_prefix_of(const char *haystack, const char *needle); /// same as strpbrk but finds in a reverse order (last occurrence returned) char *strrpbrk(char *s, const char *accept); void strappend(char **ptr, const char *ptr_end, const char *src); +void append_number(char **ptr, const char *ptr_end, uintmax_t num); void append_sig_desc(char **ptr, const char *ptr_end, int signum); void write_all(int fd, size_t len, const char *msg); -void write_number(int fd, uintmax_t num); const char *pretty_print_fourcc(const void *fcc); #ifdef __cplusplus From 624bd0f51708763d67ad87d256a4345dd6ecc12d Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 13 Aug 2025 14:57:13 +0200 Subject: [PATCH 041/100] CI Linux: enforce libbacktrace In Debian/Ubuntu, libbacktrace is part of libgcc-XY-dev package so no need to install anything (but this doesn't hold eg. for Arch where this is a packed as a standalone package libbacktrace). --- .github/scripts/environment.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/scripts/environment.sh b/.github/scripts/environment.sh index 1093379c1..95f25827d 100644 --- a/.github/scripts/environment.sh +++ b/.github/scripts/environment.sh @@ -81,8 +81,13 @@ export FEATURES="\ CUDA_FEATURES="--enable-cuda_dxt --enable-gpujpeg --enable-ldgm-gpu --enable-uyvy" case "$RUNNER_OS" in Linux) - FEATURES="$FEATURES --enable-plugins --enable-alsa \ ---enable-pipewire-audio --enable-v4l2 --enable-lavc-hw-accel-vaapi" + FEATURES="$FEATURES --enable-plugins \ +--enable-alsa \ +--enable-lavc-hw-accel-vaapi \ +--enable-libbacktrace \ +--enable-pipewire-audio \ +--enable-v4l2 \ +" if is_arm; then FEATURES="$FEATURES --disable-qt" else From e233623787550fbb50f16fcccf6c0fb616f552b6 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 13 Aug 2025 15:18:36 +0200 Subject: [PATCH 042/100] host: reenable stacktrace on mac reenable the stacktrace Disabled accidentaly by the commit 222499a2 (2025-04-14), which removed the backgrace on everything except glibc and Windows. --- src/host.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/host.cpp b/src/host.cpp index f372748fc..984b72d01 100644 --- a/src/host.cpp +++ b/src/host.cpp @@ -46,7 +46,7 @@ #ifdef _WIN32 #include -#elif defined(__GLIBC__) +#elif defined(__APPLE__) || defined(__GLIBC__) #include #include #endif // !defined _WIN32 @@ -1264,7 +1264,7 @@ bool running_in_debugger(){ return false; } -#if defined(__GLIBC__) +#if defined(__APPLE__) || defined(__GLIBC__) /// dumps output of fd (from start_off offset) to stderr /// and keep the pointer at the end of the file /// @retval size of the file pointed by fd (current pos) @@ -1367,7 +1367,7 @@ print_backtrace() { #ifdef _WIN32 print_stacktrace_win(); -#elif defined(__GLIBC__) +#elif defined(__APPLE__) || defined(__GLIBC__) print_stacktrace_glibc(); #else const char *msg = "Stacktrace printout not supported!\n"; From c0d88af1cea9594115b9fea1b6e417d0512a404c Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 13 Aug 2025 15:30:31 +0200 Subject: [PATCH 043/100] CI mac: add libbacktrace --- .github/scripts/environment.sh | 7 ++++++- .github/scripts/macOS/install_others.sh | 10 +++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/scripts/environment.sh b/.github/scripts/environment.sh index 95f25827d..8141388af 100644 --- a/.github/scripts/environment.sh +++ b/.github/scripts/environment.sh @@ -96,7 +96,12 @@ case "$RUNNER_OS" in fi ;; macOS) - FEATURES="$FEATURES --enable-avfoundation --enable-coreaudio --enable-syphon" + FEATURES="$FEATURES \ +--enable-avfoundation \ +--enable-coreaudio \ +--enable-libbacktrace \ +--enable-syphon \ +" ;; Windows) FEATURES="$FEATURES $CUDA_FEATURES --enable-dshow --enable-spout --enable-wasapi" diff --git a/.github/scripts/macOS/install_others.sh b/.github/scripts/macOS/install_others.sh index f97501d83..df617ab99 100755 --- a/.github/scripts/macOS/install_others.sh +++ b/.github/scripts/macOS/install_others.sh @@ -52,6 +52,14 @@ install_glfw() {( sudo cmake --install . )} +install_libbacktrace() {( + git clone --depth 1 https://github.com/ianlancetaylor/libbacktrace + cd libbacktrace + ./configure + make -j "$(getconf NPROCESSORS_ONLN)" + sudo make install +)} + # Install NDI install_ndi() {( # installer downloaed by cache step @@ -90,7 +98,7 @@ if [ $# -eq 1 ] && { [ "$1" = -h ] || [ "$1" = --help ] || [ "$1" = help ]; }; t fi if [ $# -eq 0 ] || [ $show_help ]; then - set -- deltacast glfw ndi soundfont syphon ximea + set -- deltacast glfw libbacktrace ndi soundfont syphon ximea fi if [ $show_help ]; then From c82282949fe06f8aadc0af57e59d29ec84a512e1 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 13 Aug 2025 15:23:07 +0200 Subject: [PATCH 044/100] Makefile: run dsymutil [mac] needed for libbacktrace --- Makefile.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.in b/Makefile.in index c92f77272..f3ec64135 100644 --- a/Makefile.in +++ b/Makefile.in @@ -244,10 +244,12 @@ $(TARGET): src/dir-stamp $(ULTRAGRID_OBJS) $(GENERATED_HEADERS) $(BIN_DEPS) $(LINKER) $(LDFLAGS) @UV_LDFLAGS@ $(ULTRAGRID_OBJS) $(LIBS) $(ULTRAGRID_LIBS)\ -o $(TARGET) @if [ -n '@DLL_LIBS@' ]; then $(INSTALL) -m 644 @DLL_LIBS@ bin; fi + @if [ "$$(uname -s)" = Darwin ]; then dsymutil $(TARGET); fi $(REFLECTOR_TARGET): src/dir-stamp $(REFLECTOR_OBJS) $(GENERATED_HEADERS) bin/hd-rum-av $(MKDIR_P) $$(dirname $@) $(LINKER) $(LDFLAGS) $(REFLECTOR_OBJS) $(LIBS) -o $@ + @if [ "$$(uname -s)" = Darwin ]; then dsymutil $(REFLECTOR_TARGET); fi bin/hd-rum-av: $(srcdir)/data/template/bin/hd-rum-av $(MKDIR_P) $$(dirname $@) From c67610c5276cd0286bfafe87e317f31e1486da18 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 13 Aug 2025 16:06:34 +0200 Subject: [PATCH 045/100] macos_bundle_libs.sh: avoid otool on dir eg. uv.dSYM --- data/scripts/macos_bundle_libs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/scripts/macos_bundle_libs.sh b/data/scripts/macos_bundle_libs.sh index dd634382a..e54e97489 100755 --- a/data/scripts/macos_bundle_libs.sh +++ b/data/scripts/macos_bundle_libs.sh @@ -16,7 +16,7 @@ starts_with_shebang() { } for n in "$bundle"/Contents/MacOS/*; do - if starts_with_shebang "$n"; then + if [ ! -f "$n" ] || starts_with_shebang "$n"; then continue fi # shellcheck disable=SC2086 # intentional, even $dylibbundler From 6651e0efb14b4d0153ee1969cb2915c59d543eb8 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 14 Aug 2025 07:48:38 +0200 Subject: [PATCH 046/100] Makefile macOS bundle: copy debug symbols --- Makefile.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index f3ec64135..01967b22f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -598,6 +598,7 @@ $(BUNDLE): $(TARGET) $(REFLECTOR_TARGET) @MANPAGES@ hd-rum-multi/hd-rum rm -rf $(BUNDLE) $(MKDIR_P) $(BUNDLE)/Contents/MacOS $(BUNDLE)/Contents/libs $(CP) $(REFLECTOR_TARGET) $(TARGET) $(BUNDLE)/Contents/MacOS/ + $(CP) -r bin/*.dSYM $(BUNDLE)/Contents/MacOS/ $(CP) -r data/template/macOS-bundle/* $(BUNDLE)/ $(CP) hd-rum-multi/hd-rum $(BUNDLE)/Contents/MacOS/ $(CP) $(srcdir)/data/template/bin/* $(BUNDLE)/Contents/MacOS @@ -621,8 +622,8 @@ $(GUI_BUNDLE): $(BUNDLE) $(GUI_BUNDLE_DEP) $(CP) -nR $(BUNDLE)/* $(GUI_BUNDLE)/ || true if [ $(MACOS_LEGACY) = no ]; then \ for n in $(GUI_BUNDLE)/Contents/MacOS/*; do \ - if expr $$n : '.*-real$$' >/dev/null || expr $$n : '.*\.sh$$' >/dev/null; \ - then continue; fi; \ + if expr $$n : '.*-real$$' >/dev/null || expr $$n : '.*\.sh$$' >/dev/null || \ + [ !-f $$n ]; then continue; fi; \ mv -f $$n $$n-real; $(CP) -f $(srcdir)/data/scripts/macos-wrapper $$n; \ done; \ fi From 4a8c12f416087c72366d8baff1445d6a5052b8bc Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 14 Aug 2025 09:08:15 +0200 Subject: [PATCH 047/100] add .sh suffix to hd-rum-av This is more convenient and allow the users that honor that behave more correctly, eg. he macOS legacy compat wrapper in Makefile adding -real to executable binaries. --- Makefile.in | 8 ++++---- data/scripts/Linux-AppImage/AppRun | 2 +- data/template/bin/{hd-rum-av => hd-rum-av.sh} | 0 3 files changed, 5 insertions(+), 5 deletions(-) rename data/template/bin/{hd-rum-av => hd-rum-av.sh} (100%) diff --git a/Makefile.in b/Makefile.in index 01967b22f..519dba0c5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -246,14 +246,14 @@ $(TARGET): src/dir-stamp $(ULTRAGRID_OBJS) $(GENERATED_HEADERS) $(BIN_DEPS) @if [ -n '@DLL_LIBS@' ]; then $(INSTALL) -m 644 @DLL_LIBS@ bin; fi @if [ "$$(uname -s)" = Darwin ]; then dsymutil $(TARGET); fi -$(REFLECTOR_TARGET): src/dir-stamp $(REFLECTOR_OBJS) $(GENERATED_HEADERS) bin/hd-rum-av +$(REFLECTOR_TARGET): src/dir-stamp $(REFLECTOR_OBJS) $(GENERATED_HEADERS) bin/hd-rum-av.sh $(MKDIR_P) $$(dirname $@) $(LINKER) $(LDFLAGS) $(REFLECTOR_OBJS) $(LIBS) -o $@ @if [ "$$(uname -s)" = Darwin ]; then dsymutil $(REFLECTOR_TARGET); fi -bin/hd-rum-av: $(srcdir)/data/template/bin/hd-rum-av +bin/hd-rum-av.sh: $(srcdir)/data/template/bin/hd-rum-av.sh $(MKDIR_P) $$(dirname $@) - $(CP) $(srcdir)/data/template/bin/hd-rum-av $@ + $(CP) $(srcdir)/data/template/bin/hd-rum-av.sh $@ -include $(DEP_FILES) @@ -553,7 +553,7 @@ clean: $(COND_SILENCE)-rm -f $(TEST_OBJS) bin/run_tests $(COND_SILENCE)-rm -f data/ag_plugin/uvReceiverService.zip data/ag_plugin/uvSenderService.zip $(COND_SILENCE)-rm -rf $(BUNDLE) $(GUI_BUNDLE) $(GUI_BUNDLE_DEP) - $(COND_SILENCE)-rm -rf $(REFLECTOR_TARGET) bin/hd-rum-av $(REFLECTOR_OBJS) + $(COND_SILENCE)-rm -rf $(REFLECTOR_TARGET) bin/hd-rum-av.sh $(REFLECTOR_OBJS) $(COND_SILENCE)-rm -rf @TOREMOVE@ @MODULES@ @LIB_GENERATED_HEADERS@ $(COND_SILENCE)-rm -rf $(DEP_FILES) $(COND_SILENCE)-rm -rf bin/shaders diff --git a/data/scripts/Linux-AppImage/AppRun b/data/scripts/Linux-AppImage/AppRun index 428f8f385..439a717c9 100755 --- a/data/scripts/Linux-AppImage/AppRun +++ b/data/scripts/Linux-AppImage/AppRun @@ -337,7 +337,7 @@ setup_firejail() { if firejail --help | grep -q -- --keep-var-tmp; then FIREJAIL_OPTS="$FIREJAIL_OPTS --keep-var-tmp" fi - if [ "$tool" = hd-rum-av ]; then + if [ "$tool" = hd-rum-av.sh ]; then FIREJAIL_OPTS="$FIREJAIL_OPTS --private-bin=basename,dirname,\ expr,kill,ps,sed,seq,sh,tput,tr,tty,uname" fi diff --git a/data/template/bin/hd-rum-av b/data/template/bin/hd-rum-av.sh similarity index 100% rename from data/template/bin/hd-rum-av rename to data/template/bin/hd-rum-av.sh From b3368a0182c81e314bb0a39e09871482dc4995e1 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 14 Aug 2025 10:16:36 +0200 Subject: [PATCH 048/100] Makefile macOS: generate symbols in GUI bundle Moving just the X.dSYM directory isn't enough since the executable is identified by name inside the .dSYM tree. But it may be possible to move the symbols to the "main" tree, since the subtree mimics the hierarchy, eg. for uv, the symbols are in uv.dSYM/Contents/Resources. --- Makefile.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 519dba0c5..31ba79538 100644 --- a/Makefile.in +++ b/Makefile.in @@ -620,11 +620,13 @@ $(GUI_BUNDLE): $(BUNDLE) $(GUI_BUNDLE_DEP) # add Qt frameworks command -v macdeployqt && macdeployqt $(GUI_BUNDLE) -verbose=2 $(CP) -nR $(BUNDLE)/* $(GUI_BUNDLE)/ || true + rm -rf $(GUI_BUNDLE)/Contents/MacOS/*.dSYM if [ $(MACOS_LEGACY) = no ]; then \ for n in $(GUI_BUNDLE)/Contents/MacOS/*; do \ if expr $$n : '.*-real$$' >/dev/null || expr $$n : '.*\.sh$$' >/dev/null || \ [ !-f $$n ]; then continue; fi; \ - mv -f $$n $$n-real; $(CP) -f $(srcdir)/data/scripts/macos-wrapper $$n; \ + mv -f $$n $$n-real; dsymutil $$n-real; \ + $(CP) -f $(srcdir)/data/scripts/macos-wrapper $$n; \ done; \ fi From 505e136382fb074cda41242703de973349a0f4f8 Mon Sep 17 00:00:00 2001 From: Martin Piatka Date: Mon, 18 Aug 2025 10:03:18 +0200 Subject: [PATCH 049/100] configure.ac: Fix --disable-gpujpeg_to_dxt option in help text --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ea4b6b29d..fd419f3fa 100644 --- a/configure.ac +++ b/configure.ac @@ -2468,7 +2468,7 @@ ENSURE_FEATURE_PRESENT([$cuda_dxt_req], [$cuda_dxt], [CUDA DXT not found]) # ------------------------------------------------------------------------------------------------- gpujpeg_to_dxt=no AC_ARG_ENABLE(gpujpeg_to_dxt, -[ --disable-jpeg-to-dxt disable GPUJPEG DXT transcoder (default is disable)] +[ --disable-gpujpeg_to_dxt disable GPUJPEG DXT transcoder (default is disable)] [ Requires: CUDA libgpujpeg], [gpujpeg_to_dxt_req=$enableval], [gpujpeg_to_dxt_req=no]) From b42ff25d72e91c471042bde242a0ca41abc62e33 Mon Sep 17 00:00:00 2001 From: Martin Piatka Date: Mon, 18 Aug 2025 10:05:05 +0200 Subject: [PATCH 050/100] acap/pipewire: Fix help text --- src/audio/capture/pipewire.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audio/capture/pipewire.cpp b/src/audio/capture/pipewire.cpp index 90f21ca2f..732082c4c 100644 --- a/src/audio/capture/pipewire.cpp +++ b/src/audio/capture/pipewire.cpp @@ -194,7 +194,7 @@ const static pw_stream_events stream_events = { static void audio_cap_pw_help(){ color_printf("Pipewire audio capture.\n"); color_printf("Usage\n"); - color_printf(TERM_BOLD TERM_FG_RED "\t-r pipewire" TERM_FG_RESET "[:target=][:buffer-len=][:channels=][:sample-rate=]\n" TERM_RESET); + color_printf(TERM_BOLD TERM_FG_RED "\t-s pipewire" TERM_FG_RESET "[:target=][:buffer-len=][:channels=][:sample-rate=]\n" TERM_RESET); color_printf("\n"); color_printf("Devices:\n"); From aef1b690cd9ec7372875083e9d9ffce6f4bc20d5 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Tue, 19 Aug 2025 09:14:01 +0200 Subject: [PATCH 051/100] fix CID 488781 very marginal, but... --- src/host.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/host.cpp b/src/host.cpp index 984b72d01..e0803a20d 100644 --- a/src/host.cpp +++ b/src/host.cpp @@ -1265,6 +1265,18 @@ bool running_in_debugger(){ } #if defined(__APPLE__) || defined(__GLIBC__) +static void +perror_sig_safe(const char *s) +{ + char buf[128]; + char *start = buf; + const char *const end = buf + sizeof buf; + strappend(&start, end, s); + strappend(&start, end, ": "); + append_number(&start, end, errno); + strappend(&start, end, "\n"); + write_all(STDERR_FILENO, start - buf, buf); +} /// dumps output of fd (from start_off offset) to stderr /// and keep the pointer at the end of the file /// @retval size of the file pointed by fd (current pos) @@ -1275,7 +1287,11 @@ st_glibc_flush_output(int fd, off_t start_off) return 0; } - lseek(fd, start_off, SEEK_SET); + const off_t off = lseek(fd, start_off, SEEK_SET); + if (off == -1) { + perror_sig_safe("lseek"); + } + char buf[STR_LEN]; ssize_t rbytes = 0; while ((rbytes = read(fd, buf, sizeof buf)) > 0) { From f170a505aa3ccce695c05752601ea2327c71e5ff Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 20 Aug 2025 10:26:31 +0200 Subject: [PATCH 052/100] old live555 compat --- src/rtsp/c_basicRTSPOnlyServer.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/rtsp/c_basicRTSPOnlyServer.cpp b/src/rtsp/c_basicRTSPOnlyServer.cpp index dd05251ca..1ff416c2b 100644 --- a/src/rtsp/c_basicRTSPOnlyServer.cpp +++ b/src/rtsp/c_basicRTSPOnlyServer.cpp @@ -48,6 +48,11 @@ #include "rtsp/c_basicRTSPOnlyServer.h" #include "rtsp/BasicRTSPOnlyServer.hh" +// compat +#if BASICUSAGEENVIRONMENT_LIBRARY_VERSION_INT < 1752883200 +typedef char volatile EventLoopWatchVariable; +#endif + struct rtsp_serv { struct rtsp_server_parameters params; pthread_t server_th; @@ -61,10 +66,11 @@ c_start_server(struct rtsp_server_parameters params) server->params = params; server->watch = 0; - int ret; BasicRTSPOnlyServer *srv = BasicRTSPOnlyServer::initInstance(server->params); srv->init_server(); - ret = pthread_create(&server->server_th, NULL, BasicRTSPOnlyServer::start_server, &server->watch); + int ret = pthread_create(&server->server_th, NULL, + BasicRTSPOnlyServer::start_server, + (void *) &server->watch); assert(ret == 0); return server; From 3af5f4dad6f11c4c50a85a7b781a9c12280e52ba Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 20 Aug 2025 12:04:39 +0200 Subject: [PATCH 053/100] CI Linux FFmpeg: update SVT-HEVC patch the older seem no longer apply with the last 5000 commits from FFmpeg Git rebased upon current FFmpeg Git master --- ...hevc-add-libsvt-hevc-encoder-wrapper.patch | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/scripts/Linux/ffmpeg-patches/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch b/.github/scripts/Linux/ffmpeg-patches/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch index 5ef50898f..3357f93ab 100644 --- a/.github/scripts/Linux/ffmpeg-patches/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch +++ b/.github/scripts/Linux/ffmpeg-patches/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch @@ -1,4 +1,4 @@ -From 3d2c2bba486f11f7a55eaf8b51159dea19c03231 Mon Sep 17 00:00:00 2001 +From 19f4bd57e3d60dc6e40bc1ff96fbb91916dd7cc4 Mon Sep 17 00:00:00 2001 From: Jing Sun Date: Wed, 21 Nov 2018 11:33:04 +0800 Subject: [PATCH] lavc/svt_hevc: add libsvt hevc encoder wrapper @@ -21,18 +21,18 @@ UPDATED 2025-06-20 by Martin Pulec: rebased against 45a30e036 + fix compile create mode 100644 libavcodec/libsvt_hevc.c diff --git a/configure b/configure -index 077b87af..5dcc49ca 100755 +index e1809a3e58..936cea82ab 100755 --- a/configure +++ b/configure @@ -339,6 +339,7 @@ External library support: - --enable-vapoursynth enable VapourSynth demuxer [no] + --enable-whisper enable whisper filter [no] --disable-xlib disable xlib [autodetect] --disable-zlib disable zlib [autodetect] + --enable-libsvthevc enable HEVC encoding via svt [no] The following libraries provide various hardware acceleration features: --disable-amf disable AMF video encoding code [autodetect] -@@ -1981,6 +1982,7 @@ EXTERNAL_LIBRARY_LIST=" +@@ -1979,6 +1980,7 @@ EXTERNAL_LIBRARY_LIST=" libsrt libssh libsvtav1 @@ -40,7 +40,7 @@ index 077b87af..5dcc49ca 100755 libtensorflow libtesseract libtheora -@@ -3655,6 +3657,7 @@ vapoursynth_demuxer_deps="vapoursynth" +@@ -3678,6 +3680,7 @@ vapoursynth_demuxer_deps="vapoursynth" videotoolbox_suggest="coreservices" videotoolbox_deps="corefoundation coremedia corevideo VTDecompressionSessionDecodeFrame" videotoolbox_encoder_deps="videotoolbox VTCompressionSessionPrepareToEncodeFrames" @@ -48,19 +48,19 @@ index 077b87af..5dcc49ca 100755 # demuxers / muxers ac3_demuxer_select="ac3_parser" -@@ -7094,6 +7097,7 @@ enabled libssh && require_pkg_config libssh "libssh >= 0.6.0" libssh/ +@@ -7149,6 +7152,7 @@ enabled libssh && require_pkg_config libssh "libssh >= 0.6.0" libssh/ enabled libspeex && require_pkg_config libspeex speex speex/speex.h speex_decoder_init enabled libsrt && require_pkg_config libsrt "srt >= 1.3.0" srt/srt.h srt_socket enabled libsvtav1 && require_pkg_config libsvtav1 "SvtAv1Enc >= 0.9.0" EbSvtAv1Enc.h svt_av1_enc_init_handle +enabled libsvthevc && require_pkg_config libsvthevc SvtHevcEnc EbApi.h EbInitHandle - enabled libsvtvp9 && require_pkg_config libsvtvp9 SvtVp9Enc EbSvtVp9Enc.h eb_vp9_svt_init_handle enabled libtensorflow && require libtensorflow tensorflow/c/c_api.h TF_Version -ltensorflow enabled libtesseract && require_pkg_config libtesseract tesseract tesseract/capi.h TessBaseAPICreate + enabled libtheora && require libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg diff --git a/libavcodec/Makefile b/libavcodec/Makefile -index 805b0a07..49c644ca 100644 +index 35408949ac..12f534a1d7 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile -@@ -1188,6 +1188,7 @@ OBJS-$(CONFIG_LIBWEBP_ANIM_ENCODER) += libwebpenc_common.o libwebpenc_anim +@@ -1202,6 +1202,7 @@ OBJS-$(CONFIG_LIBWEBP_ANIM_ENCODER) += libwebpenc_common.o libwebpenc_anim OBJS-$(CONFIG_LIBX262_ENCODER) += libx264.o OBJS-$(CONFIG_LIBX264_ENCODER) += libx264.o OBJS-$(CONFIG_LIBX265_ENCODER) += libx265.o @@ -69,10 +69,10 @@ index 805b0a07..49c644ca 100644 OBJS-$(CONFIG_LIBXAVS2_ENCODER) += libxavs2.o OBJS-$(CONFIG_LIBXEVD_DECODER) += libxevd.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c -index fef337ca..8e67e0df 100644 +index f5ec2e01e8..7844040a00 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c -@@ -826,6 +826,7 @@ extern const FFCodec ff_libxavs_encoder; +@@ -827,6 +827,7 @@ extern const FFCodec ff_libxavs_encoder; extern const FFCodec ff_libxavs2_encoder; extern const FFCodec ff_libxvid_encoder; extern const FFCodec ff_libzvbi_teletext_decoder; @@ -82,7 +82,7 @@ index fef337ca..8e67e0df 100644 extern const FFCodec ff_bintext_decoder; diff --git a/libavcodec/libsvt_hevc.c b/libavcodec/libsvt_hevc.c new file mode 100644 -index 00000000..07f62fa7 +index 0000000000..07f62fa762 --- /dev/null +++ b/libavcodec/libsvt_hevc.c @@ -0,0 +1,584 @@ @@ -671,5 +671,5 @@ index 00000000..07f62fa7 + .p.wrapper_name = "libsvt_hevc", +}; -- -2.50.0 +2.50.1 From 877ba7a7faa3be823479759982b11dbf1320c921 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 20 Aug 2025 12:04:39 +0200 Subject: [PATCH 054/100] live555: move the compat macro to header the typedef is needed in BasicRTSPOnlyServer.cpp as well --- src/rtsp/BasicRTSPOnlyServer.hh | 6 ++++++ src/rtsp/c_basicRTSPOnlyServer.cpp | 5 ----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/rtsp/BasicRTSPOnlyServer.hh b/src/rtsp/BasicRTSPOnlyServer.hh index a1bef9deb..4d15a0784 100644 --- a/src/rtsp/BasicRTSPOnlyServer.hh +++ b/src/rtsp/BasicRTSPOnlyServer.hh @@ -4,6 +4,7 @@ * Gerard Castillo * * Copyright (c) 2005-2010 Fundació i2CAT, Internet I Innovació Digital a Catalunya + * Copyright (c) 2019-2025 CESNET * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions @@ -50,6 +51,11 @@ #include "c_basicRTSPOnlyServer.h" // for rtsp_server_parameters +// compat +#if BASICUSAGEENVIRONMENT_LIBRARY_VERSION_INT < 1752883200 +typedef char volatile EventLoopWatchVariable; +#endif + class BasicRTSPOnlyServer { private: BasicRTSPOnlyServer(struct rtsp_server_parameters params); diff --git a/src/rtsp/c_basicRTSPOnlyServer.cpp b/src/rtsp/c_basicRTSPOnlyServer.cpp index 1ff416c2b..1311e8f0e 100644 --- a/src/rtsp/c_basicRTSPOnlyServer.cpp +++ b/src/rtsp/c_basicRTSPOnlyServer.cpp @@ -48,11 +48,6 @@ #include "rtsp/c_basicRTSPOnlyServer.h" #include "rtsp/BasicRTSPOnlyServer.hh" -// compat -#if BASICUSAGEENVIRONMENT_LIBRARY_VERSION_INT < 1752883200 -typedef char volatile EventLoopWatchVariable; -#endif - struct rtsp_serv { struct rtsp_server_parameters params; pthread_t server_th; From e79c688497aa16c98bade8aa8f247cec9c44ad8b Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 20 Aug 2025 14:31:34 +0200 Subject: [PATCH 055/100] host: silence warns if libbacktrace not present --- src/host.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/host.cpp b/src/host.cpp index e0803a20d..cab9f4821 100644 --- a/src/host.cpp +++ b/src/host.cpp @@ -62,8 +62,6 @@ #ifdef HAVE_LIBBACKTRACE #include -#else -struct backtrace_state {}; #endif #include // for max @@ -195,8 +193,6 @@ void *mainloop_udata; extern "C" int _fltused = 0; #endif -static struct backtrace_state *bt; - struct init_data { bool com_initialized = false; list opened_libs; @@ -437,6 +433,8 @@ static void echeck_unexpected_exit(void ) { } #ifdef HAVE_LIBBACKTRACE +static struct backtrace_state *bt; + static void libbt_error_callback(void *data, const char *msg, int errnum) { @@ -1370,6 +1368,8 @@ print_stacktrace_glibc() libbt_error_callback, &fd); } st_glibc_flush_output(fd, last_pos); +#else + (void) last_pos; #endif if (fd != STDERR_FILENO) { From 5755ce818fbb7fa26c1edcbe4efe4b7fadfa413c Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 25 Aug 2025 12:50:39 +0200 Subject: [PATCH 056/100] video_capture/*: IWYU --- src/video_capture/null.c | 19 +++++++++------- src/video_capture/screen_linux.c | 13 +++++++---- src/video_capture/screen_osx.c | 39 ++++++++++++++++---------------- src/video_capture/v4l2.c | 15 ++++++++---- src/video_capture/ximea.c | 2 +- 5 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/video_capture/null.c b/src/video_capture/null.c index 78cc7155d..a9ef373aa 100644 --- a/src/video_capture/null.c +++ b/src/video_capture/null.c @@ -6,7 +6,7 @@ * hardware or do not wish to transmit. This fits the interface of the other * capture devices, but never produces any video. * - * Copyright (c) 2005-2023 CESNET + * Copyright (c) 2005-2025 CESNET * Copyright (c) 2004 University of Glasgow * Copyright (c) 2003 University of Southern California * @@ -49,13 +49,16 @@ * */ -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#include "debug.h" -#include "lib_common.h" -#include "video.h" -#include "video_capture.h" +#include // for assert +#include // for NULL, free + +#include "lib_common.h" // for REGISTER_HIDDEN_MODULE, library_class +#include "video_capture.h" // for VIDCAP_INIT_AUDIO_NOT_SUPPORTED +#include "video_capture_params.h" // for vidcap_params_get_flags, VIDCAP_FL... + +struct audio_frame; +struct device_info; +struct vidcap_params; static int capture_state = 0; diff --git a/src/video_capture/screen_linux.c b/src/video_capture/screen_linux.c index 36a2b74cb..b306c1b93 100644 --- a/src/video_capture/screen_linux.c +++ b/src/video_capture/screen_linux.c @@ -5,7 +5,7 @@ * X11/PipeWire screen capture abstraction */ /* - * Copyright (c) 2023 CESNET + * Copyright (c) 2023-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,18 +36,23 @@ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" +#include // for printf #include #include +#include "config.h" // for HAVE_* #include "debug.h" #include "lib_common.h" #include "utils/color_out.h" #include "utils/text.h" #include "video_capture.h" +#include "video_capture_params.h" // for vidcap_params_free_struct, vidcap_... + +struct audio_frame; +struct device_info; +struct vidcap_params; + static void vidcap_screen_linux_probe(struct device_info **cards, int *count, void (**deleter)(void *)) { diff --git a/src/video_capture/screen_osx.c b/src/video_capture/screen_osx.c index da44e9305..a6f50e545 100644 --- a/src/video_capture/screen_osx.c +++ b/src/video_capture/screen_osx.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2012-2023 CESNET, z.s.p.o. + * Copyright (c) 2012-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,30 +35,29 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "debug.h" -#include "host.h" #include "lib_common.h" +#include "pixfmt_conv.h" +#include "tv.h" +#include "types.h" #include "utils/video_frame_pool.h" -#include "video.h" #include "video_capture.h" - -#include "tv.h" - -#include "audio/types.h" - -#include -#include -#include - -#include - -#include +#include "video_capture_params.h" +#include "video_codec.h" +#include "video_frame.h" +struct audio_frame; +struct vidcap_params; #define MAX_DISPLAY_COUNT 10 #define MOD_NAME "[screen cap mac] " diff --git a/src/video_capture/v4l2.c b/src/video_capture/v4l2.c index f5db365f2..89cc944d6 100644 --- a/src/video_capture/v4l2.c +++ b/src/video_capture/v4l2.c @@ -5,7 +5,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2012-2024 CESNET, z. s. p. o. + * Copyright (c) 2012-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,7 +38,7 @@ */ #ifdef HAVE_CONFIG_H -#include "config.h" +#include "config.h" // for HAVE_LIBV4LCONVERT #endif /* HAVE_CONFIG_H */ #ifdef HAVE_LIBV4LCONVERT @@ -55,11 +55,13 @@ #include #include #include +#include // for bool, false, true #include #include #include #include #include +#include // for gettimeofday, timeval #include enum { @@ -69,16 +71,21 @@ enum { #define MOD_NAME "[V4L cap.] " #include "debug.h" -#include "host.h" #include "lib_common.h" #include "tv.h" +#include "types.h" // for device_info, video_desc, tile, vid... #include "utils/color_out.h" #include "utils/list.h" #include "utils/macros.h" #include "utils/misc.h" // ug_strerror #include "v4l2_common.h" -#include "video.h" #include "video_capture.h" +#include "video_capture_params.h" // for vidcap_params_get_fmt, vidcap_para... +#include "video_codec.h" // for codec_is_planar, get_codec_name +#include "video_frame.h" // for get_interlacing_suffix, vf_alloc_desc + +struct audio_frame; +struct vidcap_params; /* prototypes of functions defined in this module */ static void print_fps(int fd, struct v4l2_frmivalenum *param); diff --git a/src/video_capture/ximea.c b/src/video_capture/ximea.c index 378bca076..063f80c61 100644 --- a/src/video_capture/ximea.c +++ b/src/video_capture/ximea.c @@ -36,7 +36,7 @@ */ #ifdef HAVE_CONFIG_H -#include "config.h" +#include "config.h" // for XIMEA_RUNTIME_LINKING #endif // HAVE_CONFIG_H #include // for assert From d75951dd411244db893ac46c339e03f83e1f1be9 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 25 Aug 2025 14:02:36 +0200 Subject: [PATCH 057/100] IWYU some more files --- src/audio/codec/dummy_pcm.c | 20 +++++++++----------- src/utils/packet_counter.cpp | 14 +++++--------- src/utils/vf_split.cpp | 21 ++++++++++----------- src/utils/wait_obj.cpp | 6 +----- src/video_frame.c | 7 ++++--- 5 files changed, 29 insertions(+), 39 deletions(-) diff --git a/src/audio/codec/dummy_pcm.c b/src/audio/codec/dummy_pcm.c index 2000e3226..279fcfb3a 100644 --- a/src/audio/codec/dummy_pcm.c +++ b/src/audio/codec/dummy_pcm.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2013-2015 CESNET, z. s. p. o. + * Copyright (c) 2013-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,17 +35,15 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ +#include // for assert +#include // for bool +#include // for uint32_t +#include // for free, malloc, NULL -#include "audio/audio.h" -#include "audio/codec.h" - -#include "debug.h" -#include "lib_common.h" +#include "audio/codec.h" // for audio_codec_direction_t, AUDIO_COMPRESS_ABI... +#include "audio/types.h" // for audio_channel, AC_PCM, AC_NONE, audio_codec_t +#include "debug.h" // for UNUSED +#include "lib_common.h" // for REGISTER_MODULE, library_class #define MAGIC 0x552bca11 diff --git a/src/utils/packet_counter.cpp b/src/utils/packet_counter.cpp index e47e83e7c..584051b2b 100644 --- a/src/utils/packet_counter.cpp +++ b/src/utils/packet_counter.cpp @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2012-2023 CESNET, z. s. p. o. + * Copyright (c) 2012-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,15 +35,11 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif // defined HAVE_CONFIG_H - #include "utils/packet_counter.h" -#include -#include + +#include // for map, _Rb_tree_iterator +#include // for pair +#include // for vector using std::map; using std::vector; diff --git a/src/utils/vf_split.cpp b/src/utils/vf_split.cpp index d85729b90..7bf080907 100644 --- a/src/utils/vf_split.cpp +++ b/src/utils/vf_split.cpp @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2011-2021 CESNET z.s.p.o. + * Copyright (c) 2011-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,18 +35,17 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "utils/vf_split.h" -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif // HAVE_CONFIG_H +#include // for assert +#include // for int64_t, INT64_MAX +#include // for malloc +#include // for memcpy +#include // for decay +#include "types.h" // for tile, video_frame, video_desc +#include "video_codec.h" // for get_bpp, vc_get_linesize +#include "video_frame.h" // for vf_get_tile, vf_alloc_desc, vf_copy_metadata -#include -#include -#include "utils/vf_split.h" -#include "video.h" -#include "video_codec.h" void vf_split(struct video_frame *out, struct video_frame *src, unsigned int x_count, unsigned int y_count, int preallocate) diff --git a/src/utils/wait_obj.cpp b/src/utils/wait_obj.cpp index f2b309513..f9802a753 100644 --- a/src/utils/wait_obj.cpp +++ b/src/utils/wait_obj.cpp @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2013 CESNET z.s.p.o. + * Copyright (c) 2013-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,10 +35,6 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" - #include "utils/wait_obj.h" struct wait_obj *wait_obj_init() diff --git a/src/video_frame.c b/src/video_frame.c index 6e1ea029c..a3a56e69e 100644 --- a/src/video_frame.c +++ b/src/video_frame.c @@ -54,14 +54,15 @@ #include #include #include +#include // for uint16_t #include #include #ifdef __linux__ #include #endif -#include "config_unix.h" -#include "config_win32.h" +#include "compat/aligned_malloc.h" +#include "compat/endian.h" // for htobe16 #include "debug.h" #include "pixfmt_conv.h" #include "utils/pam.h" @@ -447,7 +448,7 @@ static unsigned char *get_16_bit_pnm_data(struct video_frame *frame) { for (unsigned i = 0; i < frame->tiles[0].width * 3; ++i) { uint16_t tmp = *dstline; tmp >>= 16U - depth; - *dstline++ = htons(tmp); + *dstline++ = htobe16(tmp); } } return tmp; From 4259998a9c5a1be632fddafe37f08fc56fe7424d Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 25 Aug 2025 14:05:48 +0200 Subject: [PATCH 058/100] vcap/screen_win: IWYU --- src/video_capture/screen_win.c | 46 ++++++++++++++++------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/video_capture/screen_win.c b/src/video_capture/screen_win.c index 3bff6d09b..3a0f2ab30 100644 --- a/src/video_capture/screen_win.c +++ b/src/video_capture/screen_win.c @@ -9,7 +9,7 @@ * - load the dll even if working directory is not the dir with the DLL */ /* - * Copyright (c) 2019-2023 CESNET, z.s.p.o. + * Copyright (c) 2019-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,28 +41,26 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -#include - -#include "audio/types.h" -#include "debug.h" -#include "host.h" -#include "lib_common.h" -#include "utils/color_out.h" -#include "utils/macros.h" -#include "utils/text.h" -#include "utils/windows.h" -#include "video.h" -#include "video_capture.h" -#include "video_capture_params.h" +#define WIN32_LEAN_AND_MEAN +#include // for INT_PTR + +#include // for assert +#include // for ShellExecuteA, ShellExecute +#include // for bool, false, true +#include // for NULL, snprintf +#include // for free, calloc, strtol +#include // for strchr, strcmp, strlen, strstr + +#include "debug.h" // for log_msg, LOG_LEVEL_ERROR, MSG, LOG... +#include "host.h" // for uv_argv, uv_argc +#include "lib_common.h" // for REGISTER_MODULE, library_class +#include "types.h" // for device_info +#include "utils/color_out.h" // for color_printf, TBOLD, TRED +#include "utils/macros.h" // for IF_NOT_NULL_ELSE, IS_KEY_PREFIX +#include "utils/text.h" // for wrap_paragraph +#include "utils/windows.h" // for get_win32_error, hresult_to_str +#include "video_capture.h" // for VIDCAP_INIT_FAIL, video_capture_info +#include "video_capture_params.h" // for vidcap_params_allocate, vidcap_par... #define MOD_NAME "[screen win] " #define FILTER_UPSTREAM_URL "https://github.com/rdp/screen-capture-recorder-to-video-windows-free/releases" @@ -387,7 +385,7 @@ static int register_screen_cap_rec_library(bool is_elevated) { if ((INT_PTR) ret > 32) { log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Module installation successful.\n"); log_msg(LOG_LEVEL_NOTICE, MOD_NAME "If you want to unregister the module, run 'uv -t screen:unregister'.\n"); - sleep(2); + Sleep(2000); // 2 s return 1; } } From 755ee5158d99fbd33be52702b597ea6831be6c62 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 25 Aug 2025 14:28:56 +0200 Subject: [PATCH 059/100] IWYU some more files --- src/audio/audio_playback.c | 33 ++++++++++++++------------------- src/audio/audio_playback.h | 7 +++++-- src/capture_filter.cpp | 26 +++++++++++++------------- src/video_decompress.cpp | 25 ++++++++++++------------- src/video_decompress.h | 6 ++++++ 5 files changed, 50 insertions(+), 47 deletions(-) diff --git a/src/audio/audio_playback.c b/src/audio/audio_playback.c index 507162720..a598dbfca 100644 --- a/src/audio/audio_playback.c +++ b/src/audio/audio_playback.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2015-2021 CESNET, z. s. p. o. + * Copyright (c) 2015-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,25 +35,20 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif - -#include -#include -#include - #include "audio/audio_playback.h" -#include "audio/playback/sdi.h" -#include "audio/types.h" -#include "debug.h" -#include "host.h" -#include "lib_common.h" -#include "tv.h" -#include "utils/misc.h" // for fmt_number_with_delim -#include "video_display.h" /* flags */ + +#include // for printf +#include // for free, calloc +#include // for strncpy +#include // for strcasecmp + +#include "audio/types.h" // for audio_frame, AC_PCM, audio_desc +#include "debug.h" // for log_msg, LOG_LEVEL_ERROR, LOG_LEVEL_INFO +#include "host.h" // for INIT_NOERR +#include "lib_common.h" // for library_class, list_modules, load_library +#include "tv.h" // for tv_diff +#include "utils/misc.h" // for fmt_number_with_delim +#include "video_display.h" // for DISPLAY_FLAG_AUDIO_AESEBU, DISPLAY_FLAG_A... struct state_audio_playback { char name[128]; diff --git a/src/audio/audio_playback.h b/src/audio/audio_playback.h index ee5ba05ba..06048d62f 100644 --- a/src/audio/audio_playback.h +++ b/src/audio/audio_playback.h @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2012-2023 CESNET, z. s. p. o. + * Copyright (c) 2012-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,8 +38,11 @@ #ifndef AUDIO_AUDIO_PLAYBACK_H_316AA23B_3EFF_4150_83D2_24A2295CB74A #define AUDIO_AUDIO_PLAYBACK_H_316AA23B_3EFF_4150_83D2_24A2295CB74A -#ifndef __cplusplus +#ifdef __cplusplus +#include // for size_t +#else #include +#include // for size_t #endif // ! defined __cplusplus #include "../types.h" diff --git a/src/capture_filter.cpp b/src/capture_filter.cpp index adcc29e06..c8f599ec3 100644 --- a/src/capture_filter.cpp +++ b/src/capture_filter.cpp @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2014-2021 CESNET, z. s. p. o. + * Copyright (c) 2014-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,19 +35,19 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif /* HAVE_CONFIG_H */ - #include "capture_filter.h" -#include "debug.h" -#include "lib_common.h" -#include "module.h" -#include "utils/color_out.h" -#include "utils/list.h" -#include "video.h" + +#include // for assert +#include // for printf, fprintf, stderr +#include // for free, NULL, atoi, calloc, malloc +#include // for strchr, strcmp, strdup, strlen, strncmp + +#include "compat/strings.h" // for strcasecmp +#include "lib_common.h" // for get_libraries_for_class, library_class +#include "messaging.h" // for msg_universal, new_response, RESPONSE_I... +#include "module.h" // for module, module_done, module_init_default +#include "utils/color_out.h" // for color_printf, TERM_BOLD, TERM_RESET +#include "utils/list.h" // for simple_linked_list_pop, simple_linked_l... using namespace std; diff --git a/src/video_decompress.cpp b/src/video_decompress.cpp index 879e81a04..408ae87f7 100644 --- a/src/video_decompress.cpp +++ b/src/video_decompress.cpp @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2011-2019 CESNET, z. s. p. o. + * Copyright (c) 2011-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,19 +35,18 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#include "config_unix.h" -#include "config_win32.h" -#endif // HAVE_CONFIG_H - -#include -#include -#include -#include "debug.h" -#include "video_codec.h" #include "video_decompress.h" -#include "lib_common.h" + +#include // for assert +#include // for uint32_t +#include // for free, calloc +#include // for strchr, strdup +#include // for basic_string, char_traits, hash, operator<< + +#include "debug.h" // for LOG, LOG_LEVEL_VERBOSE +#include "host.h" // for commandline_params, ADD_TO_PARAM +#include "lib_common.h" // for get_libraries_for_class, library_class +#include "video_codec.h" // for get_codec_from_name #define DECOMPRESS_MAGIC 0xdff34f21u diff --git a/src/video_decompress.h b/src/video_decompress.h index 946a5df00..344859a5d 100644 --- a/src/video_decompress.h +++ b/src/video_decompress.h @@ -48,6 +48,12 @@ #ifndef __video_decompress_h #define __video_decompress_h +#ifdef __cplusplus +#include // for size_t +#else +#include // for size_t +#endif + #include "types.h" #ifdef __cplusplus From 9a738c906cdda2c9347b103594b930b8cd75b9bb Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 08:18:10 +0200 Subject: [PATCH 060/100] add acap/fluidsynth To replace sdl3_mixer that does no longer support MIDI playback, thus unusable for our use case. - song1 needs to be static included potentially from 2 compilation units - this and sdl_mixer --- configure.ac | 23 +++ src/audio/capture/fluidsynth.c | 351 +++++++++++++++++++++++++++++++++ src/audio/capture/song1.h | 2 +- src/audio/utils.cpp | 11 +- 4 files changed, 385 insertions(+), 2 deletions(-) create mode 100644 src/audio/capture/fluidsynth.c diff --git a/configure.ac b/configure.ac index fd419f3fa..ee03c67c9 100644 --- a/configure.ac +++ b/configure.ac @@ -3444,6 +3444,28 @@ fi ENSURE_FEATURE_PRESENT([$sdl_mixer_req], [$sdl_mixer], [SDL_mixer deps not found!]) +# --------------------------------------------------------------------- +# fluidsynt audio capture synthesizer +# --------------------------------------------------------------------- +AC_ARG_ENABLE(fluidsynth, + AS_HELP_STRING([--disable-fluidsynth], +[disable fluidtynth audio capture (default is auto)]), + [fluidsynth_req=$enableval], + [fluidsynth_req=$build_default]) +fluidsynth=no + +if test "${fluidsynth_req?}" != no; then + PKG_CHECK_MODULES([FLUIDSYNTH], [fluidsynth], [found_fluidsynth=yes], + [found_fluidsynth=no]) + if test "${found_fluidsynth?}" = yes; then + add_module acap_fluidsynth src/audio/capture/fluidsynth.o "$FLUIDSYNTH_LIBS" + INC="$INC${FLUIDSYNTH_CFLAGS:+ $FLUIDSYNTH_CFLAGS}" + fluidsynth=yes + fi +fi + +ENSURE_FEATURE_PRESENT([$fluidsynth_req], [$fluidsynth], [fluidsynth not found!]) + # ----------------------------------------------------------------------------- # Reflector # ----------------------------------------------------------------------------- @@ -3620,6 +3642,7 @@ RESULT=`end_section "$RESULT"` RESULT=`start_section "$RESULT" "Audio"` RESULT=`add_column "$RESULT" "ALSA" $alsa $?` RESULT=`add_column "$RESULT" "CoreAudio" $coreaudio $?` +RESULT=`add_column "$RESULT" "FluidSynth" $fluidsynth $?` RESULT=`add_column "$RESULT" "JACK" $jack $?` RESULT=`add_column "$RESULT" "JACK transport" $jack_trans $?` RESULT=`add_column "$RESULT" "SDL_mixer" $sdl_mixer $?` diff --git a/src/audio/capture/fluidsynth.c b/src/audio/capture/fluidsynth.c new file mode 100644 index 000000000..b0378e78d --- /dev/null +++ b/src/audio/capture/fluidsynth.c @@ -0,0 +1,351 @@ +/** + * @file audio/capture/fluidsynth.c + * @author Martin Pulec + */ +/* + * Copyright (c) 2025 CESNET + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include // for assert +#include // for fluid_player_play, fluid_synth_writ... +#include // for fluid_player_t, fluid_settings_t +#include // for bool +#include // for NULL, fclose, size_t, FILE, fopen +#include // for free, getenv, malloc, calloc +#include // for strdup, strlen, strncat, strcmp +#include // for unlink + +#include "audio/audio_capture.h" // for AUDIO_CAPTURE_ABI_VERSION, audio_ca... +#include "audio/types.h" // for audio_frame +#include "audio/utils.h" // for mux_channel +#include "debug.h" // for LOG_LEVEL_ERROR, MSG, log_msg, LOG_... +#include "host.h" // for audio_capture_sample_rate, INIT_NOERR +#include "lib_common.h" // for REGISTER_MODULE, library_class +#include "song1.h" // for song1 +#include "tv.h" // for get_time_in_ns, time_ns_t, NS_IN_SE... +#include "types.h" // for device_info +#include "utils/color_out.h" // for color_printf, TBOLD, TRED +#include "utils/fs.h" // for get_install_root, get_temp_file +#include "utils/macros.h" // for IS_KEY_PREFIX + +struct module; + +enum { + FLUIDSYNTH_BPS = 2, + DEFAULT_FLUIDSYNTH_SAMPLE_RATE = 48000, + CHUNK_SIZE = 480, +}; + +#define MOD_NAME "[fluidsynth] " + +struct state_fluidsynth_capture { + struct audio_frame audio; + unsigned char *left; + unsigned char *right; + + char *req_filename; + const char *tmp_filename; + + time_ns_t next_frame_time; + time_ns_t frame_interval; + ; + + fluid_settings_t *settings; + fluid_synth_t *synth; + fluid_player_t *player; +}; + +static void audio_cap_fluidsynth_done(void *state); + +static void +audio_cap_fluidsynth_probe(struct device_info **available_devices, int *count, + void (**deleter)(void *)) +{ + *deleter = free; + *count = 1; + *available_devices = calloc(1, sizeof **available_devices); + strncat((*available_devices)[0].dev, "fluidsynth", + sizeof(*available_devices)[0].dev - 1); + strncat((*available_devices)[0].name, "Sample midi song", + sizeof(*available_devices)[0].name - 1); +} + +static void +usage() +{ + color_printf( + TBOLD("fluidsynth") " is a capture device capable playing MIDI.\n\n" + "The main functional difference to " TBOLD( + "file") " video capture (that is able to " + "play audio\n" + "files as well) is the support " + "for " TBOLD( + "MIDI") " (and also having one " + "song bundled).\n\n"); + color_printf("Usage:\n"); + color_printf(TBOLD(TRED("\t-s fluidsynth") "[:file=]") "\n"); + color_printf("where\n"); + color_printf(TBOLD("\t") " - name of file to be used\n"); + color_printf("\n"); + color_printf(TBOLD( + "FLUIDSYNTH_SF") " - environment variable with path to " + "sound fonts for MIDI playback (eg. freepats)\n"); + color_printf( + TBOLD("ULTRAGRID_BUNDLED_SF") " - set this environment variable to " + "1 to skip loading system default " + "sound font\n\n"); +} + +static int +parse_opts(struct state_fluidsynth_capture *s, char *cfg) +{ + char *save_ptr = NULL; + char *item = NULL; + while ((item = strtok_r(cfg, ":", &save_ptr)) != NULL) { + cfg = NULL; + if (strcmp(item, "help") == 0) { + usage(); + return 1; + } + if (IS_KEY_PREFIX(item, "file")) { + s->req_filename = strdup(strchr(item, '=') + 1); + } else { + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong option: %s!\n", + item); + color_printf("Use " TBOLD( + "-s fluidsynth:help") " to see available " + "options.\n"); + return -1; + } + } + return 0; +} + +static const char * +load_song1() +{ + const char *filename = NULL; + FILE *f = get_temp_file(&filename); + if (f == NULL) { + perror("fopen audio"); + return NULL; + } + size_t nwritten = fwrite(song1, sizeof song1, 1, f); + fclose(f); + if (nwritten != 1) { + unlink(filename); + return NULL; + } + return filename; +} + +/** + * Try to preload a sound font. + * + * This is mainly intended to allow loading sound fonts from application bundle + * on various platforms (get_install_root is relative to executable). But use + * to system default font, if available. + */ +static char * +get_soundfont() +{ + const char *env_fs = getenv("FLUIDSYNTH_SF"); + if (env_fs != NULL) { + return strdup(env_fs); + } + const bool force_bundled_sf = + getenv("ULTRAGRID_BUNDLED_SF") != NULL && + strcmp(getenv("ULTRAGRID_BUNDLED_SF"), "1") == 0; + const char *roots[2] = { "/usr", get_install_root() }; + if (force_bundled_sf) { + roots[0] = get_install_root(); + roots[1] = "/usr"; + } + const char *sf_candidates[] = { + // without install prefix + "/share/soundfonts/default.sf2", + "/share/soundfonts/default.sf3", + "/share/sounds/sf2/default-GM.sf2", + "/share/sounds/sf2/default-GM.sf3", // Ubuntu + }; + for (size_t i = 0; i < sizeof roots / sizeof roots[0]; ++i) { + for (size_t j = 0; + j < sizeof sf_candidates / sizeof sf_candidates[0]; ++j) { + const char *root = roots[i]; + const size_t len = + strlen(root) + strlen(sf_candidates[j]) + 1; + char path[len]; + strncpy(path, root, len - 1); + strncat(path, sf_candidates[j], len - strlen(path) - 1); + FILE *f = fopen(path, "rb"); + debug_msg(MOD_NAME + "Trying to open sound font '%s': %s\n", + path, f ? "success, setting" : "failed"); + if (!f) { + continue; + } + fclose(f); + return strdup(path); + } + } + MSG(ERROR, "Cannot find any suitable sound font!\n"); + return NULL; +} + +static void * +audio_cap_fluidsynth_init(struct module *parent, const char *cfg) +{ + (void) parent; + struct state_fluidsynth_capture *s = calloc(1, sizeof *s); + char *ccfg = strdup(cfg); + int ret = parse_opts(s, ccfg); + free(ccfg); + if (ret != 0) { + audio_cap_fluidsynth_done(s); + return ret < 0 ? NULL : INIT_NOERR; + } + + char *sf = get_soundfont(); + if (sf == NULL) { + audio_cap_fluidsynth_done(s); + return NULL; + } + + s->audio.bps = FLUIDSYNTH_BPS; + s->audio.ch_count = audio_capture_channels < 2 ? 1 : 2; + s->audio.sample_rate = audio_capture_sample_rate > 0 + ? audio_capture_sample_rate + : DEFAULT_FLUIDSYNTH_SAMPLE_RATE; + + const char *filename = s->req_filename; + if (!filename) { + filename = s->tmp_filename = load_song1(); + if (!filename) { + goto error; + } + } + + s->settings = new_fluid_settings(); + fluid_settings_setnum(s->settings, "synth.sample-rate", + s->audio.sample_rate); + + s->synth = new_fluid_synth(s->settings); + if (fluid_synth_sfload(s->synth, sf, 1) < 0) { + MSG(ERROR, "Failed to load SF2: %s\n", sf); + goto error; + } + s->player = new_fluid_player(s->synth); + if (fluid_player_add(s->player, filename) != FLUID_OK) { + MSG(ERROR, "Failed to add MIDI: %s\n", s->req_filename); + goto error; + } + fluid_player_play(s->player); + + s->audio.max_size = s->audio.data_len = + s->audio.ch_count * s->audio.bps * CHUNK_SIZE; + s->audio.data = malloc(s->audio.data_len); + s->left = malloc(s->audio.data_len / s->audio.ch_count); + s->right = malloc(s->audio.data_len / s->audio.ch_count); + + s->frame_interval = CHUNK_SIZE * NS_IN_SEC_DBL / s->audio.sample_rate; + s->next_frame_time = get_time_in_ns() + s->frame_interval; + + log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Initialized fluidsynth\n"); + + free(sf); + return s; +error: + audio_cap_fluidsynth_done(s); + free(sf); + return NULL; +} + +static struct audio_frame * +audio_cap_fluidsynth_read(void *state) +{ + struct state_fluidsynth_capture *s = state; + + if (fluid_player_get_status(s->player) == FLUID_PLAYER_DONE) { + MSG(VERBOSE, "Rewinding...\n"); + fluid_player_play(s->player); + } + + if (s->audio.ch_count == 1) { + fluid_synth_write_s16(s->synth, CHUNK_SIZE, s->audio.data, 0, 1, + s->right, 0, 1); + } else { + assert(s->audio.ch_count == 2); + fluid_synth_write_s16(s->synth, CHUNK_SIZE, s->left, 0, 1, + s->right, 0, 1); + mux_channel(s->audio.data, (char *) s->left, s->audio.bps, + s->audio.bps * CHUNK_SIZE, s->audio.ch_count, 0, + 1.); + mux_channel(s->audio.data, (char *) s->right, s->audio.bps, + s->audio.bps * CHUNK_SIZE, s->audio.ch_count, 1, + 1.); + } + + time_ns_t t = get_time_in_ns(); + if (t > s->next_frame_time + s->frame_interval) { + MSG(WARNING, "Some data missed!\n"); + t = s->next_frame_time; + } else { + while (t < s->next_frame_time) { + t = get_time_in_ns(); + } + } + s->next_frame_time += s->frame_interval; + + return &s->audio; +} + +static void +audio_cap_fluidsynth_done(void *state) +{ + struct state_fluidsynth_capture *s = state; + free(s->audio.data); + free(s->req_filename); + free(s->left); + free(s->right); + if (s->tmp_filename) { + unlink(s->tmp_filename); + } + free(s); +} + +static const struct audio_capture_info acap_fluidsynth_info = { + audio_cap_fluidsynth_probe, audio_cap_fluidsynth_init, + audio_cap_fluidsynth_read, audio_cap_fluidsynth_done +}; + +REGISTER_MODULE(fluidsynth, &acap_fluidsynth_info, LIBRARY_CLASS_AUDIO_CAPTURE, + AUDIO_CAPTURE_ABI_VERSION); diff --git a/src/audio/capture/song1.h b/src/audio/capture/song1.h index 862c44764..e28cd72a8 100644 --- a/src/audio/capture/song1.h +++ b/src/audio/capture/song1.h @@ -1,5 +1,5 @@ /* THIS CHUNK OF BYTES IS AUTOMATICALLY GENERATED */ -unsigned char song1[] = +static const unsigned char song1[] = { 0x4d, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x60, 0x4d, 0x54, 0x72, 0x6b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0xff, 0x7f, 0x03, 0x00, 0x00, 0x41, 0x00, 0xff, 0x58, diff --git a/src/audio/utils.cpp b/src/audio/utils.cpp index e52745585..0e388d93e 100644 --- a/src/audio/utils.cpp +++ b/src/audio/utils.cpp @@ -4,7 +4,7 @@ * @author Martin Piatka */ /* - * Copyright (c) 2011-2024 CESNET + * Copyright (c) 2011-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -368,6 +368,15 @@ void remux_channel(char *out, const char *in, int bps, int in_len, int in_stream } } +/** + * @param out output buffer (base) pointner + * @param in input channel pointer + * @param bps bytes per second + * @param in_len in len in bytes + * @param out_stream_channels number of channels that will be muxed + * @param pos_in_stream position of muxed channels in output (0-indexed) + * @param scale scale the input channel (1.0 to keep original volume) + */ void mux_channel(char *out, const char *in, int bps, int in_len, int out_stream_channels, int pos_in_stream, double scale) { int samples = in_len / bps; From d01052833561e862b502e204ee0f296911e76452 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 08:40:44 +0200 Subject: [PATCH 061/100] acap/fluidsynth: use usleep + remove todo in sdl3 + add deprecate to sdl_mixer --- src/audio/capture/fluidsynth.c | 7 +++---- src/audio/capture/sdl_mixer.c | 1 + src/video_display/sdl3.c | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/audio/capture/fluidsynth.c b/src/audio/capture/fluidsynth.c index b0378e78d..59f11ea13 100644 --- a/src/audio/capture/fluidsynth.c +++ b/src/audio/capture/fluidsynth.c @@ -47,6 +47,7 @@ #include "audio/audio_capture.h" // for AUDIO_CAPTURE_ABI_VERSION, audio_ca... #include "audio/types.h" // for audio_frame #include "audio/utils.h" // for mux_channel +#include "compat/usleep.h" // for usleep #include "debug.h" // for LOG_LEVEL_ERROR, MSG, log_msg, LOG_... #include "host.h" // for audio_capture_sample_rate, INIT_NOERR #include "lib_common.h" // for REGISTER_MODULE, library_class @@ -318,10 +319,8 @@ audio_cap_fluidsynth_read(void *state) if (t > s->next_frame_time + s->frame_interval) { MSG(WARNING, "Some data missed!\n"); t = s->next_frame_time; - } else { - while (t < s->next_frame_time) { - t = get_time_in_ns(); - } + } else if (t < s->next_frame_time){ + usleep((s->next_frame_time - t) / US_IN_NS); } s->next_frame_time += s->frame_interval; diff --git a/src/audio/capture/sdl_mixer.c b/src/audio/capture/sdl_mixer.c index 7360ffd95..3f73ce0d5 100644 --- a/src/audio/capture/sdl_mixer.c +++ b/src/audio/capture/sdl_mixer.c @@ -226,6 +226,7 @@ adjust_ch_count(struct state_sdl_mixer_capture *s) static void * audio_cap_sdl_mixer_init(struct module *parent, const char *cfg) { + MSG(WARNING, "SDL_mixer is deprecated, used fluidsynth...\n"); UNUSED(parent); SDL_Init(SDL_INIT_AUDIO); diff --git a/src/video_display/sdl3.c b/src/video_display/sdl3.c index 8ac00a3f2..a52e4fa74 100644 --- a/src/video_display/sdl3.c +++ b/src/video_display/sdl3.c @@ -49,7 +49,6 @@ * (segfaults - wrong pitch/texture?) * 3. p010 works just on macOS/Metal, crashes on Vulkan (see previous point) * 4. p010 corrupted on d3d[12] - pixfmts skipped in query*() as a workaround - * 5. see todo in @ref ../audio/capture/sdl_mixer.c */ #include From 3a60095d587f34188088eacd3014e24182f7c9b4 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 08:56:01 +0200 Subject: [PATCH 062/100] CI: enforce fluidsynth, do not require sdl_mixer --- .github/scripts/Linux/arm/bootstrap.sh | 3 ++- .github/scripts/Linux/prepare.sh | 3 ++- .github/scripts/Windows/prepare_msys.sh | 5 ++++- .github/scripts/environment.sh | 2 +- .github/scripts/macOS/prepare.sh | 2 +- .github/workflows/coverity-scan.yml | 2 +- 6 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/scripts/Linux/arm/bootstrap.sh b/.github/scripts/Linux/arm/bootstrap.sh index 7518ea669..699825534 100755 --- a/.github/scripts/Linux/arm/bootstrap.sh +++ b/.github/scripts/Linux/arm/bootstrap.sh @@ -31,7 +31,8 @@ apt -y install libavcodec-dev libavformat-dev libswscale-dev libraspberrypi-dev apt -y install \ cmake \ libdrm-dev\ - libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev\ + libfluidsynth-devl\ + libsdl2-dev libsdl2-ttf-dev\ libva-dev\ libvulkan-dev\ diff --git a/.github/scripts/Linux/prepare.sh b/.github/scripts/Linux/prepare.sh index 872278ada..56e10b737 100755 --- a/.github/scripts/Linux/prepare.sh +++ b/.github/scripts/Linux/prepare.sh @@ -20,10 +20,11 @@ sudo apt install appstream `# appstreamcli for mkappimage AppStream validation` asciidoc sudo apt install fonts-dejavu-core sudo apt --no-install-recommends install nvidia-cuda-toolkit +sudo apt install libfluidsynth-dev sudo apt install libglew-dev libglfw3-dev sudo apt install libglm-dev sudo apt install imagemagick libmagickwand-dev -sudo apt install libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev +sudo apt install libsdl2-dev libsdl2-ttf-dev sudo apt install libsoxr-dev libspeexdsp-dev sudo apt install libssl-dev sudo apt install libasound-dev libcaca-dev libjack-jackd2-dev libnatpmp-dev libv4l-dev portaudio19-dev diff --git a/.github/scripts/Windows/prepare_msys.sh b/.github/scripts/Windows/prepare_msys.sh index a2b39dbcc..bc3264f6f 100644 --- a/.github/scripts/Windows/prepare_msys.sh +++ b/.github/scripts/Windows/prepare_msys.sh @@ -50,12 +50,15 @@ $PACMAN_INSTALL automake autoconf git make pkgconf \ $m-gcc-compat \ unzip zip $PACMAN_INSTALL $m-asciidoc \ + $m-libcaca\ $m-ffmpeg \ + $m-fluidsynth\ + $m-glew $m-glfw\ $m-libnatpmp \ $m-vulkan-headers $m-vulkan-loader \ $PACMAN_INSTALL $m-libsoxr $m-speexdsp -$PACMAN_INSTALL $m-glew $m-libcaca $m-SDL2 $m-SDL2_mixer $m-SDL2_ttf $m-glfw +$PACMAN_INSTALL $m-SDL2 $m-SDL2_ttf $PACMAN_INSTALL $m-glm $PACMAN_INSTALL $m-portaudio # in case of problems build PA with --with-winapi=wmme,directx,wasapi $PACMAN_INSTALL $m-curl # RTSP capture diff --git a/.github/scripts/environment.sh b/.github/scripts/environment.sh index 8141388af..4a0e466fc 100644 --- a/.github/scripts/environment.sh +++ b/.github/scripts/environment.sh @@ -45,6 +45,7 @@ export FEATURES="\ --enable-caca\ --enable-decklink\ --enable-file\ + --enable-fluidsynth\ --enable-gl\ --enable-gl-display\ --enable-holepunch\ @@ -64,7 +65,6 @@ export FEATURES="\ --enable-scale\ --enable-screen\ --enable-sdl=2\ - --enable-sdl_mixer\ --enable-sdp-http\ --enable-soxr\ --enable-speexdsp\ diff --git a/.github/scripts/macOS/prepare.sh b/.github/scripts/macOS/prepare.sh index efdb674a1..62bdf1d16 100755 --- a/.github/scripts/macOS/prepare.sh +++ b/.github/scripts/macOS/prepare.sh @@ -36,6 +36,7 @@ set -- \ autoconf \ automake \ ffmpeg \ + fluidsynth \ glm \ imagemagick \ jack \ @@ -50,7 +51,6 @@ set -- \ portaudio \ qt \ sdl2 \ - sdl2_mixer \ sdl2_ttf \ speexdsp \ vulkan-headers \ diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml index faff1aa75..59e74bdeb 100644 --- a/.github/workflows/coverity-scan.yml +++ b/.github/workflows/coverity-scan.yml @@ -67,7 +67,7 @@ jobs: run: | . .github/scripts/environment.sh .github/scripts/Linux/prepare.sh - sudo apt install libavcodec-dev libavformat-dev libswscale-dev libsdl2-mixer-dev libsdl2-ttf-dev + sudo apt install libavcodec-dev libavformat-dev libswscale-dev libfluidsynth-dev libsdl2-ttf-dev - name: configure if: ${{ env.coverity_token }} From e9d6d6bd2f762f201dd13e892652f5dbda99cf82 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 09:03:17 +0200 Subject: [PATCH 063/100] acap/fluidsynth: refuse unsupported ch_count/bps --- src/audio/capture/fluidsynth.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/audio/capture/fluidsynth.c b/src/audio/capture/fluidsynth.c index 59f11ea13..a4609f0d3 100644 --- a/src/audio/capture/fluidsynth.c +++ b/src/audio/capture/fluidsynth.c @@ -241,6 +241,16 @@ audio_cap_fluidsynth_init(struct module *parent, const char *cfg) return NULL; } + /// @todo add other if some-one needs that... + if (audio_capture_bps != 0 && audio_capture_bps != FLUIDSYNTH_BPS) { + MSG(ERROR, "Only %d bits-per-second supported so far...\n", FLUIDSYNTH_BPS); + goto error; + } + if (audio_capture_channels > 2) { + MSG(ERROR, "Only 1 or 2 channels currently supported...\n"); + goto error; + } + s->audio.bps = FLUIDSYNTH_BPS; s->audio.ch_count = audio_capture_channels < 2 ? 1 : 2; s->audio.sample_rate = audio_capture_sample_rate > 0 @@ -303,7 +313,7 @@ audio_cap_fluidsynth_read(void *state) if (s->audio.ch_count == 1) { fluid_synth_write_s16(s->synth, CHUNK_SIZE, s->audio.data, 0, 1, s->right, 0, 1); - } else { + } else { // drop right channel, keep the left assert(s->audio.ch_count == 2); fluid_synth_write_s16(s->synth, CHUNK_SIZE, s->left, 0, 1, s->right, 0, 1); From aa6be6b1a470bebc5597be95f3893d891c0ab25e Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 09:30:35 +0200 Subject: [PATCH 064/100] create-appimage.sh: ship soundfont also for fluidsynth not only sdl_mixer --- data/scripts/Linux-AppImage/create-appimage.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/scripts/Linux-AppImage/create-appimage.sh b/data/scripts/Linux-AppImage/create-appimage.sh index f17e69831..6b2ea3444 100755 --- a/data/scripts/Linux-AppImage/create-appimage.sh +++ b/data/scripts/Linux-AppImage/create-appimage.sh @@ -92,7 +92,8 @@ add_fonts() { # for GUI+testcard2 cp "$FONT_PATH" $APPPREFIX/share/fonts done done - if ls $APPPREFIX/lib/*mixer* >/dev/null 2>&1; then + if ls $APPPREFIX/lib/*mixer* >/dev/null 2>&1 || + ls $APPPREFIX/lib/ultragrid/*fluidsynth* >/dev/null 2>&1; then mkdir -p $APPPREFIX/share/soundfonts cp "$srcdir/data/default.sf3" $APPPREFIX/share/soundfonts/ fi From 02cefe8fd070e7211ada8ad0beea7c1c0d8c0fd3 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 12:12:17 +0200 Subject: [PATCH 065/100] vo_cf/flip: remove swscale added errorneously by IWYU --- src/capture_filter/flip.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/capture_filter/flip.c b/src/capture_filter/flip.c index f6781e7d6..af4aa6b2f 100644 --- a/src/capture_filter/flip.c +++ b/src/capture_filter/flip.c @@ -36,7 +36,6 @@ */ #include // for assert -#include // for sws_freeContext, sws_getContext, sws... #include // for bool, false, true #include // for uint8_t #include // for printf, fprintf, NULL, stderr, size_t From 6503b92f1541436fade74e8947ff70ad0632daf3 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 10:04:17 +0200 Subject: [PATCH 066/100] configure: do not prefer SDL2 over SDL3 Unpin SDL version 2 - SDL 3 can be now considered idempotent. Issues mentioned in sdl3: - sdl_mixer -> replaced with fluidsynth - Vulkan issues - Vulkan is currently (SDL 3.20) not implicit in either of platforms; if so, trigger a warning. Also it is unavailable in SDL2. - p010 corruption in Direct3D 11 or 12 - there is already a workaround to disable --- configure.ac | 10 +--------- src/video_display/sdl3.c | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/configure.ac b/configure.ac index ee03c67c9..c84fa7a83 100644 --- a/configure.ac +++ b/configure.ac @@ -1321,12 +1321,7 @@ if test "$sdl_req" != no; then sdl2_is_compat=yes fi if test "$sdl_req" = yes || test "$sdl_req" = auto; then - # temporarily prefer 2 over 3 - sdl_req_ver=231 - # but only if sdl2 is not sdl2_compat - if test "$sdl2_is_compat"; then - sdl_req_ver=321 - fi + sdl_req_ver=321 fi while test -n "$sdl_req_ver"; do @@ -1342,9 +1337,6 @@ if test "$sdl_req" != no; then sdl_req_ver=$(printf "$sdl_req_ver" | cut -c2-) done - if test "$sdl_version" = 3 && test ! "$sdl2_is_compat"; then - UG_MSG_WARN([Using SDL 3, which is experimental. SDL 2 can be enforced with --enable-sdl=2]) - fi if test "$sdl_version" = 2 && test "$sdl2_is_compat" ; then UG_MSG_WARN([Using SDL3 sdl2_compat, which is not recommended (use 3 directly)]) fi diff --git a/src/video_display/sdl3.c b/src/video_display/sdl3.c index a52e4fa74..ce129ecff 100644 --- a/src/video_display/sdl3.c +++ b/src/video_display/sdl3.c @@ -45,7 +45,7 @@ * @todo errata (SDL3 vs SDL2) * 1. [macOS] Vulkan renderer doesn't work (no matter if linked with MoltenVK or * loader) - * 2. [all platforms] with `renderer=vulkan` - none YCbCr texture works + * 2. [all platforms] with `renderer=vulkan` - none of YCbCr textures work * (segfaults - wrong pitch/texture?) * 3. p010 works just on macOS/Metal, crashes on Vulkan (see previous point) * 4. p010 corrupted on d3d[12] - pixfmts skipped in query*() as a workaround From 6eb185bda8a8ef612cded3c2023547ff35121419 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 10:53:57 +0200 Subject: [PATCH 067/100] CI Linux: build SDL3 based on 9eb376ee388df0ab3dc97a3c0c6bf8d95cd1a590 --- .github/scripts/Linux/download_build_sdl.sh | 19 +++++++++++++++++++ .github/scripts/Linux/install_sdl.sh | 7 +++++++ .github/scripts/Linux/prepare.sh | 8 ++++++-- .github/workflows/ccpp.yml | 12 ++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100755 .github/scripts/Linux/download_build_sdl.sh create mode 100755 .github/scripts/Linux/install_sdl.sh diff --git a/.github/scripts/Linux/download_build_sdl.sh b/.github/scripts/Linux/download_build_sdl.sh new file mode 100755 index 000000000..577a3e2bd --- /dev/null +++ b/.github/scripts/Linux/download_build_sdl.sh @@ -0,0 +1,19 @@ +#!/bin/sh -eu + +mkdir -p /var/tmp/sdl +cd /var/tmp/sdl + +git clone --depth 1 https://github.com/libsdl-org/SDL +cd SDL +cmake -S . -B build +cmake --build build -j "$(nproc)" +sudo cmake --install build +cd .. + +git clone --depth 1 https://github.com/libsdl-org/SDL_ttf +cd SDL_ttf +cmake -S . -B build +cmake --build build -j "$(nproc)" +sudo cmake --install build +cd .. + diff --git a/.github/scripts/Linux/install_sdl.sh b/.github/scripts/Linux/install_sdl.sh new file mode 100755 index 000000000..ec2bc9525 --- /dev/null +++ b/.github/scripts/Linux/install_sdl.sh @@ -0,0 +1,7 @@ +#!/bin/sh -eu + +cd /var/tmp/sdl/SDL +sudo cmake --install build +cd ../SDL_ttf +sudo cmake --install build + diff --git a/.github/scripts/Linux/prepare.sh b/.github/scripts/Linux/prepare.sh index 56e10b737..7b61c2287 100755 --- a/.github/scripts/Linux/prepare.sh +++ b/.github/scripts/Linux/prepare.sh @@ -24,7 +24,6 @@ sudo apt install libfluidsynth-dev sudo apt install libglew-dev libglfw3-dev sudo apt install libglm-dev sudo apt install imagemagick libmagickwand-dev -sudo apt install libsdl2-dev libsdl2-ttf-dev sudo apt install libsoxr-dev libspeexdsp-dev sudo apt install libssl-dev sudo apt install libasound-dev libcaca-dev libjack-jackd2-dev libnatpmp-dev libv4l-dev portaudio19-dev @@ -36,7 +35,12 @@ get_build_deps_excl() { # $2 - pattern to exclude; separate packates with '\|' ( apt-cache showsrc "$1" | sed -n '/^Build-Depends:/{s/Build-Depends://;p;q}' | tr ',' '\n' | cut -f 2 -d\ | grep -v "$2" } -ffmpeg_build_dep=$(get_build_deps_excl ffmpeg 'nonexistent-placeholder') +sudo apt build-dep libsdl2 +sdl2_ttf_build_dep=$(get_build_deps_excl libsdl2-ttf libsdl2-dev) +# shellcheck disable=SC2086 # intentional +sudo apt install $sdl2_ttf_build_dep + +ffmpeg_build_dep=$(get_build_deps_excl ffmpeg 'libsdl') # shellcheck disable=SC2086 # intentional sudo apt install $ffmpeg_build_dep libdav1d-dev libde265-dev libopenh264-dev sudo apt-get -y remove 'libavcodec*' 'libavutil*' 'libswscale*' libvpx-dev nginx diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 59a2d75a3..842e85876 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -91,6 +91,18 @@ jobs: - name: Install Cached FFmpeg if: steps.cache-ffmpeg.outputs.cache-hit == 'true' run: .github/scripts/Linux/install_ffmpeg.sh + - name: Cache SDL + id: cache-sdl + uses: actions/cache@v3 + with: + path: '/var/tmp/sdl' + key: cache-sdl-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/prepare.sh', '.github/scripts/Linux/download_build_sdl.sh' ) }} + - name: Build SDL + if: steps.cache-sdl.outputs.cache-hit != 'true' + run: .github/scripts/Linux/download_build_sdl.sh + - name: Install Cached SDL + if: steps.cache-sdl.outputs.cache-hit == 'true' + run: .github/scripts/Linux/install_sdl.sh - name: configure run: "./autogen.sh $FEATURES || { RC=$?; cat config.log; exit $RC; }" - name: make From 6e8a62bf1c7ed56cb1391d0322711192af249009 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 10:57:51 +0200 Subject: [PATCH 068/100] CI: use SDL3 --- .github/scripts/Linux/arm/build.sh | 1 + .github/scripts/Windows/prepare_msys.sh | 2 +- .github/scripts/environment.sh | 2 +- .github/scripts/macOS/prepare.sh | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/scripts/Linux/arm/build.sh b/.github/scripts/Linux/arm/build.sh index 6371095d2..45042f19f 100755 --- a/.github/scripts/Linux/arm/build.sh +++ b/.github/scripts/Linux/arm/build.sh @@ -22,6 +22,7 @@ export CFLAGS CXXFLAGS # shellcheck disable=SC2086 # intentional set -- $FEATURES +set -- --enable-sdl=2 # use SDL2 (environment.sh sets sdl=3) set -- "$@" --enable-drm_disp ./autogen.sh "$@" diff --git a/.github/scripts/Windows/prepare_msys.sh b/.github/scripts/Windows/prepare_msys.sh index bc3264f6f..af8c5fa27 100644 --- a/.github/scripts/Windows/prepare_msys.sh +++ b/.github/scripts/Windows/prepare_msys.sh @@ -58,7 +58,7 @@ $PACMAN_INSTALL $m-asciidoc \ $m-vulkan-headers $m-vulkan-loader \ $PACMAN_INSTALL $m-libsoxr $m-speexdsp -$PACMAN_INSTALL $m-SDL2 $m-SDL2_ttf +$PACMAN_INSTALL $m-sdl3 $m-sdl3-ttf $PACMAN_INSTALL $m-glm $PACMAN_INSTALL $m-portaudio # in case of problems build PA with --with-winapi=wmme,directx,wasapi $PACMAN_INSTALL $m-curl # RTSP capture diff --git a/.github/scripts/environment.sh b/.github/scripts/environment.sh index 4a0e466fc..75e6aa5fe 100644 --- a/.github/scripts/environment.sh +++ b/.github/scripts/environment.sh @@ -64,7 +64,7 @@ export FEATURES="\ --enable-rtsp-server\ --enable-scale\ --enable-screen\ - --enable-sdl=2\ + --enable-sdl=3\ --enable-sdp-http\ --enable-soxr\ --enable-speexdsp\ diff --git a/.github/scripts/macOS/prepare.sh b/.github/scripts/macOS/prepare.sh index 62bdf1d16..4f8ea3273 100755 --- a/.github/scripts/macOS/prepare.sh +++ b/.github/scripts/macOS/prepare.sh @@ -50,8 +50,8 @@ set -- \ pkg-config \ portaudio \ qt \ - sdl2 \ - sdl2_ttf \ + sdl3 \ + sdl3_ttf \ speexdsp \ vulkan-headers \ wolfssl \ From 031510b7e89ea0838a48c7af2ce8cc80cd7a8087 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 11:29:24 +0200 Subject: [PATCH 069/100] CI Linux: build fluidsynth alongside with SDL In U22.04 (current Linux CI image), fluidsynth depends on sdl2, which is undesirable. Actually we need to avoid sdl2 linking altogether. Build from sources alongside with SDL. This makes sense since SDL_mixer used to be build there and fluidsynth is replacing it. + prevent libsdl2-2.0-0 from (accidentally) installing --- .github/scripts/Linux/download_build_sdl.sh | 6 ++++++ .github/scripts/Linux/install_sdl.sh | 8 ++++---- .github/scripts/Linux/prepare.sh | 5 +++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/scripts/Linux/download_build_sdl.sh b/.github/scripts/Linux/download_build_sdl.sh index 577a3e2bd..04f56120f 100755 --- a/.github/scripts/Linux/download_build_sdl.sh +++ b/.github/scripts/Linux/download_build_sdl.sh @@ -17,3 +17,9 @@ cmake --build build -j "$(nproc)" sudo cmake --install build cd .. +git clone --recurse-submodules --depth 1\ + https://github.com/Fluidsynth/fluidsynth +cmake -S fluidsynth -B fluidsynth/build +cmake --build fluidsynth/build -j "$(nproc)" +sudo cmake --install fluidsynth/build + diff --git a/.github/scripts/Linux/install_sdl.sh b/.github/scripts/Linux/install_sdl.sh index ec2bc9525..ab75ddf3d 100755 --- a/.github/scripts/Linux/install_sdl.sh +++ b/.github/scripts/Linux/install_sdl.sh @@ -1,7 +1,7 @@ #!/bin/sh -eu -cd /var/tmp/sdl/SDL -sudo cmake --install build -cd ../SDL_ttf -sudo cmake --install build +cd /var/tmp/sdl +sudo cmake --install SDL/build +sudo cmake --install SDL_ttf/build +sudo cmake --install fluidsynth/build diff --git a/.github/scripts/Linux/prepare.sh b/.github/scripts/Linux/prepare.sh index 7b61c2287..f5cd33259 100755 --- a/.github/scripts/Linux/prepare.sh +++ b/.github/scripts/Linux/prepare.sh @@ -15,12 +15,12 @@ else # one-line-style (old) format sed -n '/^deb /s/^deb /deb-src /p' /etc/apt/sources.list | sudo tee /etc/apt/sources.list.d/sources.list fi +sudo apt-mark hold libsdl2-2.0-0 sudo apt update sudo apt install appstream `# appstreamcli for mkappimage AppStream validation` \ asciidoc sudo apt install fonts-dejavu-core sudo apt --no-install-recommends install nvidia-cuda-toolkit -sudo apt install libfluidsynth-dev sudo apt install libglew-dev libglfw3-dev sudo apt install libglm-dev sudo apt install imagemagick libmagickwand-dev @@ -36,9 +36,10 @@ get_build_deps_excl() { # $2 - pattern to exclude; separate packates with '\|' ( } sudo apt build-dep libsdl2 +fluidsynth_build_dep=$(get_build_deps_excl libfluidsynth3 libsdl2-dev) sdl2_ttf_build_dep=$(get_build_deps_excl libsdl2-ttf libsdl2-dev) # shellcheck disable=SC2086 # intentional -sudo apt install $sdl2_ttf_build_dep +sudo apt install $fluidsynth_build_dep $sdl2_ttf_build_dep ffmpeg_build_dep=$(get_build_deps_excl ffmpeg 'libsdl') # shellcheck disable=SC2086 # intentional From e5691c9beef636c28ab6e1ee2a2a13adfa1fac29 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 12:16:46 +0200 Subject: [PATCH 070/100] CI Linux: add /usr/local/lib to LD_LIBRARY_PATH + also to LIBRARY_PATH - this will is done also with -L in configure, probably Seems like that /usr/local/lib has never been in LD_LIBRARY_PATH and somehow it didn't matter so far. But with fluidsynth build, we now get in CI (== also prevents build): ``` ultragrid_acap_fluidsynth.so: libfluidsynth.so.3: cannot open shared object file: No such file or directory ```` --- .github/scripts/Linux/prepare.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/scripts/Linux/prepare.sh b/.github/scripts/Linux/prepare.sh index f5cd33259..7073ad26f 100755 --- a/.github/scripts/Linux/prepare.sh +++ b/.github/scripts/Linux/prepare.sh @@ -1,9 +1,11 @@ #!/bin/bash -eux export PKG_CONFIG_PATH=/usr/local/qt/lib/pkgconfig:/usr/local/lib/pkgconfig +export LIBRARY_PATH=/usr/local/lib:/usr/local/qt/lib printf "%b" "\ CPATH=/usr/local/qt/include\n\ -LIBRARY_PATH=/usr/local/qt/lib\n\ +LIBRARY_PATH=$LIBRARY_PATH\n\ +LD_LIBRARY_PATH=$LIBRARY_PATH\n\ PKG_CONFIG_PATH=$PKG_CONFIG_PATH\n" >> "$GITHUB_ENV" printf "/usr/local/qt/bin\n" >> "$GITHUB_PATH" From cefae843624b093de7a166ba6fa2670888cb8321 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 12:33:33 +0200 Subject: [PATCH 071/100] soundfont lookup (sdl,fluidsynth): fix dfl sf3 path default path for default-GM.sf3 is in /sf3 subdir, not /sf2 --- src/audio/capture/fluidsynth.c | 2 +- src/audio/capture/sdl_mixer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audio/capture/fluidsynth.c b/src/audio/capture/fluidsynth.c index a4609f0d3..e63bf909e 100644 --- a/src/audio/capture/fluidsynth.c +++ b/src/audio/capture/fluidsynth.c @@ -196,7 +196,7 @@ get_soundfont() "/share/soundfonts/default.sf2", "/share/soundfonts/default.sf3", "/share/sounds/sf2/default-GM.sf2", - "/share/sounds/sf2/default-GM.sf3", // Ubuntu + "/share/sounds/sf3/default-GM.sf3", // Ubuntu }; for (size_t i = 0; i < sizeof roots / sizeof roots[0]; ++i) { for (size_t j = 0; diff --git a/src/audio/capture/sdl_mixer.c b/src/audio/capture/sdl_mixer.c index 3f73ce0d5..b43c26c35 100644 --- a/src/audio/capture/sdl_mixer.c +++ b/src/audio/capture/sdl_mixer.c @@ -185,7 +185,7 @@ static void try_open_soundfont() { const char *roots[] = { get_install_root(), "/usr" }; const char *sf_candidates[] = { // without install prefix "/share/soundfonts/default.sf2", "/share/soundfonts/default.sf3", - "/share/sounds/sf2/default-GM.sf2", "/share/sounds/sf2/default-GM.sf3", // Ubuntu + "/share/sounds/sf2/default-GM.sf2", "/share/sounds/sf3/default-GM.sf3", // Ubuntu }; for (size_t i = 0; i < sizeof roots / sizeof roots[0]; ++i) { for (size_t j = 0; j < sizeof sf_candidates / sizeof sf_candidates[0]; ++j) { From 4466594c1e3caa1fe4d06e6e438080332fec2034 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 14:03:53 +0200 Subject: [PATCH 072/100] CI Linux: add Qt6 Wayland plugin Install the qt6-wayland package - the plugin will then be deployed to the AppImage automatically. for the case when user has set QT_QPA_PLATFORM=wayland --- .github/scripts/Linux/prepare.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/Linux/prepare.sh b/.github/scripts/Linux/prepare.sh index 7073ad26f..455a14392 100755 --- a/.github/scripts/Linux/prepare.sh +++ b/.github/scripts/Linux/prepare.sh @@ -48,7 +48,7 @@ ffmpeg_build_dep=$(get_build_deps_excl ffmpeg 'libsdl') sudo apt install $ffmpeg_build_dep libdav1d-dev libde265-dev libopenh264-dev sudo apt-get -y remove 'libavcodec*' 'libavutil*' 'libswscale*' libvpx-dev nginx -sudo apt install qt6-base-dev +sudo apt install qt6-base-dev qt6-wayland . /etc/os-release # source ID and VERSION_ID if [ "$ID" = ubuntu ] && [ "$VERSION_ID" = 22.04 ]; then # https://bugs.launchpad.net/ubuntu/+source/qtchooser/+bug/1964763 bug From 0cfe4b6b0f5895252d1ca03979689d6a23487c79 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 14:36:31 +0200 Subject: [PATCH 073/100] CI Linux SDL: consolidate add all to one script with deps/build_install/install_cached actions - yml - use action @main - cache: do not depend .github/scripts/Linux/prepare.sh, just the catch-all SDL install file - build SDL,SDL_ttf without entering the dir (as fluidsynth already does) --- .github/scripts/Linux/common.sh | 6 +++ .github/scripts/Linux/download_build_sdl.sh | 25 ----------- .github/scripts/Linux/install_sdl.sh | 50 +++++++++++++++++++-- .github/scripts/Linux/prepare.sh | 14 +++--- .github/workflows/ccpp.yml | 8 ++-- 5 files changed, 61 insertions(+), 42 deletions(-) create mode 100644 .github/scripts/Linux/common.sh delete mode 100755 .github/scripts/Linux/download_build_sdl.sh diff --git a/.github/scripts/Linux/common.sh b/.github/scripts/Linux/common.sh new file mode 100644 index 000000000..99ea2e601 --- /dev/null +++ b/.github/scripts/Linux/common.sh @@ -0,0 +1,6 @@ +# $2 - pattern to exclude; separate packates with '\|' (BRE alternation) +get_build_deps_excl() { + apt-cache showsrc "$1" | sed -n "/^Build-Depends:/\ +{s/Build-Depends://;p;q}" | tr ',' '\n' | cut -f 2 -d\ | grep -v "$2" +} + diff --git a/.github/scripts/Linux/download_build_sdl.sh b/.github/scripts/Linux/download_build_sdl.sh deleted file mode 100755 index 04f56120f..000000000 --- a/.github/scripts/Linux/download_build_sdl.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -eu - -mkdir -p /var/tmp/sdl -cd /var/tmp/sdl - -git clone --depth 1 https://github.com/libsdl-org/SDL -cd SDL -cmake -S . -B build -cmake --build build -j "$(nproc)" -sudo cmake --install build -cd .. - -git clone --depth 1 https://github.com/libsdl-org/SDL_ttf -cd SDL_ttf -cmake -S . -B build -cmake --build build -j "$(nproc)" -sudo cmake --install build -cd .. - -git clone --recurse-submodules --depth 1\ - https://github.com/Fluidsynth/fluidsynth -cmake -S fluidsynth -B fluidsynth/build -cmake --build fluidsynth/build -j "$(nproc)" -sudo cmake --install fluidsynth/build - diff --git a/.github/scripts/Linux/install_sdl.sh b/.github/scripts/Linux/install_sdl.sh index ab75ddf3d..df6ba0458 100755 --- a/.github/scripts/Linux/install_sdl.sh +++ b/.github/scripts/Linux/install_sdl.sh @@ -1,7 +1,49 @@ #!/bin/sh -eu -cd /var/tmp/sdl -sudo cmake --install SDL/build -sudo cmake --install SDL_ttf/build -sudo cmake --install fluidsynth/build +dir=$(dirname "$0") +# shellcheck source=/dev/null +. "$dir/common.sh" # for get_build_deps_excl +# build dir that will be restored from cache +cache_dir=/var/tmp/sdl + +# install the deps - runs always (regardless the cache) +deps() { + sudo apt build-dep libsdl2 + fluidsynth_build_dep=$(get_build_deps_excl libfluidsynth3 libsdl2-dev) + sdl2_ttf_build_dep=$(get_build_deps_excl libsdl2-ttf libsdl2-dev) + # shellcheck disable=SC2086 # intentional + sudo apt install $fluidsynth_build_dep $sdl2_ttf_build_dep +} + +# build SDL, SDL_ttf and fluidsynth and also install them +build_install() { + mkdir -p $cache_dir + cd $cache_dir + + git clone --depth 1 https://github.com/libsdl-org/SDL + cmake -S SDL -B SDL/build + cmake --build SDL/build -j "$(nproc)" + sudo cmake --install SDL/build + + git clone --depth 1 https://github.com/libsdl-org/SDL_ttf + cmake -S SDL_ttf -B SDL_ttf/build + cmake --build SDL_ttf/build -j "$(nproc)" + sudo cmake --install SDL_ttf/build + + git clone --recurse-submodules --depth 1\ + https://github.com/Fluidsynth/fluidsynth + cmake -S fluidsynth -B fluidsynth/build + cmake --build fluidsynth/build -j "$(nproc)" + sudo cmake --install fluidsynth/build +} + +# if cache is successfully restored, just install the builds +install_cached() { + cd $cache_dir + sudo cmake --install SDL/build + sudo cmake --install SDL_ttf/build + sudo cmake --install fluidsynth/build +} + +"${1?action required!}" diff --git a/.github/scripts/Linux/prepare.sh b/.github/scripts/Linux/prepare.sh index 455a14392..1cca80726 100755 --- a/.github/scripts/Linux/prepare.sh +++ b/.github/scripts/Linux/prepare.sh @@ -1,5 +1,9 @@ #!/bin/bash -eux +dir=$(dirname "$0") +# shellcheck source=/dev/null +. "$dir/common.sh" # for get_build_deps_excl + export PKG_CONFIG_PATH=/usr/local/qt/lib/pkgconfig:/usr/local/lib/pkgconfig export LIBRARY_PATH=/usr/local/lib:/usr/local/qt/lib printf "%b" "\ @@ -33,15 +37,7 @@ sudo apt install libopencv-core-dev libopencv-imgproc-dev sudo apt install libcurl4-openssl-dev # for RTSP client (vidcap) sudo apt install i965-va-driver-shaders libva-dev # instead of i965-va-driver -get_build_deps_excl() { # $2 - pattern to exclude; separate packates with '\|' (BRE alternation) - apt-cache showsrc "$1" | sed -n '/^Build-Depends:/{s/Build-Depends://;p;q}' | tr ',' '\n' | cut -f 2 -d\ | grep -v "$2" -} - -sudo apt build-dep libsdl2 -fluidsynth_build_dep=$(get_build_deps_excl libfluidsynth3 libsdl2-dev) -sdl2_ttf_build_dep=$(get_build_deps_excl libsdl2-ttf libsdl2-dev) -# shellcheck disable=SC2086 # intentional -sudo apt install $fluidsynth_build_dep $sdl2_ttf_build_dep +"$dir/install_sdl.sh" deps ffmpeg_build_dep=$(get_build_deps_excl ffmpeg 'libsdl') # shellcheck disable=SC2086 # intentional diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 842e85876..02b09c930 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -93,16 +93,16 @@ jobs: run: .github/scripts/Linux/install_ffmpeg.sh - name: Cache SDL id: cache-sdl - uses: actions/cache@v3 + uses: actions/cache@main with: path: '/var/tmp/sdl' - key: cache-sdl-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/prepare.sh', '.github/scripts/Linux/download_build_sdl.sh' ) }} + key: cache-sdl-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_sdl.sh' ) }} - name: Build SDL if: steps.cache-sdl.outputs.cache-hit != 'true' - run: .github/scripts/Linux/download_build_sdl.sh + run: .github/scripts/Linux/install_sdl.sh build_install - name: Install Cached SDL if: steps.cache-sdl.outputs.cache-hit == 'true' - run: .github/scripts/Linux/install_sdl.sh + run: .github/scripts/Linux/install_sdl.sh install_cached - name: configure run: "./autogen.sh $FEATURES || { RC=$?; cat config.log; exit $RC; }" - name: make From a15d3d0d956e664e8d419959329727db6615b1eb Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 15:17:48 +0200 Subject: [PATCH 074/100] CI: environment.sh: `set -eu` the script is sourced so errexit+nounset is perhaps not yet set But it is better to exit early, especially when setting the environment variables that are considered a prerequisite. --- .github/scripts/environment.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/scripts/environment.sh b/.github/scripts/environment.sh index 75e6aa5fe..59846c633 100644 --- a/.github/scripts/environment.sh +++ b/.github/scripts/environment.sh @@ -9,6 +9,8 @@ ## - **apple_key_p12_b64** - [mac only] base64-encoded $KEY_FILE (using ## password $KEY_FILE_PASS) +set -eu + if expr "$GITHUB_REF" : 'refs/tags/' >/dev/null; then TAG=${GITHUB_REF#refs/tags/} VERSION=${TAG#v} From ed78a761fed521ecd5603bd1423db3094d463539 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 27 Aug 2025 15:57:39 +0200 Subject: [PATCH 075/100] CI Linux FFmpeg: consolidate to a single script in similar fashion as already done for install_sdl.sh As a bonus, we do not need to depend on whole prepare.sh for cache rebuild but only on this script, which is perhaps cleaner and may yield less rebuilds. Also prepare.sh is slightly easier. --- .../scripts/Linux/download_build_ffmpeg.sh | 75 ---------- .github/scripts/Linux/install_ffmpeg.sh | 136 ++++++++++++++++-- .github/scripts/Linux/prepare.sh | 8 +- .github/workflows/ccpp.yml | 6 +- 4 files changed, 129 insertions(+), 96 deletions(-) delete mode 100755 .github/scripts/Linux/download_build_ffmpeg.sh diff --git a/.github/scripts/Linux/download_build_ffmpeg.sh b/.github/scripts/Linux/download_build_ffmpeg.sh deleted file mode 100755 index 40a31e9d5..000000000 --- a/.github/scripts/Linux/download_build_ffmpeg.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash -eux - -install_aom() {( - git clone --depth 1 https://aomedia.googlesource.com/aom - mkdir -p aom/build - cd aom/build - cmake -DBUILD_SHARED_LIBS=1 .. - cmake --build . --parallel "$(nproc)" - sudo cmake --install . -)} - -install_libvpx() { - ( - git clone --depth 1 https://github.com/webmproject/libvpx.git - cd libvpx - ./configure --enable-pic --disable-examples --disable-install-bins --disable-install-srcs --enable-vp9-highbitdepth - make -j "$(nproc)" - sudo make install - ) -} - -FFMPEG_GIT_DEPTH=5000 # greater depth is useful for 3-way merges -install_svt() { - ( git clone --depth 1 https://github.com/OpenVisualCloud/SVT-HEVC && cd SVT-HEVC/Build/linux && ./build.sh release && cd Release && sudo cmake --install . || exit 1 ) - ( git clone --depth 1 https://gitlab.com/AOMediaCodec/SVT-AV1.git && cd SVT-AV1 && cd Build && cmake .. -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Release && cmake --build . --parallel && sudo cmake --install . || exit 1 ) - ( git clone --depth 1 https://github.com/OpenVisualCloud/SVT-VP9.git && cd SVT-VP9/Build && cmake .. -DCMAKE_BUILD_TYPE=Release && cmake --build . --parallel && sudo cmake --install . || exit 1 ) - # libsvtav1 in FFmpeg upstream, for SVT-HEVC now our custom patch in ffmpeg-patches - # if patch apply fails, try increasing $FFMPEG_GIT_DEPTH - git am -3 SVT-VP9/ffmpeg_plugin/master-*.patch - # TOD TOREMOVE when not needed - sed 's/\* avctx->ticks_per_frame//' libavcodec/libsvt_vp9.c >fix - mv fix libavcodec/libsvt_vp9.c -} - -# The NV Video Codec SDK headers version 12.0 implies driver v520.56.06 in Linux -install_nv_codec_headers() { - git clone --depth 1 -b sdk/12.0 https://github.com/FFmpeg/nv-codec-headers - ( cd nv-codec-headers && make && sudo make install || exit 1 ) -} - -install_onevpl() {( - git clone --depth 1 https://github.com/oneapi-src/oneVPL - mkdir oneVPL/build - cd oneVPL/build - cmake -DBUILD_TOOLS=OFF .. - cmake --build . --config Release --parallel - sudo cmake --build . --config Release --target install -)} - -rm -rf /var/tmp/ffmpeg -git clone --depth $FFMPEG_GIT_DEPTH https://github.com/FFmpeg/FFmpeg.git \ - /var/tmp/ffmpeg -cd /var/tmp/ffmpeg -install_aom -install_libvpx -install_nv_codec_headers -install_onevpl -install_svt -# apply patches -find "$GITHUB_WORKSPACE/.github/scripts/Linux/ffmpeg-patches" -name '*.patch' -print0 | sort -z | xargs -0 -n 1 git am -3 -./configure --disable-static --enable-shared --enable-gpl --enable-libx264 --enable-libx265 --enable-libopus --enable-nonfree --enable-nvenc --enable-libaom --enable-libvpx --enable-libspeex --enable-libmp3lame \ - --enable-libdav1d \ - --enable-libde265 \ - --enable-libopenh264 \ - --enable-librav1e \ - --enable-libsvtav1 \ - --enable-libsvthevc \ - --enable-libsvtvp9 \ - --enable-libvpl \ - --disable-sdl2 \ - --enable-vulkan \ - -make -j "$(nproc)" -sudo make install -sudo ldconfig diff --git a/.github/scripts/Linux/install_ffmpeg.sh b/.github/scripts/Linux/install_ffmpeg.sh index b14f135aa..a2b212491 100755 --- a/.github/scripts/Linux/install_ffmpeg.sh +++ b/.github/scripts/Linux/install_ffmpeg.sh @@ -1,13 +1,127 @@ #!/bin/bash -eux -cd /var/tmp/ffmpeg -( cd libvpx && sudo make install ) -( cd nv-codec-headers && sudo make install ) -( cd aom/build && sudo cmake --install . ) -sudo cmake --install SVT-AV1/Build -sudo cmake --install SVT-HEVC/Build/linux/Release -sudo cmake --install SVT-VP9/Build -sudo cmake --build oneVPL/build --config Release --target install - -sudo make install -sudo ldconfig +dir=$(dirname "$0") +# shellcheck source=/dev/null +. "$dir/common.sh" # for get_build_deps_excl + +# build dir that will be restored from cache +cache_dir=/var/tmp/ffmpeg + +# install the deps - runs always (regardless the cache) +deps() { + ffmpeg_build_dep=$(get_build_deps_excl ffmpeg 'libsdl') + # shellcheck disable=SC2086 # intentional + sudo apt install $ffmpeg_build_dep libdav1d-dev libde265-dev \ + libopenh264-dev + sudo apt-get -y remove 'libavcodec*' 'libavutil*' 'libswscale*' \ + libvpx-dev nginx +} + +install_aom() {( + git clone --depth 1 https://aomedia.googlesource.com/aom + mkdir -p aom/build + cd aom/build + cmake -DBUILD_SHARED_LIBS=1 .. + cmake --build . --parallel "$(nproc)" + sudo cmake --install . +)} + +install_libvpx() {( + git clone --depth 1 https://github.com/webmproject/libvpx.git + cd libvpx + ./configure --enable-pic --disable-examples --disable-install-bins \ + --disable-install-srcs --enable-vp9-highbitdepth + make -j "$(nproc)" + sudo make install +)} + +install_svt() { + ( git clone --depth 1 https://github.com/OpenVisualCloud/SVT-HEVC && + cd SVT-HEVC/Build/linux && ./build.sh release && cd Release && + sudo cmake --install . || exit 1 ) + ( git clone --depth 1 https://gitlab.com/AOMediaCodec/SVT-AV1.git && + cd SVT-AV1 && cd Build && + cmake .. -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Release && + cmake --build . --parallel && sudo cmake --install . || exit 1 ) + ( git clone --depth 1 https://github.com/OpenVisualCloud/SVT-VP9.git && + cd SVT-VP9/Build && cmake .. -DCMAKE_BUILD_TYPE=Release && + cmake --build . --parallel && sudo cmake --install . || exit 1 ) + # libsvtav1 in FFmpeg upstream, for SVT-HEVC now our custom patch in ffmpeg-patches + # if patch apply fails, try increasing $FFMPEG_GIT_DEPTH + git am -3 SVT-VP9/ffmpeg_plugin/master-*.patch + # TOD TOREMOVE when not needed + sed 's/\* avctx->ticks_per_frame//' libavcodec/libsvt_vp9.c >fix + mv fix libavcodec/libsvt_vp9.c +} + +# The NV Video Codec SDK headers version 12.0 implies driver v520.56.06 in Linux +install_nv_codec_headers() { + git clone --depth 1 -b sdk/12.0 https://github.com/FFmpeg/nv-codec-headers + ( cd nv-codec-headers && make && sudo make install || exit 1 ) +} + +install_onevpl() {( + git clone --depth 1 https://github.com/oneapi-src/oneVPL + mkdir oneVPL/build + cd oneVPL/build + cmake -DBUILD_TOOLS=OFF .. + cmake --build . --config Release --parallel + sudo cmake --build . --config Release --target install +)} + +# build FFmpeg deps + FFmpeg itself +build_install() { + rm -rf $cache_dir + FFMPEG_GIT_DEPTH=5000 # greater depth is useful for 3-way merges + git clone --depth $FFMPEG_GIT_DEPTH https://github.com/FFmpeg/FFmpeg.git \ + $cache_dir + cd $cache_dir + install_aom + install_libvpx + install_nv_codec_headers + install_onevpl + install_svt + # apply patches + find "$GITHUB_WORKSPACE/.github/scripts/Linux/ffmpeg-patches" \ + -name '*.patch' -print0 | sort -z | xargs -0 -n 1 git am -3 + ./configure --disable-static --enable-shared --enable-gpl --enable-nonfree \ + --disable-sdl2 \ + --enable-libaom \ + --enable-libdav1d \ + --enable-libde265 \ + --enable-libmp3lame \ + --enable-libopenh264 \ + --enable-libopus \ + --enable-librav1e \ + --enable-libspeex \ + --enable-libsvtav1 \ + --enable-libsvthevc \ + --enable-libsvtvp9 \ + --enable-libvpl \ + --enable-libvpx \ + --enable-libx264 \ + --enable-libx265 \ + --enable-nvenc \ + --enable-vulkan \ + + make -j "$(nproc)" + sudo make install + sudo ldconfig +} + +# if cache is successfully restored, just install the builds +install_cached() { + cd $cache_dir + ( cd libvpx && sudo make install ) + ( cd nv-codec-headers && sudo make install ) + ( cd aom/build && sudo cmake --install . ) + sudo cmake --install SVT-AV1/Build + sudo cmake --install SVT-HEVC/Build/linux/Release + sudo cmake --install SVT-VP9/Build + sudo cmake --build oneVPL/build --config Release --target install + + sudo make install + sudo ldconfig +} + +"${1?action required!}" diff --git a/.github/scripts/Linux/prepare.sh b/.github/scripts/Linux/prepare.sh index 1cca80726..39b67d7a0 100755 --- a/.github/scripts/Linux/prepare.sh +++ b/.github/scripts/Linux/prepare.sh @@ -1,8 +1,6 @@ #!/bin/bash -eux dir=$(dirname "$0") -# shellcheck source=/dev/null -. "$dir/common.sh" # for get_build_deps_excl export PKG_CONFIG_PATH=/usr/local/qt/lib/pkgconfig:/usr/local/lib/pkgconfig export LIBRARY_PATH=/usr/local/lib:/usr/local/qt/lib @@ -38,11 +36,7 @@ sudo apt install libcurl4-openssl-dev # for RTSP client (vidcap) sudo apt install i965-va-driver-shaders libva-dev # instead of i965-va-driver "$dir/install_sdl.sh" deps - -ffmpeg_build_dep=$(get_build_deps_excl ffmpeg 'libsdl') -# shellcheck disable=SC2086 # intentional -sudo apt install $ffmpeg_build_dep libdav1d-dev libde265-dev libopenh264-dev -sudo apt-get -y remove 'libavcodec*' 'libavutil*' 'libswscale*' libvpx-dev nginx +"$dir/install_ffmpeg.sh" deps sudo apt install qt6-base-dev qt6-wayland . /etc/os-release # source ID and VERSION_ID diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 02b09c930..66348c88a 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -84,13 +84,13 @@ jobs: uses: actions/cache@main with: path: '/var/tmp/ffmpeg' - key: cache-ffmpeg-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/prepare.sh', '.github/scripts/Linux/download_build_ffmpeg.sh', '.github/scripts/Linux/ffmpeg-patches/*') }} + key: cache-ffmpeg-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_ffmpeg.sh', '.github/scripts/Linux/ffmpeg-patches/*') }} - name: Build FFmpeg if: steps.cache-ffmpeg.outputs.cache-hit != 'true' - run: .github/scripts/Linux/download_build_ffmpeg.sh + run: .github/scripts/Linux/install_ffmpeg.sh build_install - name: Install Cached FFmpeg if: steps.cache-ffmpeg.outputs.cache-hit == 'true' - run: .github/scripts/Linux/install_ffmpeg.sh + run: .github/scripts/Linux/install_ffmpeg.sh install_cached - name: Cache SDL id: cache-sdl uses: actions/cache@main From 1a1bee8a2987c3bbf756ca4ec13115893ca86012 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 28 Aug 2025 08:11:08 +0200 Subject: [PATCH 076/100] CI Linux: simplify caches handle cache build/install (transitively) in prepare.sh --- .github/scripts/Linux/install_ffmpeg.sh | 7 ++++++- .github/scripts/Linux/install_others.sh | 1 + .github/scripts/Linux/install_sdl.sh | 7 ++++++- .github/scripts/Linux/prepare.sh | 6 +++--- .github/scripts/install-common-deps.sh | 3 +++ .github/workflows/ccpp.yml | 26 ++++++------------------- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/scripts/Linux/install_ffmpeg.sh b/.github/scripts/Linux/install_ffmpeg.sh index a2b212491..161ceb3e1 100755 --- a/.github/scripts/Linux/install_ffmpeg.sh +++ b/.github/scripts/Linux/install_ffmpeg.sh @@ -124,4 +124,9 @@ install_cached() { sudo ldconfig } -"${1?action required!}" +deps +if [ -d $cache_dir ]; then + install_cached +else + build_install +fi diff --git a/.github/scripts/Linux/install_others.sh b/.github/scripts/Linux/install_others.sh index f74d17da8..22a7027df 100755 --- a/.github/scripts/Linux/install_others.sh +++ b/.github/scripts/Linux/install_others.sh @@ -66,6 +66,7 @@ install_rav1e() {( # FFmpeg master needs at least v1.3.277 as for 6th Mar '25 install_vulkan() {( + sudo apt build-dep libvulkan1 git clone --depth 1 https://github.com/KhronosGroup/Vulkan-Headers mkdir Vulkan-Headers/build cd Vulkan-Headers/build diff --git a/.github/scripts/Linux/install_sdl.sh b/.github/scripts/Linux/install_sdl.sh index df6ba0458..02ce10a10 100755 --- a/.github/scripts/Linux/install_sdl.sh +++ b/.github/scripts/Linux/install_sdl.sh @@ -46,4 +46,9 @@ install_cached() { sudo cmake --install fluidsynth/build } -"${1?action required!}" +deps +if [ -d $cache_dir ]; then + install_cached +else + build_install +fi diff --git a/.github/scripts/Linux/prepare.sh b/.github/scripts/Linux/prepare.sh index 39b67d7a0..11ed0c9d4 100755 --- a/.github/scripts/Linux/prepare.sh +++ b/.github/scripts/Linux/prepare.sh @@ -35,9 +35,6 @@ sudo apt install libopencv-core-dev libopencv-imgproc-dev sudo apt install libcurl4-openssl-dev # for RTSP client (vidcap) sudo apt install i965-va-driver-shaders libva-dev # instead of i965-va-driver -"$dir/install_sdl.sh" deps -"$dir/install_ffmpeg.sh" deps - sudo apt install qt6-base-dev qt6-wayland . /etc/os-release # source ID and VERSION_ID if [ "$ID" = ubuntu ] && [ "$VERSION_ID" = 22.04 ]; then @@ -53,3 +50,6 @@ qt6.conf" "/usr/lib/$(uname -m)-linux-gnu/qt-default/qtchooser/default.conf" "$GITHUB_WORKSPACE/.github/scripts/Linux/install_others.sh" +"$dir"/install_sdl.sh +"$dir"/install_ffmpeg.sh + diff --git a/.github/scripts/install-common-deps.sh b/.github/scripts/install-common-deps.sh index 94fb6bee1..281d42482 100755 --- a/.github/scripts/install-common-deps.sh +++ b/.github/scripts/install-common-deps.sh @@ -49,6 +49,9 @@ download_build_aja() { } install_aja() {( + if [ "$(uname -s)" = Linux ]; then + sudo apt install libudev-dev + fi if [ ! -d libajantv2 ]; then download_build_aja fi diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 66348c88a..08852f815 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -75,34 +75,20 @@ jobs: if: steps.cache-ndi.outputs.cache-hit != 'true' run: "curl -Lf https://downloads.ndi.tv/SDK/NDI_SDK_Linux/\ Install_NDI_SDK_v6_Linux.tar.gz -o /var/tmp/Install_NDI_SDK_Linux.tar.gz" - - name: bootstrap - run: | - . .github/scripts/environment.sh - .github/scripts/Linux/prepare.sh - - name: Run actions/cache for FFmpeg - id: cache-ffmpeg + - name: Cache FFmpeg uses: actions/cache@main with: path: '/var/tmp/ffmpeg' - key: cache-ffmpeg-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_ffmpeg.sh', '.github/scripts/Linux/ffmpeg-patches/*') }} - - name: Build FFmpeg - if: steps.cache-ffmpeg.outputs.cache-hit != 'true' - run: .github/scripts/Linux/install_ffmpeg.sh build_install - - name: Install Cached FFmpeg - if: steps.cache-ffmpeg.outputs.cache-hit == 'true' - run: .github/scripts/Linux/install_ffmpeg.sh install_cached + key: cache-ffmpeg-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_ffmpeg.sh', '.github/scripts/Linux/install_other.sh', '.github/scripts/Linux/ffmpeg-patches/*') }} - name: Cache SDL - id: cache-sdl uses: actions/cache@main with: path: '/var/tmp/sdl' key: cache-sdl-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_sdl.sh' ) }} - - name: Build SDL - if: steps.cache-sdl.outputs.cache-hit != 'true' - run: .github/scripts/Linux/install_sdl.sh build_install - - name: Install Cached SDL - if: steps.cache-sdl.outputs.cache-hit == 'true' - run: .github/scripts/Linux/install_sdl.sh install_cached + - name: bootstrap + run: | + . .github/scripts/environment.sh + .github/scripts/Linux/prepare.sh - name: configure run: "./autogen.sh $FEATURES || { RC=$?; cat config.log; exit $RC; }" - name: make From 14b41852d2e0f566cefe4f99df4c0d2ac519e414 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 28 Aug 2025 08:29:00 +0200 Subject: [PATCH 077/100] CI Linux: build up-to-date glfw --- .github/scripts/Linux/install_glfw.sh | 38 +++++++++++++++++++++++++++ .github/scripts/Linux/prepare.sh | 3 ++- .github/workflows/ccpp.yml | 5 ++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100755 .github/scripts/Linux/install_glfw.sh diff --git a/.github/scripts/Linux/install_glfw.sh b/.github/scripts/Linux/install_glfw.sh new file mode 100755 index 000000000..04459d4a8 --- /dev/null +++ b/.github/scripts/Linux/install_glfw.sh @@ -0,0 +1,38 @@ +#!/bin/sh -eu + +dir=$(dirname "$0") +# shellcheck source=/dev/null +. "$dir/common.sh" # for get_build_deps_excl + +# build dir that will be restored from cache +cache_dir=/var/tmp/glfw + +# install the deps - runs always (regardless the cache) +deps() { + sudo apt build-dep libglfw3 +} + +# build SDL, SDL_ttf and fluidsynth and also install them +build_install() { + mkdir -p $cache_dir + cd $cache_dir + + git clone --depth 1 https://github.com/glfw/glfw.git + cmake -S glfw -B glfw/build \ + -DGLFW_BUILD_WAYLAND=ON -DGLFW_BUILD_X11=ON + cmake --build glfw/build -j "$(nproc)" + sudo cmake --install glfw/build +} + +# if cache is successfully restored, just install the builds +install_cached() { + cd $cache_dir + sudo cmake --install glfw/build +} + +deps +if [ -d $cache_dir ]; then + install_cached +else + build_install +fi diff --git a/.github/scripts/Linux/prepare.sh b/.github/scripts/Linux/prepare.sh index 11ed0c9d4..4b47edc5f 100755 --- a/.github/scripts/Linux/prepare.sh +++ b/.github/scripts/Linux/prepare.sh @@ -25,7 +25,7 @@ sudo apt install appstream `# appstreamcli for mkappimage AppStream validation` asciidoc sudo apt install fonts-dejavu-core sudo apt --no-install-recommends install nvidia-cuda-toolkit -sudo apt install libglew-dev libglfw3-dev +sudo apt install libglew-dev sudo apt install libglm-dev sudo apt install imagemagick libmagickwand-dev sudo apt install libsoxr-dev libspeexdsp-dev @@ -52,4 +52,5 @@ qt6.conf" "/usr/lib/$(uname -m)-linux-gnu/qt-default/qtchooser/default.conf" "$dir"/install_sdl.sh "$dir"/install_ffmpeg.sh +"$dir"/install_glfw.sh diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 08852f815..d1f6b29b9 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -85,6 +85,11 @@ jobs: with: path: '/var/tmp/sdl' key: cache-sdl-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_sdl.sh' ) }} + - name: Cache GLFW + uses: actions/cache@main + with: + path: '/var/tmp/glfw' + key: cache-sdl-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_glfw.sh' ) }} - name: bootstrap run: | . .github/scripts/environment.sh From 78683440568ec4970caaaeb94de570faa34bf42d Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 28 Aug 2025 09:38:09 +0200 Subject: [PATCH 078/100] vdisp/gl: parse_fmt - return bool returning the pointer was a bit tricky - very slightly more correct but needlessly more complicated --- src/video_display/gl.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/video_display/gl.cpp b/src/video_display/gl.cpp index 82e20a4f4..c637fafb8 100644 --- a/src/video_display/gl.cpp +++ b/src/video_display/gl.cpp @@ -732,13 +732,15 @@ list_hints() "Example usage: " TBOLD("-d gl:init_hint=platform=x11") "\n\n"); } -static void *display_gl_parse_fmt(struct state_gl *s, char *ptr) { +static bool +display_gl_parse_fmt(struct state_gl *s, char *ptr) +{ char *tok, *save_ptr = NULL; while((tok = strtok_r(ptr, ":", &save_ptr)) != NULL) { if (strcmp(tok, "help") == 0 || strcmp(tok, "fullhelp") == 0) { gl_show_help(strcmp(tok, "fullhelp") == 0); - return INIT_NOERR; + return false; } if (!strcmp(tok, "d") || !strcmp(tok, "dforce")) { s->deinterlace = !strcmp(tok, "d") ? state_gl::deint::on : state_gl::deint::force; @@ -784,7 +786,7 @@ static void *display_gl_parse_fmt(struct state_gl *s, char *ptr) { } #else log_msg(LOG_LEVEL_ERROR, MOD_NAME "Syphon/Spout support not compiled in.\n"); - return nullptr; + return false; #endif } else if (strstr(tok, "gamma=") == tok) { s->gamma = stof(strchr(tok, '=') + 1); @@ -798,7 +800,7 @@ static void *display_gl_parse_fmt(struct state_gl *s, char *ptr) { } else if (strstr(tok, "size=") == tok || strstr(tok, "fixed_size=") == tok) { if (!set_size(s, tok)) { - return nullptr; + return false; } } else if (strcmp(tok, "fixed_size") == 0) { s->fixed_size = true; @@ -810,15 +812,15 @@ static void *display_gl_parse_fmt(struct state_gl *s, char *ptr) { parse_hints(s, false, strchr(tok, '=') + 1); } else if (strcmp(tok, "list_hints") == 0) { list_hints(); - return nullptr; + return false; } else { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unknown option: %s\n", tok); - return nullptr; + return false; } ptr = NULL; } - return s; + return true; } static void * display_gl_init(struct module *parent, const char *fmt, unsigned int flags) { @@ -833,16 +835,16 @@ static void * display_gl_init(struct module *parent, const char *fmt, unsigned i if (fmt != NULL) { char *tmp = strdup(fmt); - void *ret = nullptr; + bool ret = false; try { ret = display_gl_parse_fmt(s, tmp); } catch (std::invalid_argument &e) { LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Invalid numeric value for an option!\n"; } free(tmp); - if (ret != s) { // ret is either nullptr or INIT_NOERR (help requested) + if (!ret) { delete s; - return ret; + return strstr(fmt, "help") == nullptr ? nullptr : INIT_NOERR; } } @@ -1302,7 +1304,6 @@ static void gl_process_frames(struct state_gl *s) free_message(msg, r); } - if (s->show_cursor == state_gl::SC_AUTOHIDE) { if (s->cursor_shown_from != steady_clock::time_point()) { const auto now = steady_clock::now(); From eb7cb38ea4bd12b20586c204ed37de90ad184755 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 28 Aug 2025 10:16:35 +0200 Subject: [PATCH 079/100] vdisp/gl: add platform opt/help/info --- src/video_display/gl.cpp | 74 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/src/video_display/gl.cpp b/src/video_display/gl.cpp index c637fafb8..b6294943a 100644 --- a/src/video_display/gl.cpp +++ b/src/video_display/gl.cpp @@ -69,6 +69,7 @@ #ifdef HAVE_CONFIG_H #include "config.h" // for HAVE_SPOUT, HAVE_SYPHON #endif +#include "compat/strings.h" // for strcasecmp #include "debug.h" #include "gl_context.h" #include "host.h" @@ -365,6 +366,16 @@ static constexpr pair keybindings[] = { pair{K_CTRL_UP, "make window 10% bigger"} }; +const static struct { + int platform_id; + const char *name; +} platform_map[] = { + { GLFW_PLATFORM_WIN32, "Win32" }, + { GLFW_PLATFORM_COCOA, "Cocoa" }, + { GLFW_PLATFORM_WAYLAND, "Wayland" }, + { GLFW_PLATFORM_X11, "X11" }, +}; + /* Prototyping */ static bool check_display_gl_version(bool print_ver); static bool display_gl_init_opengl(struct state_gl *s); @@ -528,6 +539,38 @@ static void gl_print_monitors(bool fullhelp) { if (!fullhelp) { cout << "(use \"fullhelp\" to see modes)\n"; } + printf("\n"); +} + +static void +gl_print_platforms() +{ + printf("available platforms:\n"); + for (unsigned i = 0; i < ARR_COUNT(platform_map); ++i) { + if (glfwPlatformSupported(platform_map[i].platform_id)) { + color_printf("\t- " TBOLD("%s") "\n", platform_map[i].name); + } + } + color_printf("\n"); +} + +static void +gl_print_current_platform() +{ + const int platform = glfwGetPlatform(); + const char *name = "UNKNOWN/ERROR"; + for (unsigned i = 0; i < ARR_COUNT(platform_map); ++i) { + if (platform_map[i].platform_id == platform) { + name = platform_map[i].name; + break; + } + } +#ifdef __linux__ + int ll = LOG_LEVEL_NOTICE; +#else + int ll = LOG_LEVEL_VERBOSE; +#endif + log_msg(ll, MOD_NAME "Using platform: %s\n", name); } #define FEATURE_PRESENT(x) (strcmp(STRINGIFY(x), "1") == 0 ? "on" : "off") @@ -590,6 +633,7 @@ static void gl_show_help(bool full) { col() << TBOLD("\t--param " GL_DISABLE_10B_OPT_PARAM_NAME) << "\tdo not set 10-bit framebuffer (performance issues)\n"; col() << "\n" TBOLD( "[1]") " position doesn't work in Wayland\n"; + col() << TBOLD("\tplatform=

") << "\tuse platform (usable only in Linux)\n"; } else { color_printf( "\t(use \"" TBOLD("fullhelp") "\" to see options)\n"); @@ -607,7 +651,9 @@ static void gl_show_help(bool full) { return; } gl_print_monitors(full); - color_printf("\n"); + if (full) { + gl_print_platforms(); + } GLFWwindow *window = glfwCreateWindow(32, 32, DEFAULT_WIN_NAME, nullptr, nullptr); if (window != nullptr) { glfwMakeContextCurrent(window); @@ -704,6 +750,19 @@ parse_hints(struct state_gl *s, bool window_hints, char *hints) } } +static bool +set_platform(struct state_gl *s, const char *platform) +{ + for (unsigned i = 0; i < ARR_COUNT(platform_map); ++i) { + if (strcasecmp(platform_map[i].name, platform) == 0) { + s->init_hints[GLFW_PLATFORM] = platform_map[i].platform_id; + return true; + } + } + MSG(ERROR, "Unknown platform: %s\n", platform); + return false; +} + static void list_hints() { @@ -721,7 +780,7 @@ list_hints() ").\n\n"); - color_printf("Available hints keys and values:\n"); + color_printf("Some of hints keys and values:\n"); for (const auto &h : hint_map) { color_printf("\t" TBOLD("%s") " - %#x\n", h.first.c_str(), h.second); @@ -735,6 +794,7 @@ list_hints() static bool display_gl_parse_fmt(struct state_gl *s, char *ptr) { + bool ret = true; char *tok, *save_ptr = NULL; while((tok = strtok_r(ptr, ":", &save_ptr)) != NULL) { @@ -799,9 +859,7 @@ display_gl_parse_fmt(struct state_gl *s, char *ptr) s->use_pbo = strcasecmp(tok, "pbo") == 0 ? 1 : 0; } else if (strstr(tok, "size=") == tok || strstr(tok, "fixed_size=") == tok) { - if (!set_size(s, tok)) { - return false; - } + ret = ret && set_size(s, tok); } else if (strcmp(tok, "fixed_size") == 0) { s->fixed_size = true; } else if (strcmp(tok, "noresizable") == 0) { @@ -810,6 +868,8 @@ display_gl_parse_fmt(struct state_gl *s, char *ptr) parse_hints(s, true, strchr(tok, '=') + 1); } else if (strstr(tok, "init_hint=") == tok) { parse_hints(s, false, strchr(tok, '=') + 1); + } else if (IS_KEY_PREFIX(tok, "platform")) { + ret = ret && set_platform(s, strchr(tok, '=') + 1); } else if (strcmp(tok, "list_hints") == 0) { list_hints(); return false; @@ -820,7 +880,7 @@ display_gl_parse_fmt(struct state_gl *s, char *ptr) ptr = NULL; } - return true; + return ret; } static void * display_gl_init(struct module *parent, const char *fmt, unsigned int flags) { @@ -1714,6 +1774,8 @@ static bool display_gl_init_opengl(struct state_gl *s) return false; } + gl_print_current_platform(); + if (s->req_monitor_idx != -1) { int count = 0; GLFWmonitor **mon = glfwGetMonitors(&count); From bddb5054f2af4f4a6d36fa1a46641626ed103423 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 28 Aug 2025 10:41:02 +0200 Subject: [PATCH 080/100] vdisp/gl: print the no-GLX error only as verbose the Wayland case --- src/video_display/gl.cpp | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/video_display/gl.cpp b/src/video_display/gl.cpp index b6294943a..13931f5b7 100644 --- a/src/video_display/gl.cpp +++ b/src/video_display/gl.cpp @@ -1604,14 +1604,28 @@ static void display_gl_render_last(GLFWwindow *win) { #ifndef GLEW_ERROR_NO_GLX_DISPLAY #define GLEW_ERROR_NO_GLX_DISPLAY 4 #endif -static const char *glewGetError(GLenum err) { +static void printGlewError(GLenum err) { + const char *err_str = nullptr; switch (err) { - case GLEW_ERROR_NO_GL_VERSION: return "missing GL version"; - case GLEW_ERROR_GL_VERSION_10_ONLY: return "Need at least OpenGL 1.1"; - case GLEW_ERROR_GLX_VERSION_11_ONLY: return "Need at least GLX 1.2"; - case GLEW_ERROR_NO_GLX_DISPLAY: return "Need GLX display for GLX support"; - default: return (const char *) glewGetErrorString(err); + case GLEW_ERROR_NO_GL_VERSION: + err_str = "missing GL version"; + break; + case GLEW_ERROR_GL_VERSION_10_ONLY: + err_str = "Need at least OpenGL 1.1"; + break; + case GLEW_ERROR_GLX_VERSION_11_ONLY: + err_str = "Need at least GLX 1.2"; + break; + case GLEW_ERROR_NO_GLX_DISPLAY: + err_str = "Need GLX display for GLX support"; + break; + default: + err_str = (const char *) glewGetErrorString(err); + break; } + log_msg(err == GLEW_ERROR_NO_GLX_DISPLAY ? LOG_LEVEL_VERBOSE + : LOG_LEVEL_ERROR, + MOD_NAME "GLEW Error: %s (err %d)\n", err_str, err); } #endif // defined GLEW_VERSION @@ -1844,7 +1858,7 @@ static bool display_gl_init_opengl(struct state_gl *s) #if defined GLEW_VERSION if (GLenum err = glewInit()) { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "GLEW Error: %s (err %d)\n", glewGetError(err), err); + printGlewError(err); if (err != GLEW_ERROR_NO_GLX_DISPLAY) { // do not fail on error 4 (on Wayland), which can be suppressed return false; } From 7ac13a3032f3c5571cd787ce6c9d8302fd5beb6c Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 28 Aug 2025 14:10:05 +0200 Subject: [PATCH 081/100] vdisp/gl: old GLFW compat --- src/video_display/gl.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/video_display/gl.cpp b/src/video_display/gl.cpp index 13931f5b7..53b51cf48 100644 --- a/src/video_display/gl.cpp +++ b/src/video_display/gl.cpp @@ -366,6 +366,7 @@ static constexpr pair keybindings[] = { pair{K_CTRL_UP, "make window 10% bigger"} }; +#ifdef GLFW_PLATFORM const static struct { int platform_id; const char *name; @@ -375,6 +376,7 @@ const static struct { { GLFW_PLATFORM_WAYLAND, "Wayland" }, { GLFW_PLATFORM_X11, "X11" }, }; +#endif // defined GLFW_PLATFORM /* Prototyping */ static bool check_display_gl_version(bool print_ver); @@ -542,6 +544,7 @@ static void gl_print_monitors(bool fullhelp) { printf("\n"); } +#ifdef GLFW_PLATFORM static void gl_print_platforms() { @@ -572,6 +575,18 @@ gl_print_current_platform() #endif log_msg(ll, MOD_NAME "Using platform: %s\n", name); } +#else // mot defined GLFW_PLATFORM +static void +gl_print_platforms() +{ + MSG(ERROR, "platforms unsupported (old GLFW)\n"); +} +// NOOP +static void +gl_print_current_platform() +{ +} +#endif // not defined GLFW_PLATFORM #define FEATURE_PRESENT(x) (strcmp(STRINGIFY(x), "1") == 0 ? "on" : "off") @@ -753,6 +768,7 @@ parse_hints(struct state_gl *s, bool window_hints, char *hints) static bool set_platform(struct state_gl *s, const char *platform) { +#ifdef GLFW_PLATFORM for (unsigned i = 0; i < ARR_COUNT(platform_map); ++i) { if (strcasecmp(platform_map[i].name, platform) == 0) { s->init_hints[GLFW_PLATFORM] = platform_map[i].platform_id; @@ -761,6 +777,11 @@ set_platform(struct state_gl *s, const char *platform) } MSG(ERROR, "Unknown platform: %s\n", platform); return false; +#else + (void) s, (void) platform; + MSG(ERROR, "platforms unsupported (old GLFW)\n"); + return false; +#endif } static void From b0739571ce8a08503ea48fd90cf46f5a23ba684b Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 28 Aug 2025 16:11:23 +0200 Subject: [PATCH 082/100] lib_common: new class for module visibility Add MODULE_FLAG_ALIAS (+ complete API) for modules that are not hidden as technical/deprecated moudles but rather an alias. The difference is important for GUI not to print aliased module twice. --- src/lib_common.cpp | 32 +++++++++++++++++++++++--------- src/lib_common.h | 41 ++++++++++++++++++++++++++++------------- 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/lib_common.cpp b/src/lib_common.cpp index 60fb1cba5..b4fc13f87 100644 --- a/src/lib_common.cpp +++ b/src/lib_common.cpp @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2012-2023 CESNET, z. s. p. o. + * Copyright (c) 2012-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -215,7 +215,7 @@ void open_all(const char *pattern, list &libs) { struct lib_info { const void *data; int abi_version; - bool hidden; + unsigned visibility_flag; }; // http://stackoverflow.com/questions/1801892/making-mapfind-operation-case-insensitive @@ -247,13 +247,22 @@ static auto& get_libmap(){ return libraries; } -void register_library(const char *name, const void *data, enum library_class cls, int abi_version, int hidden) +/** + * @param name module name (used in listing and on cmdline for spec) + * @param cls module class + * @param name class-specific metadata structure (with callbacks) + * @param abi_version class-specific ABI version + * @param visibility_flag usually 0 or flag from @ref enum module_flag + */ +void +register_library(const char *name, const void *info, enum library_class cls, + int abi_version, unsigned visibility_flag) { auto& map = get_libmap()[cls]; if (map.find(name) != map.end()) { LOG(LOG_LEVEL_ERROR) << "Module \"" << name << "\" (class " << cls << ") multiple initialization!\n"; } - map[name] = {data, abi_version, static_cast(hidden)}; + map[name] = {info, abi_version, visibility_flag}; } const void *load_library(const char *name, enum library_class cls, int abi_version) @@ -293,10 +302,12 @@ const void *load_library(const char *name, enum library_class cls, int abi_versi /** * Prints list of modules of given class - * @param full include hidden modules + * @param full include hidden modules and aliases */ void list_modules(enum library_class cls, int abi_version, bool full) { - const auto & class_set = get_libraries_for_class(cls, abi_version, full); + const auto &class_set = get_libraries_for_class( + cls, abi_version, + full ? MODULE_SHOW_ALL : MODULE_SHOW_VISIBLE_ONLY); for (auto && item : class_set) { col() << "\t" << SBOLD(item.first.c_str()) << "\n"; } @@ -333,7 +344,9 @@ bool list_all_modules() { return ret; } -map get_libraries_for_class(enum library_class cls, int abi_version, bool include_hidden) +map +get_libraries_for_class(enum library_class cls, int abi_version, + unsigned include_flags) { map ret; auto& libraries = get_libmap(); @@ -341,7 +354,9 @@ map get_libraries_for_class(enum library_class cls, int ab if (it != libraries.end()) { for (auto && item : it->second) { if (abi_version == item.second.abi_version) { - if (include_hidden || !item.second.hidden) { + if (item.second.visibility_flag == 0 || + (include_flags & + item.second.visibility_flag) != 0U) { ret[item.first] = item.second.data; } } else { @@ -354,4 +369,3 @@ map get_libraries_for_class(enum library_class cls, int ab return ret; } - diff --git a/src/lib_common.h b/src/lib_common.h index b73556347..5cc326dc4 100644 --- a/src/lib_common.h +++ b/src/lib_common.h @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2011-2023 CESNET, z. s. p. o. + * Copyright (c) 2011-2025 CESNET * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -85,45 +85,59 @@ enum library_class { LIBRARY_CLASS_AUDIO_FILTER, }; const void *load_library(const char *name, enum library_class, int abi_version); -void register_library(const char *name, const void *info, enum library_class, int abi_version, int hidden); +void register_library(const char *name, const void *info, enum library_class, + int abi_version, unsigned visibility_flag); void list_modules(enum library_class, int abi_version, bool full); bool list_all_modules(); #ifdef __cplusplus } #endif +enum module_flag { + MODULE_SHOW_VISIBLE_ONLY = 0, ///< display only modules w/o flag + MODULE_FLAG_HIDDEN = 1 << 0, ///< flag - do not show in listing + MODULE_FLAG_ALIAS = + 1 << 1, ///< ditto + hide for GUI, for explicit init only + MODULE_SHOW_ALL = + MODULE_FLAG_HIDDEN | MODULE_FLAG_ALIAS, ///< display all modules +}; + #ifdef __cplusplus #include #include -std::map get_libraries_for_class(enum library_class cls, int abi_version, bool include_hidden = true); +std::map +get_libraries_for_class(enum library_class cls, int abi_version, + unsigned include_flags = MODULE_FLAG_HIDDEN); #endif /** * Placeholder that installs module via constructor for every macro - * REGISTER_MODULE/REGISTER_MODULE_HIDDEN call + * REGISTER_MODULE* call * @param name non-quoted module name * @param lclass class of the module * @param abi abi version (specific for every class) * @param funcname unique function name that will be used to register * the module (as a constructor) - * @param hidden 0/1 - whether the module should be visible by eg. '-c help' - * (for technical and deprecated modules), default true + * @param flag optional flag to limit visibility (@ref module_flag; + * for technical, deprecated modules and aliases), default 0 */ -#define REGISTER_MODULE_FUNCNAME(name, info, lclass, abi, funcname, hidden) static void funcname(void) __attribute__((constructor));\ +#define REGISTER_MODULE_FUNCNAME(name, info, lclass, abi, funcname, flag) \ + static void funcname(void) __attribute__((constructor)); \ \ static void funcname(void)\ {\ - register_library(#name, info, lclass, abi, hidden);\ + register_library(#name, info, lclass, abi, flag);\ }\ struct NOT_DEFINED_STRUCT_THAT_SWALLOWS_SEMICOLON -#define REGISTER_MODULE_FUNC_FUNCNAME(name, func, lclass, abi, funcname, hidden) static void funcname(void) __attribute__((constructor));\ +#define REGISTER_MODULE_FUNC_FUNCNAME(name, func, lclass, abi, funcname, flag) \ + static void funcname(void) __attribute__((constructor)); \ \ static void funcname(void)\ {\ const void *info = func();\ if (info) {\ - register_library(#name, info, lclass, abi, hidden);\ + register_library(#name, info, lclass, abi, flag);\ }\ }\ struct NOT_DEFINED_STRUCT_THAT_SWALLOWS_SEMICOLON @@ -150,10 +164,11 @@ struct NOT_DEFINED_STRUCT_THAT_SWALLOWS_SEMICOLON #define REGISTER_MODULE_WITH_FUNC(name, func, lclass, abi) REGISTER_MODULE_FUNC_FUNCNAME(name, func, lclass, abi, UNIQUE_LABEL, 0) /** - * Similar to @ref REGISTER_MODULE but do not show the module under help - * of corresponding class (usable for technical or deprecated modules). + * Similar to @ref REGISTER_MODULE but allow @ref module_flag to be added to limit visibility. */ -#define REGISTER_HIDDEN_MODULE(name, info, lclass, abi) REGISTER_MODULE_FUNCNAME(name, info, lclass, abi, UNIQUE_LABEL, 1) +#define REGISTER_MODULE_WITH_FLAG(name, info, lclass, abi, flag) \ + REGISTER_MODULE_FUNCNAME(name, info, lclass, abi, UNIQUE_LABEL, flag) +#define REGISTER_HIDDEN_MODULE(name, info, lclass, abi) REGISTER_MODULE_WITH_FLAG(name, info, lclass, abi, MODULE_FLAG_HIDDEN) #endif // defined LIB_COMMON_H From 9bad1143679ddbffa6f4c678ee974d839ad8ecee Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 28 Aug 2025 16:15:54 +0200 Subject: [PATCH 083/100] vdisp/vulkan_sdl{2,3}: register aliases Switch hidden to alias - makes a difference for GUI, see also the previous commit. --- src/video_display/sdl2.c | 4 ++-- src/video_display/sdl3.c | 4 ++-- src/video_display/vulkan/vulkan_sdl2.cpp | 4 +++- src/video_display/vulkan/vulkan_sdl3.cpp | 5 +++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/video_display/sdl2.c b/src/video_display/sdl2.c index fa71938f7..f818b061c 100644 --- a/src/video_display/sdl2.c +++ b/src/video_display/sdl2.c @@ -999,5 +999,5 @@ static const struct video_display_info display_sdl2_info = { }; REGISTER_MODULE(sdl, &display_sdl2_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); -REGISTER_HIDDEN_MODULE(sdl2, &display_sdl2_info, LIBRARY_CLASS_VIDEO_DISPLAY, - VIDEO_DISPLAY_ABI_VERSION); +REGISTER_MODULE_WITH_FLAG(sdl2, &display_sdl2_info, LIBRARY_CLASS_VIDEO_DISPLAY, + VIDEO_DISPLAY_ABI_VERSION, MODULE_FLAG_ALIAS); diff --git a/src/video_display/sdl3.c b/src/video_display/sdl3.c index ce129ecff..f8cb3f252 100644 --- a/src/video_display/sdl3.c +++ b/src/video_display/sdl3.c @@ -1395,5 +1395,5 @@ static const struct video_display_info display_sdl3_info = { REGISTER_MODULE(sdl, &display_sdl3_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); -REGISTER_HIDDEN_MODULE(sdl3, &display_sdl3_info, LIBRARY_CLASS_VIDEO_DISPLAY, - VIDEO_DISPLAY_ABI_VERSION); +REGISTER_MODULE_WITH_FLAG(sdl3, &display_sdl3_info, LIBRARY_CLASS_VIDEO_DISPLAY, + VIDEO_DISPLAY_ABI_VERSION, MODULE_FLAG_ALIAS); diff --git a/src/video_display/vulkan/vulkan_sdl2.cpp b/src/video_display/vulkan/vulkan_sdl2.cpp index eca64ddc5..36a096591 100644 --- a/src/video_display/vulkan/vulkan_sdl2.cpp +++ b/src/video_display/vulkan/vulkan_sdl2.cpp @@ -986,5 +986,7 @@ const video_display_info display_vulkan_info = { REGISTER_MODULE(vulkan_sdl2, &display_vulkan_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); -REGISTER_HIDDEN_MODULE(vulkan, &display_vulkan_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); +REGISTER_MODULE_WITH_FLAG(vulkan, &display_vulkan_info, + LIBRARY_CLASS_VIDEO_DISPLAY, + VIDEO_DISPLAY_ABI_VERSION, MODULE_FLAG_ALIAS); diff --git a/src/video_display/vulkan/vulkan_sdl3.cpp b/src/video_display/vulkan/vulkan_sdl3.cpp index 07fe68d29..e8219ed5c 100644 --- a/src/video_display/vulkan/vulkan_sdl3.cpp +++ b/src/video_display/vulkan/vulkan_sdl3.cpp @@ -999,5 +999,6 @@ const video_display_info display_vulkan_info = { REGISTER_MODULE(vulkan, &display_vulkan_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); -REGISTER_HIDDEN_MODULE(vulkan_sdl3, &display_vulkan_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); - +REGISTER_MODULE_WITH_FLAG(vulkan_sdl3, &display_vulkan_info, + LIBRARY_CLASS_VIDEO_DISPLAY, + VIDEO_DISPLAY_ABI_VERSION, MODULE_FLAG_ALIAS); From 2a34727449bb974def80fdb3f5502007e1ab104d Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Fri, 29 Aug 2025 12:01:56 +0200 Subject: [PATCH 084/100] excludelist.local.x86: disable libgdk_pixbuf-2.0.so.0 commented out libgdk_pixbuf-2.0.so.0 Normal build no longer runs on Rocky Linux 9 but alternative build (built on centos 7) does. But conversly excluding the files causes dependency problems for vcap/file vcomp/lavc, vdisp.file, vdisp/gl. Tested on Alma 9.6, which shuld be basically the same as Rocky. After re-adding the problem disappears. --- data/scripts/Linux-AppImage/excludelist.local.x86 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/scripts/Linux-AppImage/excludelist.local.x86 b/data/scripts/Linux-AppImage/excludelist.local.x86 index 851a1f9e3..678490fdc 100644 --- a/data/scripts/Linux-AppImage/excludelist.local.x86 +++ b/data/scripts/Linux-AppImage/excludelist.local.x86 @@ -1,4 +1,4 @@ # Causes crash on U22.04 (see https://github.com/CESNET/UltraGrid/releases/download/v1.7/UltraGrid-1.7-x86_64.AppImage) libgdk-3.so.0 # Rocky Linux 9 -libgdk_pixbuf-2.0.so.0 +# libgdk_pixbuf-2.0.so.0 From 2ae0eee1909d5a5098281b81c005a2742453f997 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Fri, 29 Aug 2025 12:13:23 +0200 Subject: [PATCH 085/100] excludelist.local.x86: disable also libgdk-3.so.0 Not tested but trivially, libgdk-3.so.0 from (build) Ubuntu 22.04 should not be incompatible when running on the same distro. This is not known to cause issues but better to remove this since it the original use case seem no longer being valid. Track just the excludelist from AppImage creator. --- data/scripts/Linux-AppImage/excludelist.local.x86 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/scripts/Linux-AppImage/excludelist.local.x86 b/data/scripts/Linux-AppImage/excludelist.local.x86 index 678490fdc..783418a50 100644 --- a/data/scripts/Linux-AppImage/excludelist.local.x86 +++ b/data/scripts/Linux-AppImage/excludelist.local.x86 @@ -1,4 +1,4 @@ # Causes crash on U22.04 (see https://github.com/CESNET/UltraGrid/releases/download/v1.7/UltraGrid-1.7-x86_64.AppImage) -libgdk-3.so.0 +# libgdk-3.so.0 # Rocky Linux 9 # libgdk_pixbuf-2.0.so.0 From 517f47ec7e563e72add24b891b195f9104f1efcb Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Fri, 29 Aug 2025 12:33:07 +0200 Subject: [PATCH 086/100] create_appimage.sh: fix missing $ The excludelist was not applied for arm64 (used x86 instead)? Currently excluded was just ld-linux-aarch64.so.1 but it may cause problems... --- data/scripts/Linux-AppImage/create-appimage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/scripts/Linux-AppImage/create-appimage.sh b/data/scripts/Linux-AppImage/create-appimage.sh index 6b2ea3444..ce14ab000 100755 --- a/data/scripts/Linux-AppImage/create-appimage.sh +++ b/data/scripts/Linux-AppImage/create-appimage.sh @@ -136,7 +136,7 @@ fi DIRNAME=$(dirname "$0") uname_m=$(uname -m) excl_list_arch=x86 -if expr "$uname_m" : arm >/dev/null || expr "uname_m" : aarch64 > /dev/null; then +if expr "$uname_m" : arm >/dev/null || expr "$uname_m" : aarch64 > /dev/null; then excl_list_arch=arm fi cat "$DIRNAME/excludelist.local.$excl_list_arch" >> excludelist From d473bc00d0036af6a05297d81d1e0dbcf6359b55 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 1 Sep 2025 08:45:14 +0200 Subject: [PATCH 087/100] CI Linux: fix wrong token name for cache not a big problem - hash of appropriate fie included in the key --- .github/workflows/ccpp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index d1f6b29b9..d8c5cd02c 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -89,7 +89,7 @@ jobs: uses: actions/cache@main with: path: '/var/tmp/glfw' - key: cache-sdl-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_glfw.sh' ) }} + key: cache-glfw-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_glfw.sh' ) }} - name: bootstrap run: | . .github/scripts/environment.sh From 44788b71ce0ae3d925e892aaf84a0f554d5db8fe Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 1 Sep 2025 09:17:27 +0200 Subject: [PATCH 088/100] CI Linux install_sdl.sh: ensure features ensure required features explicitly on --- .github/scripts/Linux/install_sdl.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/scripts/Linux/install_sdl.sh b/.github/scripts/Linux/install_sdl.sh index 02ce10a10..40ef6b586 100755 --- a/.github/scripts/Linux/install_sdl.sh +++ b/.github/scripts/Linux/install_sdl.sh @@ -6,6 +6,11 @@ dir=$(dirname "$0") # build dir that will be restored from cache cache_dir=/var/tmp/sdl +features="-DSDL_KMSDRM=ON\ + -DSDL_OPENGL=ON\ + -DSDL_VULKAN=ON\ + -DSDL_WAYLAND=ON\ + -DSDL_X11=ON" # install the deps - runs always (regardless the cache) deps() { @@ -33,7 +38,8 @@ build_install() { git clone --recurse-submodules --depth 1\ https://github.com/Fluidsynth/fluidsynth - cmake -S fluidsynth -B fluidsynth/build + # shellcheck disable=SC2086 # intentional + cmake $features -S fluidsynth -B fluidsynth/build cmake --build fluidsynth/build -j "$(nproc)" sudo cmake --install fluidsynth/build } From 21697720638934497e1a64e41d091052cb25082c Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 1 Sep 2025 09:17:51 +0200 Subject: [PATCH 089/100] remove sdl_mixer should be replaced altogether with fluidsynth - add alias there + removed typo in fluidsynth pkg name ARM CI bootstrap.sh --- .github/scripts/Linux/arm/bootstrap.sh | 2 +- configure.ac | 32 +-- src/audio/capture/fluidsynth.c | 2 + src/audio/capture/sdl_mixer.c | 342 ------------------------- 4 files changed, 4 insertions(+), 374 deletions(-) delete mode 100644 src/audio/capture/sdl_mixer.c diff --git a/.github/scripts/Linux/arm/bootstrap.sh b/.github/scripts/Linux/arm/bootstrap.sh index 699825534..1b6eed51c 100755 --- a/.github/scripts/Linux/arm/bootstrap.sh +++ b/.github/scripts/Linux/arm/bootstrap.sh @@ -31,7 +31,7 @@ apt -y install libavcodec-dev libavformat-dev libswscale-dev libraspberrypi-dev apt -y install \ cmake \ libdrm-dev\ - libfluidsynth-devl\ + libfluidsynth-dev\ libsdl2-dev libsdl2-ttf-dev\ libva-dev\ libvulkan-dev\ diff --git a/configure.ac b/configure.ac index c84fa7a83..1be402af7 100644 --- a/configure.ac +++ b/configure.ac @@ -3407,37 +3407,7 @@ fi ENSURE_FEATURE_PRESENT([$pcp_req], [$pcp], [PCP not found]) # --------------------------------------------------------------------- -# SDL_mixer audio capture -# --------------------------------------------------------------------- -AC_ARG_ENABLE(sdl_mixer, - AS_HELP_STRING([--disable-sdl_mixer], [disable SDL_mixer audio capture (default is auto)] - [Requires: SDL[2]_mixer]), - [sdl_mixer_req=$enableval], - [sdl_mixer_req=$build_default]) -found_sdl_mixer=no -sdl_mixer=no - -if test $sdl = yes && test $sdl_mixer_req != no -then - if test "$sdl_version" -lt 3; then - PKG_CHECK_MODULES([SDL_MIXER], [SDL${sdl_ver_suffix}_mixer], - [found_sdl_mixer=yes], [found_sdl_mixer=no]) - elif test "$sdl_mixer_req" = yes; then - AC_MSG_ERROR([SDL_mixer 3 no longer supports MIDI playback so it can be no longer used!]); - fi - if test "$found_sdl_mixer" = yes; then - SDL_MIXER_LIBS=$(remove_mwindows "$SDL_MIXER_LIBS") - SDL_MIXER_CFLAGS=$($PKG_CONFIG --cflags-only-I SDL${sdl_ver_suffix}_mixer) - add_module acap_sdl_mixer src/audio/capture/sdl_mixer.o "$SDL_MIXER_LIBS" - INC="$INC${SDL_MIXER_CFLAGS:+ $SDL_MIXER_CFLAGS}" - sdl_mixer=yes - fi -fi - -ENSURE_FEATURE_PRESENT([$sdl_mixer_req], [$sdl_mixer], [SDL_mixer deps not found!]) - -# --------------------------------------------------------------------- -# fluidsynt audio capture synthesizer +# fluidsynth audio capture synthesizer # --------------------------------------------------------------------- AC_ARG_ENABLE(fluidsynth, AS_HELP_STRING([--disable-fluidsynth], diff --git a/src/audio/capture/fluidsynth.c b/src/audio/capture/fluidsynth.c index e63bf909e..c0ecbe1a4 100644 --- a/src/audio/capture/fluidsynth.c +++ b/src/audio/capture/fluidsynth.c @@ -358,3 +358,5 @@ static const struct audio_capture_info acap_fluidsynth_info = { REGISTER_MODULE(fluidsynth, &acap_fluidsynth_info, LIBRARY_CLASS_AUDIO_CAPTURE, AUDIO_CAPTURE_ABI_VERSION); +REGISTER_MODULE_WITH_FLAG(sdl_mixer, &acap_fluidsynth_info, LIBRARY_CLASS_AUDIO_CAPTURE, + AUDIO_CAPTURE_ABI_VERSION, MODULE_FLAG_ALIAS); diff --git a/src/audio/capture/sdl_mixer.c b/src/audio/capture/sdl_mixer.c deleted file mode 100644 index b43c26c35..000000000 --- a/src/audio/capture/sdl_mixer.c +++ /dev/null @@ -1,342 +0,0 @@ -/** - * @file audio/capture/sdl_mixer.c - * @author Martin Pulec - */ -/* - * Copyright (c) 2011-2025 CESNET - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, is permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of CESNET nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/** - * @file - * @todo errata (SDL3 vs SDL2) - * 1. 1 channel capture (-a ch=1) seem no longer work but there is a workaround - * 2. insufficient performance (generates overflow even in default config) - */ - -#include "config.h" // for HAVE_SDL3 - -#ifdef HAVE_SDL3 -#include // for SDL_Init, SDL_INIT_AUDIO -#include // for AUDIO_S16LSB, AUDIO_S32LSB, AUDIO_S8 -#include // for MIX_MAX_VOLUME, Mix_GetError, Mix_C... -#else -#include // for SDL_Init, SDL_INIT_AUDIO -#include // for AUDIO_S16LSB, AUDIO_S32LSB, AUDIO_S8 -#include // for MIX_MAX_VOLUME, Mix_GetError, Mix_C... -#endif -#include // for NULL, fclose, fopen, size_t, FILE -#include // for free, calloc, getenv, atoi, malloc -#include // for strlen, strncat, strchr, strcmp -#include // for unlink - -#include "audio/audio_capture.h" -#include "audio/types.h" -#include "debug.h" -#include "host.h" -#include "lib_common.h" -#include "song1.h" -#include "types.h" -#include "utils/color_out.h" -#include "utils/fs.h" -#include "utils/macros.h" -#include "utils/ring_buffer.h" - -#define DEFAULT_SDL_MIXER_BPS 2 -#define DEFAULT_MIX_MAX_VOLUME (MIX_MAX_VOLUME / 4) -#define SDL_MIXER_SAMPLE_RATE 48000 -#define MOD_NAME "[SDL_mixer] " - -#ifdef HAVE_SDL3 -#define Mix_GetError SDL_GetError -#define SDL_ERR false -#else -#define SDL_AUDIO_S8 AUDIO_S8 -#define SDL_AUDIO_S16LE AUDIO_S16LSB -#define SDL_AUDIO_S32LE AUDIO_S32LSB -#define SDL_ERR (-1) -#endif - -struct state_sdl_mixer_capture { - Mix_Music *music; - struct audio_frame audio; - struct ring_buffer *sdl_mixer_buf; - int volume; - char *req_filename; -}; - -static void audio_cap_sdl_mixer_done(void *state); - -static void audio_cap_sdl_mixer_probe(struct device_info **available_devices, int *count, void (**deleter)(void *)) -{ - *deleter = free; - *count = 1; - *available_devices = calloc(1, sizeof **available_devices); - strncat((*available_devices)[0].dev, "sdl_mixer", sizeof (*available_devices)[0].dev - 1); - strncat((*available_devices)[0].name, "Sample midi song", sizeof (*available_devices)[0].name - 1); -} - -static void sdl_mixer_audio_callback(int chan, void *stream, int len, void *udata) -{ - UNUSED(chan); - struct state_sdl_mixer_capture *s = udata; - - ring_buffer_write(s->sdl_mixer_buf, stream, len); - memset(stream, 0, len); // do not playback anything to PC output -} - -static int parse_opts(struct state_sdl_mixer_capture *s, char *cfg) { - char *save_ptr = NULL; - char *item = NULL; - while ((item = strtok_r(cfg, ":", &save_ptr)) != NULL) { - cfg = NULL; - if (strcmp(item, "help") == 0) { - color_printf(TBOLD("sdl_mixer") " is a capture device capable playing various audio files like FLAC,\n" - "MIDI, mp3, Vorbis or WAV.\n\n" - "The main functional difference to " TBOLD("file") " video capture (that is able to play audio\n" - "files as well) is the support for " TBOLD("MIDI") " (and also having one song bundled).\n\n"); - color_printf("Usage:\n"); - color_printf(TBOLD(TRED("\t-s sdl_mixer") "[:file=][:volume=]") "\n"); - color_printf("where\n"); - color_printf(TBOLD("\t") " - name of file to be used\n"); - color_printf(TBOLD("\t ") " - volume [0..%d], default %d\n", MIX_MAX_VOLUME, DEFAULT_MIX_MAX_VOLUME); - color_printf("\n"); - color_printf(TBOLD("SDL_SOUNDFONTS") " - environment variable with path to sound fonts for MIDI playback (eg. freepats)\n"); - color_printf(TBOLD("ULTRAGRID_BUNDLED_SF") " - set this environment variable to 1 to skip loading system default sound font\n\n"); - return 1; - } - if (strstr(item, "file=") == item) { - s->req_filename = strdup(strchr(item, '=') + 1); - } else if (strstr(item, "volume=") == item) { - s->volume = atoi(strchr(item, '=') + 1); - } else { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong option: %s!\n", item); - color_printf("Use " TBOLD("-s sdl_mixer:help") " to see available options.\n"); - return -1; - } - } - return 0; -} - -static const char *load_song1() { - const char *filename = NULL; - FILE *f = get_temp_file(&filename); - if (f == NULL) { - perror("fopen audio"); - return NULL; - } - size_t nwritten = fwrite(song1, sizeof song1, 1, f); - fclose(f); - if (nwritten != 1) { - unlink(filename); - return NULL; - } - return filename; -} - -/** - * Try to preload a sound font. - * - * This is mainly intended to allow loading sound fonts from application bundle - * on various platforms (get_install_root is relative to executable). But use - * to system default font, if available. - */ -static void try_open_soundfont() { - const _Bool force_bundled_sf = getenv("ULTRAGRID_BUNDLED_SF") != NULL && strcmp(getenv("ULTRAGRID_BUNDLED_SF"), "1") == 0; - if (!force_bundled_sf) { - const char *default_soundfont = Mix_GetSoundFonts(); - if (default_soundfont) { - FILE *f = fopen(default_soundfont, "rb"); - if (f) { - debug_msg(MOD_NAME "Default sound font '%s' seems usable, not trying to load additional fonts.\n", default_soundfont); - fclose(f); - return; - } - } - debug_msg(MOD_NAME "Unable to open default sound font '%s'\n", default_soundfont ? default_soundfont : "(no font)"); - } - const char *roots[] = { get_install_root(), "/usr" }; - const char *sf_candidates[] = { // without install prefix - "/share/soundfonts/default.sf2", "/share/soundfonts/default.sf3", - "/share/sounds/sf2/default-GM.sf2", "/share/sounds/sf3/default-GM.sf3", // Ubuntu - }; - for (size_t i = 0; i < sizeof roots / sizeof roots[0]; ++i) { - for (size_t j = 0; j < sizeof sf_candidates / sizeof sf_candidates[0]; ++j) { - const char *root = roots[i]; - const size_t len = strlen(root) + strlen(sf_candidates[j]) + 1; - char path[len]; - strncpy(path, root, len - 1); - strncat(path, sf_candidates[j], len - strlen(path) - 1); - FILE *f = fopen(path, "rb"); - debug_msg(MOD_NAME "Trying to open sound font '%s': %s\n", path, f ? "success, setting" : "failed"); - if (!f) { - continue; - } - fclose(f); - Mix_SetSoundFonts(path); - return; - } - } -} - -/// handle SDL 3.0.0 mixer not being able to capture mono -static void -adjust_ch_count(struct state_sdl_mixer_capture *s) -{ - int frequency = 0; - SDL_AudioFormat format = { 0 }; - int channels = 0; - Mix_QuerySpec(&frequency, &format, &channels); - if (audio_capture_channels > 0 && - channels != (int) audio_capture_channels) { - MSG(WARNING, - "%d channel capture seem to be broken with SDL3 mixer - " - "capturing %d channels and dropping the excessive later.\n", - s->audio.ch_count, channels); - s->audio.ch_count = channels; - } -} - -static void * audio_cap_sdl_mixer_init(struct module *parent, const char *cfg) -{ - MSG(WARNING, "SDL_mixer is deprecated, used fluidsynth...\n"); - UNUSED(parent); - SDL_Init(SDL_INIT_AUDIO); - - struct state_sdl_mixer_capture *s = calloc(1, sizeof *s); - s->volume = DEFAULT_MIX_MAX_VOLUME; - char *ccfg = strdup(cfg); - int ret = parse_opts(s, ccfg); - free(ccfg); - if (ret != 0) { - audio_cap_sdl_mixer_done(s); - return ret < 0 ? NULL : INIT_NOERR; - } - - s->audio.bps = audio_capture_bps ? audio_capture_bps : DEFAULT_SDL_MIXER_BPS; - s->audio.ch_count = audio_capture_channels > 0 ? audio_capture_channels - : MIX_DEFAULT_CHANNELS; - s->audio.sample_rate = SDL_MIXER_SAMPLE_RATE; - - int audio_format = 0; - switch (s->audio.bps) { - case 1: audio_format = SDL_AUDIO_S8; break; - case 2: audio_format = SDL_AUDIO_S16LE; break; - case 4: audio_format = SDL_AUDIO_S32LE; break; - default: UG_ASSERT(0 && "BPS can be only 1, 2 or 4"); - } - -#ifdef HAVE_SDL3 - SDL_AudioSpec spec = { - .format = audio_format, - .channels = s->audio.ch_count, - .freq = s->audio.sample_rate, - }; - if (!Mix_OpenAudio(0, &spec)) { -#else - if( Mix_OpenAudio(SDL_MIXER_SAMPLE_RATE, audio_format, - s->audio.ch_count, 4096 ) == -1 ) { -#endif - log_msg(LOG_LEVEL_ERROR, MOD_NAME "error initializing sound: %s\n", Mix_GetError()); - goto error; - } - adjust_ch_count(s); - const char *filename = s->req_filename; - if (!filename) { - filename = load_song1(); - if (!filename) { - goto error; - } - } - try_open_soundfont(); - s->music = Mix_LoadMUS(filename); - if (filename != s->req_filename) { - unlink(filename); - } - if (s->music == NULL) { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "error loading file: %s\n", Mix_GetError()); - goto error; - } - - s->audio.max_size = - s->audio.data_len = s->audio.ch_count * s->audio.bps * s->audio.sample_rate /* 1 sec */; - s->audio.data = malloc(s->audio.data_len); - s->sdl_mixer_buf = ring_buffer_init(s->audio.data_len); - - // register grab as a postmix processor - if (!Mix_RegisterEffect(MIX_CHANNEL_POST, sdl_mixer_audio_callback, NULL, s)) { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "Mix_RegisterEffect: %s\n", Mix_GetError()); - goto error; - } - - Mix_VolumeMusic(s->volume); - if (Mix_PlayMusic(s->music, -1) == SDL_ERR) { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "error playing file: %s\n", Mix_GetError()); - goto error; - } - - log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Initialized SDL_mixer\n"); - - return s; -error: - audio_cap_sdl_mixer_done(s); - return NULL; -} - -static struct audio_frame *audio_cap_sdl_mixer_read(void *state) -{ - struct state_sdl_mixer_capture *s = state; - s->audio.data_len = ring_buffer_read(s->sdl_mixer_buf, s->audio.data, s->audio.max_size); - if (s->audio.data_len == 0) { - return NULL; - } - return &s->audio; -} - -static void audio_cap_sdl_mixer_done(void *state) -{ - struct state_sdl_mixer_capture *s = state; - Mix_HaltMusic(); - Mix_FreeMusic(s->music); - Mix_CloseAudio(); - free(s->audio.data); - free(s->req_filename); - free(s); -} - -static const struct audio_capture_info acap_sdl_mixer_info = { - audio_cap_sdl_mixer_probe, - audio_cap_sdl_mixer_init, - audio_cap_sdl_mixer_read, - audio_cap_sdl_mixer_done -}; - -REGISTER_MODULE(sdl_mixer, &acap_sdl_mixer_info, LIBRARY_CLASS_AUDIO_CAPTURE, AUDIO_CAPTURE_ABI_VERSION); - From 8d15eb58a3b0c2547968ddf69943ae866858cc4b Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 1 Sep 2025 10:51:59 +0200 Subject: [PATCH 090/100] vulkan_sdl3: use common func for FPS pretty printout A common way would be to use generic FPS indicator. But a slight problem is that _putf return value isn't entirely reliable for this module, since the frame may be dropped later doe to insufficient performance. Because of the above, do not use the generic indicator, at least for now. But use the common function to make the output "prettier" (bold/color and if not performing well also use a color for FPS). --- src/video_display.c | 32 +++++++++++++++++------- src/video_display.h | 5 +++- src/video_display/vulkan/vulkan_sdl3.cpp | 6 ++--- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/video_display.c b/src/video_display.c index 7b3d37c9a..e6fa96956 100644 --- a/src/video_display.c +++ b/src/video_display.c @@ -345,6 +345,26 @@ struct video_frame *display_get_frame(struct display *d) } } +/** + * print display FPS + * + * Usually called from display_frame_helper for displays that use generic FPS + * indicator but externally linked for those that do not, like vulkan_sdl3. + */ +void +display_print_fps(const char *prefix, double seconds, int frames, + double nominal_fps) +{ + const double fps = frames / seconds; + const char *const fps_col = get_stat_color(fps / nominal_fps); + + log_msg(LOG_LEVEL_INFO, + TERM_BOLD TERM_FG_MAGENTA "%s" TERM_RESET + "%d frames in %g seconds = " TERM_BOLD + "%s%g FPS" TERM_RESET "\n", + prefix, frames, seconds, fps_col, fps); +} + static bool display_frame_helper(struct display *d, struct video_frame *frame, long long timeout_ns) { enum { @@ -364,15 +384,9 @@ static bool display_frame_helper(struct display *d, struct video_frame *frame, l long long seconds_ns = t - d->t0; if (seconds_ns > 5 * NS_IN_SEC) { const double seconds = (double) seconds_ns / NS_IN_SEC; - const double fps = d->frames / seconds; - const char *const fps_col = get_stat_color(fps / frame_fps); - - log_msg(LOG_LEVEL_INFO, - TERM_BOLD TERM_FG_MAGENTA - "%s" TERM_RESET "%d frames in %g seconds = " TERM_BOLD - "%s%g FPS" TERM_RESET "\n", - d->funcs->generic_fps_indicator_prefix, d->frames, - seconds, fps_col, fps); + display_print_fps(d->funcs->generic_fps_indicator_prefix, + seconds, d->frames, frame_fps); + d->frames = 0; d->t0 = t; } diff --git a/src/video_display.h b/src/video_display.h index f15882c42..b7fffba4c 100644 --- a/src/video_display.h +++ b/src/video_display.h @@ -13,7 +13,7 @@ * @ingroup display */ /* Copyright (c) 2001-2003 University of Southern California - * Copyright (c) 2005-2023 CESNET z.s.p.o. + * Copyright (c) 2005-2025 CESNET, zájmové sdružení právnických osob * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions @@ -211,6 +211,9 @@ bool display_reconfigure_audio(struct display *d, int quant_ struct video_frame *get_splashscreen(void); const char *get_audio_conn_flag_name(int audio_init_flag); +void display_print_fps(const char *prefix, double seconds, int frames, + double nominal_fps); + #ifdef __cplusplus } #endif // __cplusplus diff --git a/src/video_display/vulkan/vulkan_sdl3.cpp b/src/video_display/vulkan/vulkan_sdl3.cpp index e8219ed5c..ee427c639 100644 --- a/src/video_display/vulkan/vulkan_sdl3.cpp +++ b/src/video_display/vulkan/vulkan_sdl3.cpp @@ -394,9 +394,9 @@ void display_vulkan_run(void* state) { auto now = chrono::steady_clock::now(); double seconds = chrono::duration{ now - s->time }.count(); if (seconds > 5) { - double fps = s->frames / seconds; - log_msg(LOG_LEVEL_INFO, MOD_NAME "%llu frames in %g seconds = %g FPS\n", - static_cast(s->frames), seconds, fps); + display_print_fps(MOD_NAME, seconds, (int) s->frames, + s->current_desc.fps); + s->time = now; s->frames = 0; } From a3906013e1ca8a0cda345819fb199a3c94c4369d Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 3 Sep 2025 08:57:25 +0200 Subject: [PATCH 091/100] Coverity CI: sync with master YAML --- .github/workflows/coverity-scan.yml | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml index 59e74bdeb..7d3194ac2 100644 --- a/.github/workflows/coverity-scan.yml +++ b/.github/workflows/coverity-scan.yml @@ -45,29 +45,40 @@ jobs: if: ${{ env.coverity_token }} - name: Fetch SDKs ETags - if: ${{ env.coverity_token }} id: etags run: | $GITHUB_WORKSPACE/.github/scripts/get-etag.sh ndi\ - https://downloads.ndi.tv/SDK/NDI_SDK_Linux/Install_NDI_SDK_v6_Linux.\ - tar.gz >> $GITHUB_OUTPUT + https://downloads.ndi.tv/SDK/NDI_SDK_Linux/\ + Install_NDI_SDK_v6_Linux.tar.gz >> $GITHUB_OUTPUT - name: Run actions/cache for NDI - if: ${{ env.coverity_token }} id: cache-ndi uses: actions/cache@main with: path: /var/tmp/Install_NDI_SDK_Linux.tar.gz key: cache-ndi-${{ runner.os }}-${{ steps.etags.outputs.ndi }} - name: Download NDI - if: ${{ env.coverity_token }} && steps.cache-ndi.outputs.cache-hit != 'true' + if: steps.cache-ndi.outputs.cache-hit != 'true' run: "curl -Lf https://downloads.ndi.tv/SDK/NDI_SDK_Linux/\ Install_NDI_SDK_v6_Linux.tar.gz -o /var/tmp/Install_NDI_SDK_Linux.tar.gz" - + - name: Cache FFmpeg + uses: actions/cache@main + with: + path: '/var/tmp/ffmpeg' + key: cache-ffmpeg-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_ffmpeg.sh', '.github/scripts/Linux/install_other.sh', '.github/scripts/Linux/ffmpeg-patches/*') }} + - name: Cache SDL + uses: actions/cache@main + with: + path: '/var/tmp/sdl' + key: cache-sdl-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_sdl.sh' ) }} + - name: Cache GLFW + uses: actions/cache@main + with: + path: '/var/tmp/glfw' + key: cache-glfw-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_glfw.sh' ) }} - name: bootstrap run: | . .github/scripts/environment.sh .github/scripts/Linux/prepare.sh - sudo apt install libavcodec-dev libavformat-dev libswscale-dev libfluidsynth-dev libsdl2-ttf-dev - name: configure if: ${{ env.coverity_token }} From 28e8ca872bbaca3726f4d94e0bc658db922574ed Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 3 Sep 2025 09:08:18 +0200 Subject: [PATCH 092/100] Coverity CI: do not check coverity_token in every step This reverts 960d2315 (2022-02-21) because the commit 18f049608 (2022-04-20) already restricts the scheduled run to CESNET Git repository or manual push. So that the token unavailability should be considered error and thus the condition doesn't need to be re-iterated in every step. --- .github/workflows/coverity-scan.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml index 7d3194ac2..9694fdf21 100644 --- a/.github/workflows/coverity-scan.yml +++ b/.github/workflows/coverity-scan.yml @@ -16,8 +16,13 @@ jobs: coverity_token: ${{ secrets.coverity_token }} steps: + - name: Check Coverity token presence + if: env.coverity_token == '' + run: | + echo "secrects.coverity_token not present, skipping the analysys!" + exit 1 + - name: Get Coverity tool name # the file name contains version and is used as the cache key - if: ${{ env.coverity_token }} id: tool run: | FILENAME=$(curl -LIf "https://scan.coverity.com/download/linux64\ @@ -25,7 +30,6 @@ jobs: sed -n '/content-disposition/s/.*\"\(.*\)\"/\1/p') echo "filename=$FILENAME" >> $GITHUB_OUTPUT - name: Run actions/cache for Coverity build tool - if: ${{ env.coverity_token }} id: cache-coverity-tool uses: actions/cache@main with: @@ -36,13 +40,11 @@ jobs: run: | wget --no-verbose https://scan.coverity.com/download/linux64 --post-data "token=$coverity_token&project=UltraGrid" -O ~/coverity_tool.tgz - name: Extract Coverity build tool - if: ${{ env.coverity_token }} run: | tar xaf ~/coverity_tool.tgz mv cov-analysis* /tmp/cov-analysis - uses: actions/checkout@v4 - if: ${{ env.coverity_token }} - name: Fetch SDKs ETags id: etags @@ -81,14 +83,11 @@ jobs: .github/scripts/Linux/prepare.sh - name: configure - if: ${{ env.coverity_token }} run: ./autogen.sh $FEATURES - name: Build with cov-build - if: ${{ env.coverity_token }} run: | /tmp/cov-analysis/bin/cov-build --dir cov-int make -j2 - name: Submit the result to Coverity Scan - if: ${{ env.coverity_token }} run: | tar caf ultragrid.tar.xz cov-int result=$(curl -Sf --form token=$coverity_token \ From 7fc5380d18e6a7ca6087cfbdd581472a8cfd3df1 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 3 Sep 2025 09:46:32 +0200 Subject: [PATCH 093/100] Coverity CI: use own caches Coverity CI uses ubuntu-latest but C/C++ CI ubuntu-22.04, so that the builds won't be compatible. --- .github/workflows/coverity-scan.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml index 9694fdf21..c8f971fd5 100644 --- a/.github/workflows/coverity-scan.yml +++ b/.github/workflows/coverity-scan.yml @@ -66,17 +66,17 @@ jobs: uses: actions/cache@main with: path: '/var/tmp/ffmpeg' - key: cache-ffmpeg-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_ffmpeg.sh', '.github/scripts/Linux/install_other.sh', '.github/scripts/Linux/ffmpeg-patches/*') }} + key: cache-${{ github.workflow }}-ffmpeg-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_ffmpeg.sh', '.github/scripts/Linux/install_other.sh', '.github/scripts/Linux/ffmpeg-patches/*') }} - name: Cache SDL uses: actions/cache@main with: path: '/var/tmp/sdl' - key: cache-sdl-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_sdl.sh' ) }} + key: cache-${{ github.workflow }}-sdl-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_sdl.sh' ) }} - name: Cache GLFW uses: actions/cache@main with: path: '/var/tmp/glfw' - key: cache-glfw-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_glfw.sh' ) }} + key: cache-${{ github.workflow }}-glfw-${{ runner.os }}-${{ hashFiles( '.github/scripts/Linux/install_glfw.sh' ) }} - name: bootstrap run: | . .github/scripts/environment.sh From d1667f8187b0eadd60cab9ae79a9466a590a70ec Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 3 Sep 2025 12:40:26 +0200 Subject: [PATCH 094/100] snprintf_ch: discard return value from fprintf clang-tidy complains about the unused return value of fprintf, which is mostly useless here --- src/utils/macros.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/macros.h b/src/utils/macros.h index 007ab958e..d096d98d7 100644 --- a/src/utils/macros.h +++ b/src/utils/macros.h @@ -107,7 +107,7 @@ do { /* NOLINT(cppcoreguidelines-avoid-do-while) */ \ if (snprintf(str, sizeof str, __VA_ARGS__) >= \ (int) sizeof str) { \ - fprintf(stderr, \ + (void) fprintf(stderr, \ "\n%s:%d: %s: snprintf truncates %s (%d B " \ "needed)!\n\n", \ __FILE__, __LINE__, __func__, #str, \ From f806f01428479b88e65bca48b959f2c89c834141 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 3 Sep 2025 15:08:16 +0200 Subject: [PATCH 095/100] fs: get_exec_path: static + consolidate --- src/utils/fs.c | 46 ++++++++++++++++++++++++---------------------- src/utils/fs.h | 5 ----- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/utils/fs.c b/src/utils/fs.c index 3c9115e5d..16efffc83 100644 --- a/src/utils/fs.c +++ b/src/utils/fs.c @@ -50,6 +50,20 @@ #include "utils/fs.h" #include "utils/string.h" +// for get_exec_path +#ifdef __APPLE__ +#include //_NSGetExecutablePath +#include +#elif defined __FreeBSD__ +#include +#include +#elif !defined(_WIN32) && !defined(__linux__) && !defined(__DragonFly__) && \ + !defined(__NetBSD__) +#include // for getcwd +#include "host.h" // for uv_argv +#endif + + /** * Returns temporary path ending with path delimiter ('/' or '\' in Windows) */ @@ -79,28 +93,23 @@ const char *get_temp_dir(void) return temp_dir; } -// see also +/** + * see also + * @param path buffer with size MAX_PATH_SIZE where function stores path to executable + * @return 1 - SUCCESS, 0 - ERROR + */ +static int +get_exec_path(char *path) +{ #ifdef _WIN32 -int get_exec_path(char* path) { return GetModuleFileNameA(NULL, path, MAX_PATH_SIZE) != 0; -} #elif defined __linux__ -int get_exec_path(char* path) { return realpath("/proc/self/exe", path) != NULL; -} #elif defined __DragonFly__ -int get_exec_path(char* path) { return realpath("/proc/curproc/file", path) != NULL; -} #elif defined __NetBSD__ -int get_exec_path(char* path) { return realpath("/proc/curproc/exe", path) != NULL; -} #elif defined __APPLE__ -#include //_NSGetExecutablePath -#include - -int get_exec_path(char* path) { char raw_path_name[MAX_PATH_SIZE]; uint32_t raw_path_size = (uint32_t)(sizeof(raw_path_name)); @@ -108,19 +117,11 @@ int get_exec_path(char* path) { return false; } return realpath(raw_path_name, path) != NULL; -} #elif defined __FreeBSD__ -#include -#include -int get_exec_path(char* path) { int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; size_t cb = MAX_PATH_SIZE; return sysctl(mib, sizeof mib / sizeof mib[0], path, &cb, NULL, 0); -} #else -#include // for getcwd -#include "host.h" // for uv_argv -int get_exec_path(char* path) { if (uv_argv[0][0] == '/') { // with absolute path if (snprintf(path, MAX_PATH_SIZE, "%s", uv_argv[0]) == MAX_PATH_SIZE) { @@ -157,7 +158,8 @@ int get_exec_path(char* path) { path[strlen(path) - 1] = '\0'; return 0; } -#endif +#endif +} /** * @returns installation root without trailing '/', eg. installation prefix on diff --git a/src/utils/fs.h b/src/utils/fs.h index c31c8ddd3..1e258d4ac 100644 --- a/src/utils/fs.h +++ b/src/utils/fs.h @@ -59,11 +59,6 @@ extern "C" { #include #endif -/** - * @param path buffer with size MAX_PATH_SIZE where function stores path to executable - * @return 1 - SUCCESS, 0 - ERROR - */ -int get_exec_path(char* path); const char *get_temp_dir(void); FILE *get_temp_file(const char **filename); const char *get_install_root(void); From faf95598ad93958487543b459ea9c416b865d9dd Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 3 Sep 2025 16:06:14 +0200 Subject: [PATCH 096/100] get_exec_path: return bool + fixes return true fix FreeBSD and generic variant - the logic was inversed to the definition, returning 0 on success and !0 otherwise --- src/utils/fs.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/utils/fs.c b/src/utils/fs.c index 16efffc83..56f270f30 100644 --- a/src/utils/fs.c +++ b/src/utils/fs.c @@ -96,9 +96,8 @@ const char *get_temp_dir(void) /** * see also * @param path buffer with size MAX_PATH_SIZE where function stores path to executable - * @return 1 - SUCCESS, 0 - ERROR */ -static int +static bool get_exec_path(char *path) { #ifdef _WIN32 @@ -120,43 +119,43 @@ get_exec_path(char *path) #elif defined __FreeBSD__ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; size_t cb = MAX_PATH_SIZE; - return sysctl(mib, sizeof mib / sizeof mib[0], path, &cb, NULL, 0); + return sysctl(mib, sizeof mib / sizeof mib[0], path, &cb, NULL, 0) == 0; #else if (uv_argv[0][0] == '/') { // with absolute path if (snprintf(path, MAX_PATH_SIZE, "%s", uv_argv[0]) == MAX_PATH_SIZE) { - return -1; // truncated + return false; // truncated } - return 0; + return true; } if (strchr(uv_argv[0], '/') != NULL) { // or with relative path char cwd[MAX_PATH_SIZE]; if (getcwd(cwd, sizeof cwd) != cwd) { - return -1; + return false; } if (snprintf(path, MAX_PATH_SIZE, "%s/%s", cwd, uv_argv[0]) == MAX_PATH_SIZE) { - return -1; // truncated + return false; // truncated } - return 0; + return true; } // else launched from PATH char args[1024]; snprintf(args, sizeof args, "command -v %s", uv_argv[0]); FILE *f = popen(args, "r"); if (f == NULL) { - return -1; + return false; } if (fgets(path, MAX_PATH_SIZE, f) == NULL) { fclose(f); - return -1; + return false; } fclose(f); if (strlen(path) == 0 || path[strlen(path) - 1] != '\n') { - return -1; // truncated (?) + return false; // truncated (?) } path[strlen(path) - 1] = '\0'; - return 0; + return true; } #endif } From 4357452c32aee5558dd15c6516d8b0e13e979e49 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Wed, 3 Sep 2025 16:12:26 +0200 Subject: [PATCH 097/100] move soundfont to shared + always install This simplifies things a bit at the expense that the soundfont is always included/installed (91 kB). Added get_data_path() function for generic handling the path to common data (eg. /usr/local/share/ultragrid if installed). The idea is to catch all cases - installed (run with absolute or relative path) or run from inside the source directory in one place. --- .github/scripts/Windows/prepare_msys.sh | 7 -- .github/scripts/macOS/install_others.sh | 8 +-- Makefile.in | 2 + .../scripts/Linux-AppImage/create-appimage.sh | 5 -- {data => share/ultragrid}/default.sf3 | Bin src/audio/capture/fluidsynth.c | 59 ++++++++-------- src/utils/fs.c | 64 ++++++++++++++++-- src/utils/fs.h | 6 +- src/video_display/vulkan/vulkan_display.cpp | 16 ++--- 9 files changed, 98 insertions(+), 69 deletions(-) rename {data => share/ultragrid}/default.sf3 (100%) diff --git a/.github/scripts/Windows/prepare_msys.sh b/.github/scripts/Windows/prepare_msys.sh index af8c5fa27..0d66807d3 100644 --- a/.github/scripts/Windows/prepare_msys.sh +++ b/.github/scripts/Windows/prepare_msys.sh @@ -99,17 +99,10 @@ https://github.com/CESNET/GPUJPEG/releases/download/continuous/"$fname" cp -r GPUJPEG/* /usr/local/ )} -install_soundfont() { - sf_dir="$GITHUB_WORKSPACE/data/Windows/share/soundfonts" - mkdir -p "$sf_dir" - cp "$GITHUB_WORKSPACE/data/default.sf3" "$sf_dir" -} - # Install cross-platform deps "$GITHUB_WORKSPACE/.github/scripts/install-common-deps.sh" build_aja_wrapper install_deltacast install_gpujpeg -install_soundfont diff --git a/.github/scripts/macOS/install_others.sh b/.github/scripts/macOS/install_others.sh index df617ab99..6b7b07d68 100755 --- a/.github/scripts/macOS/install_others.sh +++ b/.github/scripts/macOS/install_others.sh @@ -71,12 +71,6 @@ install_ndi() {( printf '%b' "CPATH=$CPATH\n" >> "$GITHUB_ENV" } -install_soundfont() {( - sf_dir="$srcroot/data/template/macOS-bundle/Contents/share/soundfonts" - mkdir -p "$sf_dir" - cp "$GITHUB_WORKSPACE/data/default.sf3" "$sf_dir" -)} - install_syphon() { syphon_dst=/Library/Frameworks ( @@ -98,7 +92,7 @@ if [ $# -eq 1 ] && { [ "$1" = -h ] || [ "$1" = --help ] || [ "$1" = help ]; }; t fi if [ $# -eq 0 ] || [ $show_help ]; then - set -- deltacast glfw libbacktrace ndi soundfont syphon ximea + set -- deltacast glfw libbacktrace ndi syphon ximea fi if [ $show_help ]; then diff --git a/Makefile.in b/Makefile.in index 31ba79538..1687b8544 100644 --- a/Makefile.in +++ b/Makefile.in @@ -694,6 +694,7 @@ install: all $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir) if [ -n '@MANPAGES@' ]; then $(INSTALL) -m 644 @MANPAGES@ $(DESTDIR)$(man1dir); fi if [ -n '@DLL_LIBS@' ]; then $(INSTALL) -m 644 @DLL_LIBS@ $(DESTDIR)$(bindir); fi + $(INSTALL) -D -m 644 "$(srcdir)"/share/ultragrid/default.sf3 -t "$(DESTDIR)$(datarootdir)/ultragrid" if [ -n "@VULKAN@" ]; then\ $(INSTALL) -D -m 644 "$(srcdir)/share/ultragrid/vulkan_shaders/"* -t "$(DESTDIR)$(datadir)/ultragrid/vulkan_shaders"; \ fi @@ -716,6 +717,7 @@ uninstall: $(RM) $(DESTDIR)$(datadir)/metainfo/cz.cesnet.ultragrid.appdata.xml;\ $(RM) $(DESTDIR)$(datadir)/pixmaps/ultragrid.png;\ fi + $(RM) $(DESTDIR)$(datarootdir)/ultragrid/default.sf3 if [ -n "@VULKAN@" ]; then\ $(RM) "$(DESTDIR)$(datadir)/ultragrid/vulkan_shaders/"*;\ rmdir $(DESTDIR)$(datadir)/ultragrid/vulkan_shaders;\ diff --git a/data/scripts/Linux-AppImage/create-appimage.sh b/data/scripts/Linux-AppImage/create-appimage.sh index ce14ab000..18fdcf590 100755 --- a/data/scripts/Linux-AppImage/create-appimage.sh +++ b/data/scripts/Linux-AppImage/create-appimage.sh @@ -92,11 +92,6 @@ add_fonts() { # for GUI+testcard2 cp "$FONT_PATH" $APPPREFIX/share/fonts done done - if ls $APPPREFIX/lib/*mixer* >/dev/null 2>&1 || - ls $APPPREFIX/lib/ultragrid/*fluidsynth* >/dev/null 2>&1; then - mkdir -p $APPPREFIX/share/soundfonts - cp "$srcdir/data/default.sf3" $APPPREFIX/share/soundfonts/ - fi } # copy dependencies diff --git a/data/default.sf3 b/share/ultragrid/default.sf3 similarity index 100% rename from data/default.sf3 rename to share/ultragrid/default.sf3 diff --git a/src/audio/capture/fluidsynth.c b/src/audio/capture/fluidsynth.c index c0ecbe1a4..b5b59e5d2 100644 --- a/src/audio/capture/fluidsynth.c +++ b/src/audio/capture/fluidsynth.c @@ -56,7 +56,7 @@ #include "types.h" // for device_info #include "utils/color_out.h" // for color_printf, TBOLD, TRED #include "utils/fs.h" // for get_install_root, get_temp_file -#include "utils/macros.h" // for IS_KEY_PREFIX +#include "utils/macros.h" // for ARR_COUNT, IS_KEY_PREFIX struct module; @@ -183,40 +183,35 @@ get_soundfont() if (env_fs != NULL) { return strdup(env_fs); } - const bool force_bundled_sf = - getenv("ULTRAGRID_BUNDLED_SF") != NULL && - strcmp(getenv("ULTRAGRID_BUNDLED_SF"), "1") == 0; - const char *roots[2] = { "/usr", get_install_root() }; - if (force_bundled_sf) { - roots[0] = get_install_root(); - roots[1] = "/usr"; - } + char bundled[MAX_PATH_SIZE]; + snprintf_ch(bundled, "%s/%s", get_data_path(), "default.sf3"); const char *sf_candidates[] = { - // without install prefix - "/share/soundfonts/default.sf2", - "/share/soundfonts/default.sf3", - "/share/sounds/sf2/default-GM.sf2", - "/share/sounds/sf3/default-GM.sf3", // Ubuntu + "/usr/share/soundfonts/default.sf2", + "/usr/share/soundfonts/default.sf3", + "/usr/share/sounds/sf2/default-GM.sf2", + "/usr/share/sounds/sf3/default-GM.sf3", // Ubuntu + bundled, }; - for (size_t i = 0; i < sizeof roots / sizeof roots[0]; ++i) { - for (size_t j = 0; - j < sizeof sf_candidates / sizeof sf_candidates[0]; ++j) { - const char *root = roots[i]; - const size_t len = - strlen(root) + strlen(sf_candidates[j]) + 1; - char path[len]; - strncpy(path, root, len - 1); - strncat(path, sf_candidates[j], len - strlen(path) - 1); - FILE *f = fopen(path, "rb"); - debug_msg(MOD_NAME - "Trying to open sound font '%s': %s\n", - path, f ? "success, setting" : "failed"); - if (!f) { - continue; - } - fclose(f); - return strdup(path); + + const char *force_bundled_sf = getenv("ULTRAGRID_BUNDLED_SF"); + if (force_bundled_sf != NULL && strcmp(force_bundled_sf, "1") == 0) { + for (size_t i = ARR_COUNT(sf_candidates) - 1; i > 0; --i) { + sf_candidates[i] = sf_candidates[i - 1]; + } + sf_candidates[0] = bundled; + } + + for (size_t i = 0; i < ARR_COUNT(sf_candidates); ++i) { + const char *path = sf_candidates[i]; + FILE *f = fopen(path, "rb"); + debug_msg(MOD_NAME + "Trying to open sound font '%s': %s\n", + path, f ? "success, setting" : "failed"); + if (!f) { + continue; } + fclose(f); + return strdup(path); } MSG(ERROR, "Cannot find any suitable sound font!\n"); return NULL; diff --git a/src/utils/fs.c b/src/utils/fs.c index 56f270f30..4766fbd73 100644 --- a/src/utils/fs.c +++ b/src/utils/fs.c @@ -40,14 +40,19 @@ #include "config.h" #include "config_unix.h" #include "config_win32.h" +#else +#define SRCDIR ".." #endif #include #include #include #include +#include +#include "debug.h" #include "utils/fs.h" +#include "utils/macros.h" #include "utils/string.h" // for get_exec_path @@ -63,6 +68,7 @@ #include "host.h" // for uv_argv #endif +#define MOD_NAME "[fs] " /** * Returns temporary path ending with path delimiter ('/' or '\' in Windows) @@ -165,8 +171,9 @@ get_exec_path(char *path) * Linux - default "/usr/local", Windows - top-level directory extracted * UltraGrid directory */ -const char *get_install_root(void) { - static __thread char exec_path[MAX_PATH_SIZE]; +static bool +get_install_root(char exec_path[static MAX_PATH_SIZE]) +{ if (!get_exec_path(exec_path)) { return NULL; } @@ -177,12 +184,61 @@ const char *get_install_root(void) { *last_path_delim = '\0'; // cut off executable name last_path_delim = strrpbrk(exec_path, "/\\"); if (!last_path_delim) { - return exec_path; + return true; } if (strcmp(last_path_delim + 1, "bin") == 0 || strcmp(last_path_delim + 1, "MacOS") == 0) { *last_path_delim = '\0'; // remove "bin" suffix if there is one (not in Windows builds) or MacOS in a bundle } - return exec_path; + return true; +} + +static bool +dir_exists(const char *path) +{ + struct stat sb; + if (stat(path, &sb) == -1) { + return false; + } + return S_ISDIR(sb.st_mode); +} + +/** + * returns path with UltraGrid data (path to `share/ultragrid` if run from + * source, otherwise the corresponding path in system if installed or in + * bundle/appimage/extracted dir) + * @retval the path; NULL if not found + */ +const char * +get_data_path() +{ + static __thread char path[MAX_PATH_SIZE]; + if (strlen(path) > 0) { // already set + return path; + } + + const char suffix[] = "/share/ultragrid"; + + if (get_install_root(path)) { + size_t len = sizeof path - strlen(path); + if ((size_t) snprintf(path + strlen(path), len, + suffix) >= len) { + abort(); // path truncated + } + if (dir_exists(path)) { + MSG(VERBOSE, "Using data path %s\n", path); + return path; + } + } + + snprintf_ch(path, SRCDIR "%s", suffix); + if (dir_exists(path)) { + MSG(VERBOSE, "Using data path %s\n", path); + return path; + } + + MSG(WARNING, "No data path could have been found!\n"); + path[0] = '\0'; // avoid quick cached return at start + return NULL; } /** diff --git a/src/utils/fs.h b/src/utils/fs.h index 1e258d4ac..2d8fa1898 100644 --- a/src/utils/fs.h +++ b/src/utils/fs.h @@ -4,7 +4,7 @@ * @author Martin Bela <492789@mail.muni.cz> */ /* - * Copyright (c) 2018-2023 CESNET, z. s. p. o. + * Copyright (c) 2018-2025 CESNET, zájmové sdružení právnických osob * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -61,8 +61,8 @@ extern "C" { const char *get_temp_dir(void); FILE *get_temp_file(const char **filename); -const char *get_install_root(void); - char *strdup_path_with_expansion(const char *orig_path); +const char *get_data_path(void); +char *strdup_path_with_expansion(const char *orig_path); #ifdef __cplusplus } // extern "C" diff --git a/src/video_display/vulkan/vulkan_display.cpp b/src/video_display/vulkan/vulkan_display.cpp index c1bbdffe0..6c4dbcb28 100644 --- a/src/video_display/vulkan/vulkan_display.cpp +++ b/src/video_display/vulkan/vulkan_display.cpp @@ -51,12 +51,6 @@ #include "debug.h" #include "utils/fs.h" -#ifdef HAVE_CONFIG_H -#include "config.h" // for SRCDIR -#else -#define SRCDIR ".." -#endif // HAVE_CONFIG_H - #define MOD_NAME "[vulkan] " using namespace vulkan_display_detail; @@ -663,19 +657,19 @@ void VulkanDisplay::window_parameters_changed(WindowParameters new_parameters) { std::string get_shader_path() { - constexpr char suffix[] = "/share/ultragrid/vulkan_shaders"; + constexpr char suffix[] = "/vulkan_shaders"; // note that get_install_root returns bin/.. if run from build, // which will not contain the shaders for out-of-tree builds - const char *path = get_install_root(); - if (path != nullptr) { - std::string path_to_shaders = std::string(path) + suffix; + const char *data_path = get_data_path(); + if (data_path != nullptr) { + std::string path_to_shaders = std::string(data_path) + suffix; std::filesystem::directory_entry dir{ std::filesystem::path( path_to_shaders) }; if (dir.exists()) { return path_to_shaders; } } - return std::string(SRCDIR) + suffix; + return {}; } } //namespace vulkan_display From 0578d809c1433ba1f1fd10eb206708d14903e802 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 4 Sep 2025 08:58:46 +0200 Subject: [PATCH 098/100] default sountfont: use original name The name default.sf3 is no longer required, the soundfont name is resolved directly so use the original name. It is TimGM6mb derivative with just piano downloaded from: --- Makefile.in | 4 ++-- .../{default.sf3 => TimGM6mb_but_fixed__piano_.sf3} | Bin src/audio/capture/fluidsynth.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename share/ultragrid/{default.sf3 => TimGM6mb_but_fixed__piano_.sf3} (100%) diff --git a/Makefile.in b/Makefile.in index 1687b8544..d225a0341 100644 --- a/Makefile.in +++ b/Makefile.in @@ -694,7 +694,7 @@ install: all $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir) if [ -n '@MANPAGES@' ]; then $(INSTALL) -m 644 @MANPAGES@ $(DESTDIR)$(man1dir); fi if [ -n '@DLL_LIBS@' ]; then $(INSTALL) -m 644 @DLL_LIBS@ $(DESTDIR)$(bindir); fi - $(INSTALL) -D -m 644 "$(srcdir)"/share/ultragrid/default.sf3 -t "$(DESTDIR)$(datarootdir)/ultragrid" + $(INSTALL) -D -m 644 "$(srcdir)"/share/ultragrid/TimGM6mb_but_fixed__piano_.sf3 -t "$(DESTDIR)$(datarootdir)/ultragrid" if [ -n "@VULKAN@" ]; then\ $(INSTALL) -D -m 644 "$(srcdir)/share/ultragrid/vulkan_shaders/"* -t "$(DESTDIR)$(datadir)/ultragrid/vulkan_shaders"; \ fi @@ -717,7 +717,7 @@ uninstall: $(RM) $(DESTDIR)$(datadir)/metainfo/cz.cesnet.ultragrid.appdata.xml;\ $(RM) $(DESTDIR)$(datadir)/pixmaps/ultragrid.png;\ fi - $(RM) $(DESTDIR)$(datarootdir)/ultragrid/default.sf3 + $(RM) $(DESTDIR)$(datarootdir)/ultragrid/TimGM6mb_but_fixed__piano_.sf3 if [ -n "@VULKAN@" ]; then\ $(RM) "$(DESTDIR)$(datadir)/ultragrid/vulkan_shaders/"*;\ rmdir $(DESTDIR)$(datadir)/ultragrid/vulkan_shaders;\ diff --git a/share/ultragrid/default.sf3 b/share/ultragrid/TimGM6mb_but_fixed__piano_.sf3 similarity index 100% rename from share/ultragrid/default.sf3 rename to share/ultragrid/TimGM6mb_but_fixed__piano_.sf3 diff --git a/src/audio/capture/fluidsynth.c b/src/audio/capture/fluidsynth.c index b5b59e5d2..52c1e763e 100644 --- a/src/audio/capture/fluidsynth.c +++ b/src/audio/capture/fluidsynth.c @@ -184,7 +184,7 @@ get_soundfont() return strdup(env_fs); } char bundled[MAX_PATH_SIZE]; - snprintf_ch(bundled, "%s/%s", get_data_path(), "default.sf3"); + snprintf_ch(bundled, "%s/%s", get_data_path(), "TimGM6mb_but_fixed__piano_.sf3"); const char *sf_candidates[] = { "/usr/share/soundfonts/default.sf2", "/usr/share/soundfonts/default.sf3", From 193c8d5ab19a9f1b86d734827c8cbd11b52e9e72 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 4 Sep 2025 10:40:11 +0200 Subject: [PATCH 099/100] minor fixes - coverity-scan.yml: typo - configure.ac: misleading message if SDL not found -> do not mention version at all, it was perhaps meant "didn't found any version" but it might have also been that did't found specified version... - configure.ac: removed sdl_mixer from summary --- .github/workflows/coverity-scan.yml | 2 +- configure.ac | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml index c8f971fd5..4b94e3b0d 100644 --- a/.github/workflows/coverity-scan.yml +++ b/.github/workflows/coverity-scan.yml @@ -19,7 +19,7 @@ jobs: - name: Check Coverity token presence if: env.coverity_token == '' run: | - echo "secrects.coverity_token not present, skipping the analysys!" + echo "secrects.coverity_token not present, skipping the analysis!" exit 1 - name: Get Coverity tool name # the file name contains version and is used as the cache key diff --git a/configure.ac b/configure.ac index 1be402af7..fdfe91b36 100644 --- a/configure.ac +++ b/configure.ac @@ -1360,7 +1360,7 @@ fi sdl_version=${sdl_version:-0} -ENSURE_FEATURE_PRESENT([$sdl_req], [$sdl], [SDL requested but found any version]) +ENSURE_FEATURE_PRESENT([$sdl_req], [$sdl], [SDL requested but not found]) # ------------------------------------------------------------------------------ # Vulkan @@ -3607,7 +3607,6 @@ RESULT=`add_column "$RESULT" "CoreAudio" $coreaudio $?` RESULT=`add_column "$RESULT" "FluidSynth" $fluidsynth $?` RESULT=`add_column "$RESULT" "JACK" $jack $?` RESULT=`add_column "$RESULT" "JACK transport" $jack_trans $?` -RESULT=`add_column "$RESULT" "SDL_mixer" $sdl_mixer $?` RESULT=`add_column "$RESULT" "Pipewire" $pipewire_audio $?` RESULT=`add_column "$RESULT" "Portaudio" $portaudio $?` RESULT=`add_column "$RESULT" "WASAPI" $wasapi $?` From ffa79acdc54576ee54a02e868f8675b68def7422 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 4 Sep 2025 11:22:51 +0200 Subject: [PATCH 100/100] vcomp/cuda_dxt: improve error print - use logger - print CUDA error string --- src/video_compress/cuda_dxt.cpp | 93 ++++++++++++++++----------------- 1 file changed, 44 insertions(+), 49 deletions(-) diff --git a/src/video_compress/cuda_dxt.cpp b/src/video_compress/cuda_dxt.cpp index 55b1b817c..4aafcd9b5 100644 --- a/src/video_compress/cuda_dxt.cpp +++ b/src/video_compress/cuda_dxt.cpp @@ -35,7 +35,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include // for printf, fprintf, stderr +#include // for printf, stderr #include // for free, malloc #include // for shared_ptr @@ -52,6 +52,15 @@ #include "video_compress.h" #include "video_frame.h" // for vf_get_tile, video_desc_from_f... +#define MOD_NAME "[CUDA DXT] " + +#define CHECK_CUDA(cmd, msg, action) \ + if ((cmd) != CUDA_WRAPPER_SUCCESS) { \ + MSG(ERROR, "%s: %s\n", msg, cuda_wrapper_last_error_string()); \ + action; \ + } + + using namespace std; namespace { @@ -144,25 +153,22 @@ static bool configure_with(struct state_video_compress_cuda_dxt *s, struct video codec_t supported_codecs[] = { RGB, UYVY, VIDEO_CODEC_NONE }; s->decoder = get_best_decoder_from(desc.color_spec, supported_codecs, &s->in_codec); if (!s->decoder) { - fprintf(stderr, "Unsupported codec: %s\n", get_codec_name(desc.color_spec)); + MSG(ERROR, "Unsupported codec: %s\n", + get_codec_name(desc.color_spec)); return false; } if (s->in_codec == UYVY) { - if (CUDA_WRAPPER_SUCCESS != cuda_wrapper_malloc((void **) &s->cuda_uyvy_buffer, - desc.width * desc.height * 2)) { - fprintf(stderr, "Could not allocate CUDA UYVY buffer.\n"); - return false; - } + CHECK_CUDA(cuda_wrapper_malloc((void **) &s->cuda_uyvy_buffer, + desc.width * desc.height * 2), + "Could not allocate CUDA UYVY buffer", return false); } s->in_buffer = (char *) malloc(desc.width * desc.height * 3); - if (CUDA_WRAPPER_SUCCESS != cuda_wrapper_malloc((void **) &s->cuda_in_buffer, - desc.width * desc.height * 3)) { - fprintf(stderr, "Could not allocate CUDA output buffer.\n"); - return false; - } + CHECK_CUDA(cuda_wrapper_malloc((void **) &s->cuda_in_buffer, + desc.width * desc.height * 3), + "Could not allocate CUDA output buffer", return false); struct video_desc compressed_desc = desc; compressed_desc.color_spec = s->out_codec; @@ -171,12 +177,8 @@ static bool configure_with(struct state_video_compress_cuda_dxt *s, struct video s->pool.reconfigure(compressed_desc, data_len); - if (CUDA_WRAPPER_SUCCESS != cuda_wrapper_malloc((void **) - &s->cuda_out_buffer, - data_len)) { - fprintf(stderr, "Could not allocate CUDA output buffer.\n"); - return false; - } + CHECK_CUDA(cuda_wrapper_malloc((void **) &s->cuda_out_buffer, data_len), + "Could not allocate CUDA output buffer", return false); return true; } @@ -196,7 +198,7 @@ shared_ptr cuda_dxt_compress_tile(void *state, shared_ptrsaved_desc = video_desc_from_frame(tx.get()); } else { - fprintf(stderr, "[CUDA DXT] Reconfiguration failed!\n"); + MSG(ERROR, "Reconfiguration failed!\n"); return NULL; } } @@ -218,24 +220,22 @@ shared_ptr cuda_dxt_compress_tile(void *state, shared_ptrin_codec == UYVY) { - if (cuda_wrapper_memcpy(s->cuda_uyvy_buffer, in_buffer, tx->tiles[0].width * - tx->tiles[0].height * 2, - CUDA_WRAPPER_MEMCPY_HOST_TO_DEVICE) != CUDA_WRAPPER_SUCCESS) { - fprintf(stderr, "Memcpy failed: %s\n", cuda_wrapper_last_error_string()); - return NULL; - } - if (cuda_yuv422_to_yuv444(s->cuda_uyvy_buffer, s->cuda_in_buffer, - tx->tiles[0].width * - tx->tiles[0].height, 0) != CUDA_WRAPPER_SUCCESS) { - fprintf(stderr, "Kernel failed: %s\n", cuda_wrapper_last_error_string()); - } + CHECK_CUDA(cuda_wrapper_memcpy( + s->cuda_uyvy_buffer, in_buffer, + tx->tiles[0].width * tx->tiles[0].height * 2, + CUDA_WRAPPER_MEMCPY_HOST_TO_DEVICE), + "Memcpy failed", return nullptr); + + CHECK_CUDA(cuda_yuv422_to_yuv444( + s->cuda_uyvy_buffer, s->cuda_in_buffer, + tx->tiles[0].width * tx->tiles[0].height, 0), + "Kernel failed", return nullptr); } else { - if (cuda_wrapper_memcpy(s->cuda_in_buffer, in_buffer, tx->tiles[0].width * - tx->tiles[0].height * 3, - CUDA_WRAPPER_MEMCPY_HOST_TO_DEVICE) != CUDA_WRAPPER_SUCCESS) { - fprintf(stderr, "Memcpy failed: %s\n", cuda_wrapper_last_error_string()); - return NULL; - } + CHECK_CUDA(cuda_wrapper_memcpy( + s->cuda_in_buffer, in_buffer, + tx->tiles[0].width * tx->tiles[0].height * 3, + CUDA_WRAPPER_MEMCPY_HOST_TO_DEVICE), + "Memcpy failed", return nullptr); } int (*cuda_dxt_enc_func)(const void * src, void * out, int size_x, int size_y, @@ -254,21 +254,16 @@ shared_ptr cuda_dxt_compress_tile(void *state, shared_ptrcuda_in_buffer, s->cuda_out_buffer, - s->saved_desc.width, s->saved_desc.height, 0); - if (ret != 0) { - fprintf(stderr, "Encoding failed: %s\n", cuda_wrapper_last_error_string()); - return NULL; - } + CHECK_CUDA(cuda_dxt_enc_func(s->cuda_in_buffer, s->cuda_out_buffer, + s->saved_desc.width, s->saved_desc.height, + 0), + "Encoding failed", return nullptr); shared_ptr out = s->pool.get_frame(); - if (cuda_wrapper_memcpy(out->tiles[0].data, - s->cuda_out_buffer, - out->tiles[0].data_len, - CUDA_WRAPPER_MEMCPY_DEVICE_TO_HOST) != CUDA_WRAPPER_SUCCESS) { - fprintf(stderr, "Memcpy failed: %s\n", cuda_wrapper_last_error_string()); - return NULL; - } + CHECK_CUDA(cuda_wrapper_memcpy(out->tiles[0].data, s->cuda_out_buffer, + out->tiles[0].data_len, + CUDA_WRAPPER_MEMCPY_DEVICE_TO_HOST), + "Memcpy failed", return nullptr); return out; }