From 5be04150a0b7ab81ceb129e60c8c1e738e95be98 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sat, 6 Jul 2024 10:33:39 -0700 Subject: [PATCH 01/17] Load presets and play them with playlist --- CMakeLists.txt | 3 ++- src/config.h | 2 +- src/projectm.c | 17 +++++++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a214d6..3a778a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ project(gstprojectm VERSION 0.0.1) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") -find_package(projectM4 REQUIRED) +find_package(projectM4 REQUIRED COMPONENTS Playlist) find_package(GStreamer REQUIRED COMPONENTS gstreamer-audio gstreamer-gl gstreamer-pbutils gstreamer-video) find_package(GLIB2 REQUIRED) @@ -61,6 +61,7 @@ endif() target_link_libraries(gstprojectm PRIVATE libprojectM::projectM + libprojectM::playlist PUBLIC ${GSTREAMER_LIBRARIES} ${GSTREAMER_BASE_LIBRARIES} diff --git a/src/config.h b/src/config.h index 6d736d7..c3d7d77 100644 --- a/src/config.h +++ b/src/config.h @@ -30,7 +30,7 @@ G_BEGIN_DECLS #define DEFAULT_MESH_SIZE "48,32" #define DEFAULT_ASPECT_CORRECTION TRUE #define DEFAULT_EASTER_EGG 0.0 -#define DEFAULT_PRESET_LOCKED TRUE +#define DEFAULT_PRESET_LOCKED FALSE G_END_DECLS diff --git a/src/projectm.c b/src/projectm.c index b86a7fe..b3391e6 100644 --- a/src/projectm.c +++ b/src/projectm.c @@ -5,6 +5,7 @@ #include #include +#include #include "projectm.h" #include "plugin.h" @@ -15,6 +16,7 @@ GST_DEBUG_CATEGORY_STATIC(projectm_debug); projectm_handle projectm_init(GstProjectM *plugin) { projectm_handle handle = NULL; + projectm_playlist_handle playlist = NULL; GST_DEBUG_CATEGORY_INIT(projectm_debug, "projectm", 0, "ProjectM"); @@ -34,6 +36,11 @@ projectm_handle projectm_init(GstProjectM *plugin) GST_DEBUG_OBJECT(plugin, "Created projectM instance!"); } + // initialize preset playlist + playlist = projectm_playlist_create(handle); + projectm_playlist_set_shuffle(playlist, true); + // projectm_playlist_set_preset_switched_event_callback(_playlist, &ProjectMWrapper::PresetSwitchedEvent, static_cast(this)); + // Log properties GST_INFO_OBJECT(plugin, "Using Properties: " "preset=%s, " @@ -63,8 +70,10 @@ projectm_handle projectm_init(GstProjectM *plugin) plugin->preset_locked); // Load preset file if path is provided - if (plugin->preset_path != NULL) - projectm_load_preset_file(handle, plugin->preset_path, false); + if (plugin->preset_path != NULL) { + int added_count = projectm_playlist_add_path(playlist, plugin->preset_path, true, false); + GST_INFO("Loaded preset path: %s, presets found: %d", plugin->preset_path, added_count); + } // Set texture search path if directory path is provided if (plugin->texture_dir_path != NULL) @@ -84,6 +93,10 @@ projectm_handle projectm_init(GstProjectM *plugin) if (plugin->preset_duration > 0.0) { projectm_set_preset_duration(handle, plugin->preset_duration); + + // kick off the first preset + if (projectm_playlist_size(playlist) > 0 && ! plugin->preset_locked) + projectm_playlist_play_next(playlist, true); } else { From 0428f462ddbba41dc76220f45873a32475a7d638 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sat, 6 Jul 2024 10:42:37 -0700 Subject: [PATCH 02/17] CI: build projectM with playlist --- .github/workflows/build_linux.yml | 2 +- .idea/.gitignore | 8 ++ .idea/.name | 1 + .idea/editor.xml | 103 ++++++++++++++++++++++++ .idea/gst-projectm.iml | 2 + .idea/misc.xml | 7 ++ .idea/modules.xml | 8 ++ .idea/runConfigurations/gstprojectm.xml | 7 ++ .idea/vcs.xml | 6 ++ 9 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/editor.xml create mode 100644 .idea/gst-projectm.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/runConfigurations/gstprojectm.xml create mode 100644 .idea/vcs.xml diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 9c0a192..1469cd2 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -42,7 +42,7 @@ jobs: if: steps.cache-install.outputs.cache-hit != 'true' - name: Configure Build - run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON + run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON -DENABLE_PLAYLIST=ON if: steps.cache-install.outputs.cache-hit != 'true' - name: Build Release diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..5fbd39d --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +gstprojectm \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml new file mode 100644 index 0000000..855412d --- /dev/null +++ b/.idea/editor.xml @@ -0,0 +1,103 @@ + + + + + \ No newline at end of file diff --git a/.idea/gst-projectm.iml b/.idea/gst-projectm.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/gst-projectm.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0b76fe5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..88facca --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/gstprojectm.xml b/.idea/runConfigurations/gstprojectm.xml new file mode 100644 index 0000000..84d73fa --- /dev/null +++ b/.idea/runConfigurations/gstprojectm.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From 3ef035c83b0b0dab7f8613ba5c320ca897370731 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sat, 6 Jul 2024 10:47:06 -0700 Subject: [PATCH 03/17] CI: build projectM with playlist --- .github/workflows/build_linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 1469cd2..7306970 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -25,7 +25,7 @@ jobs: - name: Get ProjectM Git Hash id: git-hash - run: echo "hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + run: echo "hash=$(git rev-parse HEAD)v2" >> $GITHUB_OUTPUT - name: Cache Install id: cache-install From 1380c7ca976b2c3a90dc988834be0df14a5d8504 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sat, 6 Jul 2024 11:07:34 -0700 Subject: [PATCH 04/17] CMAKE_MODULE_PATH --- .github/workflows/build_linux.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 7306970..20346b3 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -88,6 +88,8 @@ jobs: - name: Configure Build run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_VERBOSE_MAKEFILE=YES -DprojectM4_DIR="artifacts/lib/cmake/projectM4" + env: + CMAKE_MODULE_PATH: "${{ github.workspace }}/artifacts/lib/cmake" - name: Build Release id: build From 5007aea65129c8c4b3f153aae58e891b06aaa526 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sat, 6 Jul 2024 11:10:47 -0700 Subject: [PATCH 05/17] projectM4Playlist cmake file --- .github/workflows/build_linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 20346b3..114026d 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -89,7 +89,7 @@ jobs: - name: Configure Build run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_VERBOSE_MAKEFILE=YES -DprojectM4_DIR="artifacts/lib/cmake/projectM4" env: - CMAKE_MODULE_PATH: "${{ github.workspace }}/artifacts/lib/cmake" + CMAKE_MODULE_PATH: "${{ github.workspace }}/artifacts/lib/cmake/projectM4Playlist" - name: Build Release id: build From 82a91e568025329d7f0177615986a15efed5cfb8 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sat, 6 Jul 2024 11:16:24 -0700 Subject: [PATCH 06/17] projectM4Playlist cmake file --- .github/workflows/build_linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 114026d..6c160bd 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -89,7 +89,7 @@ jobs: - name: Configure Build run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_VERBOSE_MAKEFILE=YES -DprojectM4_DIR="artifacts/lib/cmake/projectM4" env: - CMAKE_MODULE_PATH: "${{ github.workspace }}/artifacts/lib/cmake/projectM4Playlist" + projectM4Playlist_DIR: "${{ github.workspace }}/artifacts/lib/cmake/projectM4Playlist" - name: Build Release id: build From b47065b40f7fd3717d28b2b46742e260f3e87862 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sat, 6 Jul 2024 11:19:51 -0700 Subject: [PATCH 07/17] projectM4Playlist cmake file --- .github/workflows/build_osx.yml | 2 +- .github/workflows/build_windows.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_osx.yml b/.github/workflows/build_osx.yml index 405f0a1..5b99af9 100644 --- a/.github/workflows/build_osx.yml +++ b/.github/workflows/build_osx.yml @@ -43,7 +43,7 @@ jobs: if: steps.cache-install.outputs.cache-hit != 'true' - name: Configure Build - run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON + run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON -DENABLE_PLAYLIST=ON if: steps.cache-install.outputs.cache-hit != 'true' - name: Build Release diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 885c230..15955b4 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -34,7 +34,7 @@ jobs: - name: Get ProjectM Git Hash id: git-hash - run: echo "hash=$(git rev-parse HEAD)" >> $Env:GITHUB_OUTPUT + run: echo "hash=$(git rev-parse HEAD)v2" >> $Env:GITHUB_OUTPUT - name: Cache Install id: cache-install @@ -45,7 +45,7 @@ jobs: key: install-${{ runner.os }}-projectm-${{ steps.git-hash.outputs.hash }} - name: Configure Build - run: cmake -G "Visual Studio 17 2022" -A "X64" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_TOOLCHAIN_FILE="${Env:VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>DLL" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON + run: cmake -G "Visual Studio 17 2022" -A "X64" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_TOOLCHAIN_FILE="${Env:VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>DLL" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON -DENABLE_PLAYLIST=ON if: steps.cache-install.outputs.cache-hit != 'true' - name: Build Release From 9741260f2e2c5dc994e0a6ad1d506d88b4e86445 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sat, 6 Jul 2024 12:22:45 -0700 Subject: [PATCH 08/17] projectM4Playlist cmake file --- .github/workflows/build_osx.yml | 2 ++ .github/workflows/build_windows.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/build_osx.yml b/.github/workflows/build_osx.yml index 5b99af9..f657fc0 100644 --- a/.github/workflows/build_osx.yml +++ b/.github/workflows/build_osx.yml @@ -89,6 +89,8 @@ jobs: - name: Configure Build run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_VERBOSE_MAKEFILE=YES -DprojectM4_DIR="artifacts/lib/cmake/projectM4" + env: + projectM4Playlist_DIR: "${{ github.workspace }}/artifacts/lib/cmake/projectM4Playlist" - name: Build Release id: build diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 15955b4..503f428 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -97,6 +97,8 @@ jobs: - name: Configure Build run: cmake -G "Visual Studio 17 2022" -A "X64" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_TOOLCHAIN_FILE="${Env:VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>" -DCMAKE_VERBOSE_MAKEFILE=YES -DprojectM4_DIR="artifacts/lib/cmake/projectM4" + env: + projectM4Playlist_DIR: "${{ github.workspace }}/artifacts/lib/cmake/projectM4Playlist" - name: Build Release id: build From cc385834b40b5c3941ed1a30f0173c7969bb33b3 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sat, 6 Jul 2024 14:31:28 -0700 Subject: [PATCH 09/17] Add shuffle presets property --- src/config.h | 1 + src/enums.h | 1 + src/plugin.c | 14 +++++++++++++- src/plugin.h | 1 + src/projectm.c | 14 ++++++++------ 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/config.h b/src/config.h index c3d7d77..6145a41 100644 --- a/src/config.h +++ b/src/config.h @@ -31,6 +31,7 @@ G_BEGIN_DECLS #define DEFAULT_ASPECT_CORRECTION TRUE #define DEFAULT_EASTER_EGG 0.0 #define DEFAULT_PRESET_LOCKED FALSE +#define DEFAULT_SHUFFLE_PRESETS TRUE G_END_DECLS diff --git a/src/enums.h b/src/enums.h index b6b2761..5b41757 100644 --- a/src/enums.h +++ b/src/enums.h @@ -24,6 +24,7 @@ enum PROP_ASPECT_CORRECTION, PROP_EASTER_EGG, PROP_PRESET_LOCKED, + PROP_SHUFFLE_PRESETS, }; G_END_DECLS diff --git a/src/plugin.c b/src/plugin.c index 4615f54..3981f83 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -95,6 +95,9 @@ void gst_projectm_set_property(GObject *object, guint property_id, case PROP_PRESET_LOCKED: plugin->preset_locked = g_value_get_boolean(value); break; + case PROP_SHUFFLE_PRESETS: + plugin->shuffle_presets = g_value_get_boolean(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; @@ -151,6 +154,9 @@ void gst_projectm_get_property(GObject *object, guint property_id, case PROP_PRESET_LOCKED: g_value_set_boolean(value, plugin->preset_locked); break; + case PROP_SHUFFLE_PRESETS: + g_value_set_boolean(value, plugin->shuffle_presets); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; @@ -170,6 +176,7 @@ static void gst_projectm_init(GstProjectM *plugin) plugin->hard_cut_sensitivity = DEFAULT_HARD_CUT_SENSITIVITY; plugin->soft_cut_duration = DEFAULT_SOFT_CUT_DURATION; plugin->preset_duration = DEFAULT_PRESET_DURATION; + plugin->shuffle_presets = DEFAULT_SHUFFLE_PRESETS; const gchar *meshSizeStr = DEFAULT_MESH_SIZE; gint width, height; @@ -250,7 +257,6 @@ static gboolean gst_projectm_setup(GstGLBaseAudioVisualizer *glav) { // Calculate required samples per frame bscope->req_spf = (bscope->ainfo.channels * bscope->ainfo.rate * 2) / bscope->vinfo.fps_n; - // get GStreamer video format and map it to the corresponding OpenGL pixel format const GstVideoFormat video_format = GST_VIDEO_INFO_FORMAT(&bscope->vinfo); @@ -423,6 +429,12 @@ static void gst_projectm_class_init(GstProjectMClass *klass) DEFAULT_PRESET_LOCKED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property(gobject_class, PROP_SHUFFLE_PRESETS, + g_param_spec_boolean("shuffle-presets", "Shuffle Presets", + "Enables or disables preset shuffling. When enabled, the visualizer randomly selects presets from the playlist if presets are provided and not locked.", + DEFAULT_SHUFFLE_PRESETS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gobject_class->finalize = gst_projectm_finalize; scope_class->supported_gl_api = GST_GL_API_OPENGL3 | GST_GL_API_GLES2; diff --git a/src/plugin.h b/src/plugin.h index 2f2377b..b453be5 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -30,6 +30,7 @@ struct _GstProjectM gboolean aspect_correction; gfloat easter_egg; gboolean preset_locked; + gboolean shuffle_presets; GstProjectMPrivate *priv; }; diff --git a/src/projectm.c b/src/projectm.c index b3391e6..e69ba38 100644 --- a/src/projectm.c +++ b/src/projectm.c @@ -13,8 +13,7 @@ GST_DEBUG_CATEGORY_STATIC(projectm_debug); #define GST_CAT_DEFAULT projectm_debug -projectm_handle projectm_init(GstProjectM *plugin) -{ +projectm_handle projectm_init(GstProjectM *plugin) { projectm_handle handle = NULL; projectm_playlist_handle playlist = NULL; GST_DEBUG_CATEGORY_INIT(projectm_debug, "projectm", @@ -54,7 +53,8 @@ projectm_handle projectm_init(GstProjectM *plugin) "mesh-size=(%lu, %lu)" "aspect-correction=%d, " "easter-egg=%f, " - "preset-locked=%d, ", + "preset-locked=%d, " + "shuffle-presets=%d", plugin->preset_path, plugin->texture_dir_path, plugin->beat_sensitivity, @@ -67,7 +67,8 @@ projectm_handle projectm_init(GstProjectM *plugin) plugin->mesh_height, plugin->aspect_correction, plugin->easter_egg, - plugin->preset_locked); + plugin->preset_locked, + plugin->shuffle_presets); // Load preset file if path is provided if (plugin->preset_path != NULL) { @@ -95,8 +96,9 @@ projectm_handle projectm_init(GstProjectM *plugin) projectm_set_preset_duration(handle, plugin->preset_duration); // kick off the first preset - if (projectm_playlist_size(playlist) > 0 && ! plugin->preset_locked) - projectm_playlist_play_next(playlist, true); + if (projectm_playlist_size(playlist) > 1 && ! plugin->preset_locked && plugin->shuffle_presets) { + projectm_playlist_play_next(playlist, true); + } } else { From 583cf18194046471865c492435a29836bd22621e Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sat, 6 Jul 2024 14:33:10 -0700 Subject: [PATCH 10/17] Add shuffle presets property --- src/projectm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/projectm.c b/src/projectm.c index e69ba38..c46147a 100644 --- a/src/projectm.c +++ b/src/projectm.c @@ -37,7 +37,7 @@ projectm_handle projectm_init(GstProjectM *plugin) { // initialize preset playlist playlist = projectm_playlist_create(handle); - projectm_playlist_set_shuffle(playlist, true); + projectm_playlist_set_shuffle(playlist, plugin->shuffle_presets); // projectm_playlist_set_preset_switched_event_callback(_playlist, &ProjectMWrapper::PresetSwitchedEvent, static_cast(this)); // Log properties From db201bfa97023ecaa75307150de1f77376edabdd Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sat, 6 Jul 2024 14:33:44 -0700 Subject: [PATCH 11/17] Add shuffle presets property --- src/projectm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/projectm.c b/src/projectm.c index c46147a..b8697a4 100644 --- a/src/projectm.c +++ b/src/projectm.c @@ -96,7 +96,7 @@ projectm_handle projectm_init(GstProjectM *plugin) { projectm_set_preset_duration(handle, plugin->preset_duration); // kick off the first preset - if (projectm_playlist_size(playlist) > 1 && ! plugin->preset_locked && plugin->shuffle_presets) { + if (projectm_playlist_size(playlist) > 1 && ! plugin->preset_locked) { projectm_playlist_play_next(playlist, true); } } From b80678f5caa444d5b96adccc05b0a3f33c253873 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Fri, 21 Mar 2025 21:44:20 -0700 Subject: [PATCH 12/17] Add option to disable playlist entirely --- src/config.h | 7 +- src/enums.h | 34 ++--- src/plugin.c | 390 ++++++++++++++++++++++++++++--------------------- src/plugin.h | 21 ++- src/projectm.c | 185 ++++++++++++----------- 5 files changed, 344 insertions(+), 293 deletions(-) diff --git a/src/config.h b/src/config.h index 6145a41..bc83e3b 100644 --- a/src/config.h +++ b/src/config.h @@ -11,7 +11,7 @@ G_BEGIN_DECLS #define PACKAGE "GstProjectM" #define PACKAGE_NAME "GstProjectM" -#define PACKAGE_VERSION "0.0.1" +#define PACKAGE_VERSION "0.0.2" #define PACKAGE_LICENSE "LGPL" #define PACKAGE_ORIGIN "https://github.com/projectM-visualizer/gst-projectm" @@ -31,8 +31,9 @@ G_BEGIN_DECLS #define DEFAULT_ASPECT_CORRECTION TRUE #define DEFAULT_EASTER_EGG 0.0 #define DEFAULT_PRESET_LOCKED FALSE -#define DEFAULT_SHUFFLE_PRESETS TRUE +#define DEFAULT_ENABLE_PLAYLIST TRUE +#define DEFAULT_SHUFFLE_PRESETS TRUE // depends on ENABLE_PLAYLIST G_END_DECLS -#endif /* __GST_PROJECTM_CONFIG_H__ */ \ No newline at end of file +#endif /* __GST_PROJECTM_CONFIG_H__ */ diff --git a/src/enums.h b/src/enums.h index 5b41757..863d677 100644 --- a/src/enums.h +++ b/src/enums.h @@ -9,24 +9,24 @@ G_BEGIN_DECLS * @brief Properties */ -enum -{ - PROP_0, - PROP_PRESET_PATH, - PROP_TEXTURE_DIR_PATH, - PROP_BEAT_SENSITIVITY, - PROP_HARD_CUT_DURATION, - PROP_HARD_CUT_ENABLED, - PROP_HARD_CUT_SENSITIVITY, - PROP_SOFT_CUT_DURATION, - PROP_PRESET_DURATION, - PROP_MESH_SIZE, - PROP_ASPECT_CORRECTION, - PROP_EASTER_EGG, - PROP_PRESET_LOCKED, - PROP_SHUFFLE_PRESETS, +enum { + PROP_0, + PROP_PRESET_PATH, + PROP_TEXTURE_DIR_PATH, + PROP_BEAT_SENSITIVITY, + PROP_HARD_CUT_DURATION, + PROP_HARD_CUT_ENABLED, + PROP_HARD_CUT_SENSITIVITY, + PROP_SOFT_CUT_DURATION, + PROP_PRESET_DURATION, + PROP_MESH_SIZE, + PROP_ASPECT_CORRECTION, + PROP_EASTER_EGG, + PROP_PRESET_LOCKED, + PROP_SHUFFLE_PRESETS, + PROP_ENABLE_PLAYLIST }; G_END_DECLS -#endif /* __GST_PROJECTM_ENUMS_H__ */ \ No newline at end of file +#endif /* __GST_PROJECTM_ENUMS_H__ */ diff --git a/src/plugin.c b/src/plugin.c index 3981f83..9735626 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -5,44 +5,43 @@ #ifdef USE_GLEW #include #endif -#include #include +#include #include #include -#include "plugin.h" #include "caps.h" #include "config.h" #include "debug.h" #include "enums.h" -#include "projectm.h" #include "gstglbaseaudiovisualizer.h" +#include "plugin.h" +#include "projectm.h" GST_DEBUG_CATEGORY_STATIC(gst_projectm_debug); #define GST_CAT_DEFAULT gst_projectm_debug -struct _GstProjectMPrivate -{ +struct _GstProjectMPrivate { GLenum gl_format; projectm_handle handle; }; -G_DEFINE_TYPE_WITH_CODE(GstProjectM, gst_projectm, GST_TYPE_GL_BASE_AUDIO_VISUALIZER, G_ADD_PRIVATE (GstProjectM) - GST_DEBUG_CATEGORY_INIT(gst_projectm_debug, - "gstprojectm", 0, - "Plugin Root")); +G_DEFINE_TYPE_WITH_CODE(GstProjectM, gst_projectm, + GST_TYPE_GL_BASE_AUDIO_VISUALIZER, + G_ADD_PRIVATE(GstProjectM) + GST_DEBUG_CATEGORY_INIT(gst_projectm_debug, + "gstprojectm", 0, + "Plugin Root")); void gst_projectm_set_property(GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec) -{ + const GValue *value, GParamSpec *pspec) { GstProjectM *plugin = GST_PROJECTM(object); const gchar *property_name = g_param_spec_get_name(pspec); GST_DEBUG_OBJECT(plugin, "set-property <%s>", property_name); - switch (property_id) - { + switch (property_id) { case PROP_PRESET_PATH: plugin->preset_path = g_strdup(g_value_get_string(value)); break; @@ -67,15 +66,13 @@ void gst_projectm_set_property(GObject *object, guint property_id, case PROP_PRESET_DURATION: plugin->preset_duration = g_value_get_double(value); break; - case PROP_MESH_SIZE: - { + case PROP_MESH_SIZE: { const gchar *meshSizeStr = g_value_get_string(value); gint width, height; gchar **parts = g_strsplit(meshSizeStr, ",", 2); - if (parts && g_strv_length(parts) == 2) - { + if (parts && g_strv_length(parts) == 2) { width = atoi(parts[0]); height = atoi(parts[1]); @@ -84,8 +81,7 @@ void gst_projectm_set_property(GObject *object, guint property_id, g_strfreev(parts); } - } - break; + } break; case PROP_ASPECT_CORRECTION: plugin->aspect_correction = g_value_get_boolean(value); break; @@ -95,6 +91,9 @@ void gst_projectm_set_property(GObject *object, guint property_id, case PROP_PRESET_LOCKED: plugin->preset_locked = g_value_get_boolean(value); break; + case PROP_ENABLE_PLAYLIST: + plugin->enable_playlist = g_value_get_boolean(value); + break; case PROP_SHUFFLE_PRESETS: plugin->shuffle_presets = g_value_get_boolean(value); break; @@ -105,15 +104,13 @@ void gst_projectm_set_property(GObject *object, guint property_id, } void gst_projectm_get_property(GObject *object, guint property_id, - GValue *value, GParamSpec *pspec) -{ + GValue *value, GParamSpec *pspec) { GstProjectM *plugin = GST_PROJECTM(object); const gchar *property_name = g_param_spec_get_name(pspec); GST_DEBUG_OBJECT(plugin, "get-property <%s>", property_name); - switch (property_id) - { + switch (property_id) { case PROP_PRESET_PATH: g_value_set_string(value, plugin->preset_path); break; @@ -138,9 +135,9 @@ void gst_projectm_get_property(GObject *object, guint property_id, case PROP_PRESET_DURATION: g_value_set_double(value, plugin->preset_duration); break; - case PROP_MESH_SIZE: - { - gchar *meshSizeStr = g_strdup_printf("%lu,%lu", plugin->mesh_width, plugin->mesh_height); + case PROP_MESH_SIZE: { + gchar *meshSizeStr = + g_strdup_printf("%lu,%lu", plugin->mesh_width, plugin->mesh_height); g_value_set_string(value, meshSizeStr); g_free(meshSizeStr); break; @@ -154,6 +151,9 @@ void gst_projectm_get_property(GObject *object, guint property_id, case PROP_PRESET_LOCKED: g_value_set_boolean(value, plugin->preset_locked); break; + case PROP_ENABLE_PLAYLIST: + g_value_set_boolean(value, plugin->enable_playlist); + break; case PROP_SHUFFLE_PRESETS: g_value_set_boolean(value, plugin->shuffle_presets); break; @@ -163,9 +163,8 @@ void gst_projectm_get_property(GObject *object, guint property_id, } } -static void gst_projectm_init(GstProjectM *plugin) -{ - plugin->priv = gst_projectm_get_instance_private (plugin); +static void gst_projectm_init(GstProjectM *plugin) { + plugin->priv = gst_projectm_get_instance_private(plugin); // Set default values for properties plugin->preset_path = DEFAULT_PRESET_PATH; @@ -176,6 +175,7 @@ static void gst_projectm_init(GstProjectM *plugin) plugin->hard_cut_sensitivity = DEFAULT_HARD_CUT_SENSITIVITY; plugin->soft_cut_duration = DEFAULT_SOFT_CUT_DURATION; plugin->preset_duration = DEFAULT_PRESET_DURATION; + plugin->enable_playlist = DEFAULT_ENABLE_PLAYLIST; plugin->shuffle_presets = DEFAULT_SHUFFLE_PRESETS; const gchar *meshSizeStr = DEFAULT_MESH_SIZE; @@ -183,8 +183,7 @@ static void gst_projectm_init(GstProjectM *plugin) gchar **parts = g_strsplit(meshSizeStr, ",", 2); - if (parts && g_strv_length(parts) == 2) - { + if (parts && g_strv_length(parts) == 2) { width = atoi(parts[0]); height = atoi(parts[1]); @@ -200,8 +199,7 @@ static void gst_projectm_init(GstProjectM *plugin) plugin->priv->handle = NULL; } -static void gst_projectm_finalize(GObject *object) -{ +static void gst_projectm_finalize(GObject *object) { GstProjectM *plugin = GST_PROJECTM(object); g_free(plugin->preset_path); g_free(plugin->texture_dir_path); @@ -210,16 +208,14 @@ static void gst_projectm_finalize(GObject *object) static void gst_projectm_gl_stop(GstGLBaseAudioVisualizer *src) { GstProjectM *plugin = GST_PROJECTM(src); - if (plugin->priv->handle) - { - GST_DEBUG_OBJECT(plugin, "Destroying ProjectM instance"); - projectm_destroy(plugin->priv->handle); - plugin->priv->handle = NULL; + if (plugin->priv->handle) { + GST_DEBUG_OBJECT(plugin, "Destroying ProjectM instance"); + projectm_destroy(plugin->priv->handle); + plugin->priv->handle = NULL; } } -static gboolean gst_projectm_gl_start(GstGLBaseAudioVisualizer *glav) -{ +static gboolean gst_projectm_gl_start(GstGLBaseAudioVisualizer *glav) { // Cast the audio visualizer to the ProjectM plugin GstProjectM *plugin = GST_PROJECTM(glav); @@ -233,8 +229,7 @@ static gboolean gst_projectm_gl_start(GstGLBaseAudioVisualizer *glav) #endif // Check if ProjectM instance exists, and create if not - if (!plugin->priv->handle) - { + if (!plugin->priv->handle) { // Create ProjectM instance plugin->priv->handle = projectm_init(plugin); if (!plugin->priv->handle) { @@ -252,50 +247,54 @@ static gboolean gst_projectm_setup(GstGLBaseAudioVisualizer *glav) { GstProjectM *plugin = GST_PROJECTM(glav); // Calculate depth based on pixel stride and bits - gint depth = bscope->vinfo.finfo->pixel_stride[0] * ((bscope->vinfo.finfo->bits >= 8) ? 8 : 1); + gint depth = bscope->vinfo.finfo->pixel_stride[0] * + ((bscope->vinfo.finfo->bits >= 8) ? 8 : 1); // Calculate required samples per frame - bscope->req_spf = (bscope->ainfo.channels * bscope->ainfo.rate * 2) / bscope->vinfo.fps_n; + bscope->req_spf = + (bscope->ainfo.channels * bscope->ainfo.rate * 2) / bscope->vinfo.fps_n; - // get GStreamer video format and map it to the corresponding OpenGL pixel format + // get GStreamer video format and map it to the corresponding OpenGL pixel + // format const GstVideoFormat video_format = GST_VIDEO_INFO_FORMAT(&bscope->vinfo); - // TODO: why is the reversed byte order needed when copying pixel data from OpenGL ? + // TODO: why is the reversed byte order needed when copying pixel data from + // OpenGL ? switch (video_format) { - case GST_VIDEO_FORMAT_ABGR: - plugin->priv->gl_format = GL_RGBA; - break; + case GST_VIDEO_FORMAT_ABGR: + plugin->priv->gl_format = GL_RGBA; + break; - case GST_VIDEO_FORMAT_RGBA: - // GL_ABGR_EXT does not seem to be well-supported, does not work on Windows - plugin->priv->gl_format = GL_ABGR_EXT; - break; + case GST_VIDEO_FORMAT_RGBA: + // GL_ABGR_EXT does not seem to be well-supported, does not work on Windows + plugin->priv->gl_format = GL_ABGR_EXT; + break; - default: - GST_ERROR_OBJECT(plugin, "Unsupported video format: %d", video_format); - return FALSE; + default: + GST_ERROR_OBJECT(plugin, "Unsupported video format: %d", video_format); + return FALSE; } // Log audio info - GST_DEBUG_OBJECT(glav, - "Audio Information ", - bscope->ainfo.channels, bscope->ainfo.rate, bscope->ainfo.finfo->description); + GST_DEBUG_OBJECT( + glav, "Audio Information ", + bscope->ainfo.channels, bscope->ainfo.rate, + bscope->ainfo.finfo->description); // Log video info GST_DEBUG_OBJECT(glav, - "Video Information ", + "Video Information ", GST_VIDEO_INFO_WIDTH(&bscope->vinfo), - GST_VIDEO_INFO_HEIGHT(&bscope->vinfo), - bscope->vinfo.fps_n, bscope->vinfo.fps_d, - depth, bscope->req_spf); + GST_VIDEO_INFO_HEIGHT(&bscope->vinfo), bscope->vinfo.fps_n, + bscope->vinfo.fps_d, depth, bscope->req_spf); return TRUE; } - // TODO: CLEANUP & ADD DEBUGGING -static gboolean gst_projectm_render(GstGLBaseAudioVisualizer *glav, GstBuffer *audio, GstVideoFrame *video) -{ +static gboolean gst_projectm_render(GstGLBaseAudioVisualizer *glav, + GstBuffer *audio, GstVideoFrame *video) { GstProjectM *plugin = GST_PROJECTM(glav); GstMapInfo audioMap; @@ -304,12 +303,17 @@ static gboolean gst_projectm_render(GstGLBaseAudioVisualizer *glav, GstBuffer *a // AUDIO gst_buffer_map(audio, &audioMap, GST_MAP_READ); - // GST_DEBUG_OBJECT(plugin, "Audio Samples: %u, Offset: %lu, Offset End: %lu, Sample Rate: %d, FPS: %d, Required Samples Per Frame: %d", - // audioMap.size / 8, audio->offset, audio->offset_end, bscope->ainfo.rate, bscope->vinfo.fps_n, bscope->req_spf); + // GST_DEBUG_OBJECT(plugin, "Audio Samples: %u, Offset: %lu, Offset End: %lu, + // Sample Rate: %d, FPS: %d, Required Samples Per Frame: %d", + // audioMap.size / 8, audio->offset, audio->offset_end, + // bscope->ainfo.rate, bscope->vinfo.fps_n, bscope->req_spf); - projectm_pcm_add_int16(plugin->priv->handle, (gint16 *)audioMap.data, audioMap.size / 4, PROJECTM_STEREO); + projectm_pcm_add_int16(plugin->priv->handle, (gint16 *)audioMap.data, + audioMap.size / 4, PROJECTM_STEREO); - // GST_DEBUG_OBJECT(plugin, "Audio Data: %d %d %d %d", ((gint16 *)audioMap.data)[100], ((gint16 *)audioMap.data)[101], ((gint16 *)audioMap.data)[102], ((gint16 *)audioMap.data)[103]); + // GST_DEBUG_OBJECT(plugin, "Audio Data: %d %d %d %d", ((gint16 + // *)audioMap.data)[100], ((gint16 *)audioMap.data)[101], ((gint16 + // *)audioMap.data)[102], ((gint16 *)audioMap.data)[103]); // VIDEO const GstGLFuncs *glFunctions = glav->context->gl_vtable; @@ -321,119 +325,168 @@ static gboolean gst_projectm_render(GstGLBaseAudioVisualizer *glav, GstBuffer *a projectm_opengl_render_frame(plugin->priv->handle); gl_error_handler(glav->context, plugin); - glFunctions->ReadPixels(0, 0, windowWidth, windowHeight, plugin->priv->gl_format, GL_UNSIGNED_INT_8_8_8_8, (guint8 *)GST_VIDEO_FRAME_PLANE_DATA(video, 0)); + glFunctions->ReadPixels(0, 0, windowWidth, windowHeight, + plugin->priv->gl_format, GL_UNSIGNED_INT_8_8_8_8, + (guint8 *)GST_VIDEO_FRAME_PLANE_DATA(video, 0)); gst_buffer_unmap(audio, &audioMap); - // GST_DEBUG_OBJECT(plugin, "Video Data: %d %d\n", GST_VIDEO_FRAME_N_PLANES(video), ((uint8_t *)(GST_VIDEO_FRAME_PLANE_DATA(video, 0)))[0]); + // GST_DEBUG_OBJECT(plugin, "Video Data: %d %d\n", + // GST_VIDEO_FRAME_N_PLANES(video), ((uint8_t + // *)(GST_VIDEO_FRAME_PLANE_DATA(video, 0)))[0]); // GST_DEBUG_OBJECT(plugin, "Rendered one frame"); return result; } -static void gst_projectm_class_init(GstProjectMClass *klass) -{ +static void gst_projectm_class_init(GstProjectMClass *klass) { GObjectClass *gobject_class = (GObjectClass *)klass; GstElementClass *element_class = (GstElementClass *)klass; - GstGLBaseAudioVisualizerClass *scope_class = GST_GL_BASE_AUDIO_VISUALIZER_CLASS(klass); + GstGLBaseAudioVisualizerClass *scope_class = + GST_GL_BASE_AUDIO_VISUALIZER_CLASS(klass); // Setup audio and video caps const gchar *audio_sink_caps = get_audio_sink_cap(0); const gchar *video_src_caps = get_video_src_cap(0); - gst_element_class_add_pad_template(GST_ELEMENT_CLASS(klass), - gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS, - gst_caps_from_string(video_src_caps))); - gst_element_class_add_pad_template(GST_ELEMENT_CLASS(klass), - gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - gst_caps_from_string(audio_sink_caps))); - - gst_element_class_set_static_metadata(GST_ELEMENT_CLASS(klass), - "ProjectM Visualizer", "Generic", "A plugin for visualizing music using ProjectM", - "AnomieVision | Tristan Charpentier "); + gst_element_class_add_pad_template( + GST_ELEMENT_CLASS(klass), + gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS, + gst_caps_from_string(video_src_caps))); + gst_element_class_add_pad_template( + GST_ELEMENT_CLASS(klass), + gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + gst_caps_from_string(audio_sink_caps))); + + gst_element_class_set_static_metadata( + GST_ELEMENT_CLASS(klass), "ProjectM Visualizer", "Generic", + "A plugin for visualizing music using ProjectM", + "AnomieVision | Tristan Charpentier " + ""); // Setup properties gobject_class->set_property = gst_projectm_set_property; gobject_class->get_property = gst_projectm_get_property; - g_object_class_install_property(gobject_class, PROP_PRESET_PATH, - g_param_spec_string("preset", "Preset", - "Specifies the path to the preset file. The preset file determines the visual style and behavior of the audio visualizer.", - DEFAULT_PRESET_PATH, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(gobject_class, PROP_TEXTURE_DIR_PATH, - g_param_spec_string("texture-dir", "Texture Directory", - "Sets the path to the directory containing textures used in the visualizer.", - DEFAULT_TEXTURE_DIR_PATH, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(gobject_class, PROP_BEAT_SENSITIVITY, - g_param_spec_float("beat-sensitivity", "Beat Sensitivity", - "Controls the sensitivity to audio beats. Higher values make the visualizer respond more strongly to beats.", - 0.0, 5.0, DEFAULT_BEAT_SENSITIVITY, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(gobject_class, PROP_HARD_CUT_DURATION, - g_param_spec_double("hard-cut-duration", "Hard Cut Duration", - "Sets the duration, in seconds, for hard cuts. Hard cuts are abrupt transitions in the visualizer.", - 0.0, 999999.0, DEFAULT_HARD_CUT_DURATION, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(gobject_class, PROP_HARD_CUT_ENABLED, - g_param_spec_boolean("hard-cut-enabled", "Hard Cut Enabled", - "Enables or disables hard cuts. When enabled, the visualizer may exhibit sudden transitions based on the audio input.", - DEFAULT_HARD_CUT_ENABLED, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(gobject_class, PROP_HARD_CUT_SENSITIVITY, - g_param_spec_float("hard-cut-sensitivity", "Hard Cut Sensitivity", - "Adjusts the sensitivity of the visualizer to hard cuts. Higher values increase the responsiveness to abrupt changes in audio.", - 0.0, 1.0, DEFAULT_HARD_CUT_SENSITIVITY, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(gobject_class, PROP_SOFT_CUT_DURATION, - g_param_spec_double("soft-cut-duration", "Soft Cut Duration", - "Sets the duration, in seconds, for soft cuts. Soft cuts are smoother transitions between visualizer states.", - 0.0, 999999.0, DEFAULT_SOFT_CUT_DURATION, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(gobject_class, PROP_PRESET_DURATION, - g_param_spec_double("preset-duration", "Preset Duration", - "Sets the duration, in seconds, for each preset. A zero value causes the preset to play indefinitely.", - 0.0, 999999.0, DEFAULT_PRESET_DURATION, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(gobject_class, PROP_MESH_SIZE, - g_param_spec_string("mesh-size", "Mesh Size", - "Sets the size of the mesh used in rendering. The format is 'width,height'.", - DEFAULT_MESH_SIZE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(gobject_class, PROP_ASPECT_CORRECTION, - g_param_spec_boolean("aspect-correction", "Aspect Correction", - "Enables or disables aspect ratio correction. When enabled, the visualizer adjusts for aspect ratio differences in rendering.", - DEFAULT_ASPECT_CORRECTION, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(gobject_class, PROP_EASTER_EGG, - g_param_spec_float("easter-egg", "Easter Egg", - "DControls the activation of an Easter Egg feature. The value determines the likelihood of triggering the Easter Egg.", - 0.0, 1.0, DEFAULT_EASTER_EGG, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(gobject_class, PROP_PRESET_LOCKED, - g_param_spec_boolean("preset-locked", "Preset Locked", - "Locks or unlocks the current preset. When locked, the visualizer remains on the current preset without automatic changes.", - DEFAULT_PRESET_LOCKED, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property(gobject_class, PROP_SHUFFLE_PRESETS, - g_param_spec_boolean("shuffle-presets", "Shuffle Presets", - "Enables or disables preset shuffling. When enabled, the visualizer randomly selects presets from the playlist if presets are provided and not locked.", - DEFAULT_SHUFFLE_PRESETS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property( + gobject_class, PROP_PRESET_PATH, + g_param_spec_string( + "preset", "Preset", + "Specifies the path to the preset file. The preset file determines " + "the visual style and behavior of the audio visualizer.", + DEFAULT_PRESET_PATH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_TEXTURE_DIR_PATH, + g_param_spec_string("texture-dir", "Texture Directory", + "Sets the path to the directory containing textures " + "used in the visualizer.", + DEFAULT_TEXTURE_DIR_PATH, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_BEAT_SENSITIVITY, + g_param_spec_float( + "beat-sensitivity", "Beat Sensitivity", + "Controls the sensitivity to audio beats. Higher values make the " + "visualizer respond more strongly to beats.", + 0.0, 5.0, DEFAULT_BEAT_SENSITIVITY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_HARD_CUT_DURATION, + g_param_spec_double("hard-cut-duration", "Hard Cut Duration", + "Sets the duration, in seconds, for hard cuts. Hard " + "cuts are abrupt transitions in the visualizer.", + 0.0, 999999.0, DEFAULT_HARD_CUT_DURATION, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_HARD_CUT_ENABLED, + g_param_spec_boolean( + "hard-cut-enabled", "Hard Cut Enabled", + "Enables or disables hard cuts. When enabled, the visualizer may " + "exhibit sudden transitions based on the audio input.", + DEFAULT_HARD_CUT_ENABLED, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_HARD_CUT_SENSITIVITY, + g_param_spec_float( + "hard-cut-sensitivity", "Hard Cut Sensitivity", + "Adjusts the sensitivity of the visualizer to hard cuts. Higher " + "values increase the responsiveness to abrupt changes in audio.", + 0.0, 1.0, DEFAULT_HARD_CUT_SENSITIVITY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_SOFT_CUT_DURATION, + g_param_spec_double( + "soft-cut-duration", "Soft Cut Duration", + "Sets the duration, in seconds, for soft cuts. Soft cuts are " + "smoother transitions between visualizer states.", + 0.0, 999999.0, DEFAULT_SOFT_CUT_DURATION, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_PRESET_DURATION, + g_param_spec_double("preset-duration", "Preset Duration", + "Sets the duration, in seconds, for each preset. A " + "zero value causes the preset to play indefinitely.", + 0.0, 999999.0, DEFAULT_PRESET_DURATION, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_MESH_SIZE, + g_param_spec_string("mesh-size", "Mesh Size", + "Sets the size of the mesh used in rendering. The " + "format is 'width,height'.", + DEFAULT_MESH_SIZE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_ASPECT_CORRECTION, + g_param_spec_boolean( + "aspect-correction", "Aspect Correction", + "Enables or disables aspect ratio correction. When enabled, the " + "visualizer adjusts for aspect ratio differences in rendering.", + DEFAULT_ASPECT_CORRECTION, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_EASTER_EGG, + g_param_spec_float( + "easter-egg", "Easter Egg", + "Controls the activation of an Easter Egg feature. The value " + "determines the likelihood of triggering the Easter Egg.", + 0.0, 1.0, DEFAULT_EASTER_EGG, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_PRESET_LOCKED, + g_param_spec_boolean( + "preset-locked", "Preset Locked", + "Locks or unlocks the current preset. When locked, the visualizer " + "remains on the current preset without automatic changes.", + DEFAULT_PRESET_LOCKED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_ENABLE_PLAYLIST, + g_param_spec_boolean( + "enable-playlist", "Enable Playlist", + "Enables or disables the playlist feature. When enabled, the " + "visualizer can switch between presets based on a provided playlist.", + DEFAULT_ENABLE_PLAYLIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_SHUFFLE_PRESETS, + g_param_spec_boolean( + "shuffle-presets", "Shuffle Presets", + "Enables or disables preset shuffling. When enabled, the visualizer " + "randomly selects presets from the playlist if presets are provided " + "and not locked. Playlist must be enabled for this to take effect.", + DEFAULT_SHUFFLE_PRESETS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gobject_class->finalize = gst_projectm_finalize; @@ -444,16 +497,15 @@ static void gst_projectm_class_init(GstProjectMClass *klass) scope_class->setup = GST_DEBUG_FUNCPTR(gst_projectm_setup); } -static gboolean plugin_init(GstPlugin *plugin) -{ +static gboolean plugin_init(GstPlugin *plugin) { GST_DEBUG_CATEGORY_INIT(gst_projectm_debug, "projectm", 0, "projectM visualizer plugin"); - return gst_element_register(plugin, "projectm", GST_RANK_NONE, GST_TYPE_PROJECTM); + return gst_element_register(plugin, "projectm", GST_RANK_NONE, + GST_TYPE_PROJECTM); } - - GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, projectm, - "plugin to visualize audio using the ProjectM library", plugin_init, - PACKAGE_VERSION, PACKAGE_LICENSE, PACKAGE_NAME, PACKAGE_ORIGIN) \ No newline at end of file + "plugin to visualize audio using the ProjectM library", + plugin_init, PACKAGE_VERSION, PACKAGE_LICENSE, PACKAGE_NAME, + PACKAGE_ORIGIN) diff --git a/src/plugin.h b/src/plugin.h index b453be5..de1acff 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -1,19 +1,18 @@ #ifndef __GST_PROJECTM_H__ #define __GST_PROJECTM_H__ -#include #include "gstglbaseaudiovisualizer.h" +#include typedef struct _GstProjectMPrivate GstProjectMPrivate; G_BEGIN_DECLS #define GST_TYPE_PROJECTM (gst_projectm_get_type()) -G_DECLARE_FINAL_TYPE(GstProjectM, gst_projectm, GST, - PROJECTM, GstGLBaseAudioVisualizer) +G_DECLARE_FINAL_TYPE(GstProjectM, gst_projectm, GST, PROJECTM, + GstGLBaseAudioVisualizer) -struct _GstProjectM -{ +struct _GstProjectM { GstGLBaseAudioVisualizer element; gchar *preset_path; @@ -30,19 +29,18 @@ struct _GstProjectM gboolean aspect_correction; gfloat easter_egg; gboolean preset_locked; + gboolean enable_playlist; gboolean shuffle_presets; GstProjectMPrivate *priv; }; -struct _GstProjectMClass -{ +struct _GstProjectMClass { GstAudioVisualizerClass parent_class; }; static void gst_projectm_set_property(GObject *object, guint prop_id, - const GValue *value, - GParamSpec *pspec); + const GValue *value, GParamSpec *pspec); static void gst_projectm_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); @@ -55,7 +53,8 @@ static gboolean gst_projectm_gl_start(GstGLBaseAudioVisualizer *glav); static void gst_projectm_gl_stop(GstGLBaseAudioVisualizer *glav); -static gboolean gst_projectm_render(GstGLBaseAudioVisualizer *glav, GstBuffer *audio, GstVideoFrame *video); +static gboolean gst_projectm_render(GstGLBaseAudioVisualizer *glav, + GstBuffer *audio, GstVideoFrame *video); static void gst_projectm_class_init(GstProjectMClass *klass); @@ -65,4 +64,4 @@ static gboolean gst_projectm_setup(GstGLBaseAudioVisualizer *glav); G_END_DECLS -#endif /* __GST_PROJECTM_H__ */ \ No newline at end of file +#endif /* __GST_PROJECTM_H__ */ diff --git a/src/projectm.c b/src/projectm.c index b8697a4..ef385f7 100644 --- a/src/projectm.c +++ b/src/projectm.c @@ -4,123 +4,122 @@ #include -#include #include +#include -#include "projectm.h" #include "plugin.h" +#include "projectm.h" GST_DEBUG_CATEGORY_STATIC(projectm_debug); #define GST_CAT_DEFAULT projectm_debug projectm_handle projectm_init(GstProjectM *plugin) { - projectm_handle handle = NULL; - projectm_playlist_handle playlist = NULL; - GST_DEBUG_CATEGORY_INIT(projectm_debug, "projectm", - 0, "ProjectM"); + projectm_handle handle = NULL; + projectm_playlist_handle playlist = NULL; + GST_DEBUG_CATEGORY_INIT(projectm_debug, "projectm", 0, "ProjectM"); - GstAudioVisualizer *bscope = GST_AUDIO_VISUALIZER(plugin); + GstAudioVisualizer *bscope = GST_AUDIO_VISUALIZER(plugin); - // Create ProjectM instance - GST_DEBUG_OBJECT(plugin, "Creating projectM instance.."); - handle = projectm_create(); + // Create ProjectM instance + GST_DEBUG_OBJECT(plugin, "Creating projectM instance.."); + handle = projectm_create(); - if (!handle) - { - GST_DEBUG_OBJECT(plugin, "project_create() returned NULL, projectM instance was not created!"); - return NULL; - } - else - { - GST_DEBUG_OBJECT(plugin, "Created projectM instance!"); - } + if (!handle) { + GST_DEBUG_OBJECT( + plugin, + "project_create() returned NULL, projectM instance was not created!"); + return NULL; + } else { + GST_DEBUG_OBJECT(plugin, "Created projectM instance!"); + } + + if (plugin->enable_playlist) { + GST_DEBUG_OBJECT(plugin, "Playlist enabled"); // initialize preset playlist playlist = projectm_playlist_create(handle); projectm_playlist_set_shuffle(playlist, plugin->shuffle_presets); - // projectm_playlist_set_preset_switched_event_callback(_playlist, &ProjectMWrapper::PresetSwitchedEvent, static_cast(this)); - - // Log properties - GST_INFO_OBJECT(plugin, "Using Properties: " - "preset=%s, " - "texture-dir=%s, " - "beat-sensitivity=%f, " - "hard-cut-duration=%f, " - "hard-cut-enabled=%d, " - "hard-cut-sensitivity=%f, " - "soft-cut-duration=%f, " - "preset-duration=%f, " - "mesh-size=(%lu, %lu)" - "aspect-correction=%d, " - "easter-egg=%f, " - "preset-locked=%d, " - "shuffle-presets=%d", - plugin->preset_path, - plugin->texture_dir_path, - plugin->beat_sensitivity, - plugin->hard_cut_duration, - plugin->hard_cut_enabled, - plugin->hard_cut_sensitivity, - plugin->soft_cut_duration, - plugin->preset_duration, - plugin->mesh_width, - plugin->mesh_height, - plugin->aspect_correction, - plugin->easter_egg, - plugin->preset_locked, - plugin->shuffle_presets); - - // Load preset file if path is provided - if (plugin->preset_path != NULL) { - int added_count = projectm_playlist_add_path(playlist, plugin->preset_path, true, false); - GST_INFO("Loaded preset path: %s, presets found: %d", plugin->preset_path, added_count); - } - - // Set texture search path if directory path is provided - if (plugin->texture_dir_path != NULL) - { - const gchar *texturePaths[1] = {plugin->texture_dir_path}; - projectm_set_texture_search_paths(handle, texturePaths, 1); - } - - // Set properties - projectm_set_beat_sensitivity(handle, plugin->beat_sensitivity); - projectm_set_hard_cut_duration(handle, plugin->hard_cut_duration); - projectm_set_hard_cut_enabled(handle, plugin->hard_cut_enabled); - projectm_set_hard_cut_sensitivity(handle, plugin->hard_cut_sensitivity); - projectm_set_soft_cut_duration(handle, plugin->soft_cut_duration); - - // Set preset duration, or set to in infinite duration if zero - if (plugin->preset_duration > 0.0) - { - projectm_set_preset_duration(handle, plugin->preset_duration); - - // kick off the first preset - if (projectm_playlist_size(playlist) > 1 && ! plugin->preset_locked) { - projectm_playlist_play_next(playlist, true); - } - } - else - { - projectm_set_preset_duration(handle, 999999.0); + // projectm_playlist_set_preset_switched_event_callback(_playlist, + // &ProjectMWrapper::PresetSwitchedEvent, static_cast(this)); + } else { + GST_DEBUG_OBJECT(plugin, "Playlist disabled"); + } + + // Log properties + GST_INFO_OBJECT(plugin, + "Using Properties: " + "preset=%s, " + "texture-dir=%s, " + "beat-sensitivity=%f, " + "hard-cut-duration=%f, " + "hard-cut-enabled=%d, " + "hard-cut-sensitivity=%f, " + "soft-cut-duration=%f, " + "preset-duration=%f, " + "mesh-size=(%lu, %lu)" + "aspect-correction=%d, " + "easter-egg=%f, " + "preset-locked=%d, " + "shuffle-presets=%d", + plugin->preset_path, plugin->texture_dir_path, + plugin->beat_sensitivity, plugin->hard_cut_duration, + plugin->hard_cut_enabled, plugin->hard_cut_sensitivity, + plugin->soft_cut_duration, plugin->preset_duration, + plugin->mesh_width, plugin->mesh_height, + plugin->aspect_correction, plugin->easter_egg, + plugin->preset_locked, plugin->shuffle_presets); + + // Load preset file if path is provided + if (plugin->preset_path != NULL) { + int added_count = + projectm_playlist_add_path(playlist, plugin->preset_path, true, false); + GST_INFO("Loaded preset path: %s, presets found: %d", plugin->preset_path, + added_count); + } + + // Set texture search path if directory path is provided + if (plugin->texture_dir_path != NULL) { + const gchar *texturePaths[1] = {plugin->texture_dir_path}; + projectm_set_texture_search_paths(handle, texturePaths, 1); + } + + // Set properties + projectm_set_beat_sensitivity(handle, plugin->beat_sensitivity); + projectm_set_hard_cut_duration(handle, plugin->hard_cut_duration); + projectm_set_hard_cut_enabled(handle, plugin->hard_cut_enabled); + projectm_set_hard_cut_sensitivity(handle, plugin->hard_cut_sensitivity); + projectm_set_soft_cut_duration(handle, plugin->soft_cut_duration); + + // Set preset duration, or set to in infinite duration if zero + if (plugin->preset_duration > 0.0) { + projectm_set_preset_duration(handle, plugin->preset_duration); + + // kick off the first preset + if (projectm_playlist_size(playlist) > 1 && !plugin->preset_locked) { + projectm_playlist_play_next(playlist, true); } + } else { + projectm_set_preset_duration(handle, 999999.0); + } - projectm_set_mesh_size(handle, plugin->mesh_width, plugin->mesh_height); - projectm_set_aspect_correction(handle, plugin->aspect_correction); - projectm_set_easter_egg(handle, plugin->easter_egg); - projectm_set_preset_locked(handle, plugin->preset_locked); + projectm_set_mesh_size(handle, plugin->mesh_width, plugin->mesh_height); + projectm_set_aspect_correction(handle, plugin->aspect_correction); + projectm_set_easter_egg(handle, plugin->easter_egg); + projectm_set_preset_locked(handle, plugin->preset_locked); - projectm_set_fps(handle, GST_VIDEO_INFO_FPS_N(&bscope->vinfo)); - projectm_set_window_size(handle, GST_VIDEO_INFO_WIDTH(&bscope->vinfo), GST_VIDEO_INFO_HEIGHT(&bscope->vinfo)); + projectm_set_fps(handle, GST_VIDEO_INFO_FPS_N(&bscope->vinfo)); + projectm_set_window_size(handle, GST_VIDEO_INFO_WIDTH(&bscope->vinfo), + GST_VIDEO_INFO_HEIGHT(&bscope->vinfo)); - return handle; + return handle; } // void projectm_render(GstProjectM *plugin, gint16 *samples, gint sample_count) // { // GST_DEBUG_OBJECT(plugin, "Rendering %d samples", sample_count); -// projectm_pcm_add_int16(plugin->handle, samples, sample_count, PROJECTM_STEREO); +// projectm_pcm_add_int16(plugin->handle, samples, sample_count, +// PROJECTM_STEREO); // projectm_opengl_render_frame(plugin->handle); -// } \ No newline at end of file +// } From d7d1039db409f41e6b0d89ad09ca1ca1f30bdcbf Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Mon, 24 Mar 2025 19:06:24 -0700 Subject: [PATCH 13/17] retry CI From 5c3061851e677fa334de2872a486b14f8b3e3b0c Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Wed, 26 Mar 2025 08:49:41 -0700 Subject: [PATCH 14/17] retry CI From 4578d21d97b4200dd6b8312202c9f78adf748ba3 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Fri, 4 Apr 2025 20:38:08 -0700 Subject: [PATCH 15/17] formatting --- src/caps.c | 30 +- src/debug.c | 75 +-- src/gstglbaseaudiovisualizer.c | 884 +++++++++++++++++---------------- src/gstglbaseaudiovisualizer.h | 67 +-- src/projectm.c | 1 + src/projectm.h | 3 +- 6 files changed, 542 insertions(+), 518 deletions(-) diff --git a/src/caps.c b/src/caps.c index 8001ead..09b5add 100644 --- a/src/caps.c +++ b/src/caps.c @@ -12,20 +12,19 @@ GST_DEBUG_CATEGORY_STATIC(gst_projectm_caps_debug); #define GST_CAT_DEFAULT gst_projectm_caps_debug -const gchar *get_audio_sink_cap(unsigned int type) -{ +const gchar *get_audio_sink_cap(unsigned int type) { const char *format; - switch (type) - { + switch (type) { case 0: - format = GST_AUDIO_CAPS_MAKE("audio/x-raw, " - "format = (string) " GST_AUDIO_NE( - S16) ", " - "layout = (string) interleaved, " - "channels = (int) { 2 }, " - "rate = (int) { 44100 }, " - "channel-mask = (bitmask) { 0x0003 }"); + format = + GST_AUDIO_CAPS_MAKE("audio/x-raw, " + "format = (string) " GST_AUDIO_NE( + S16) ", " + "layout = (string) interleaved, " + "channels = (int) { 2 }, " + "rate = (int) { 44100 }, " + "channel-mask = (bitmask) { 0x0003 }"); break; default: format = NULL; @@ -35,14 +34,13 @@ const gchar *get_audio_sink_cap(unsigned int type) return format; } -const gchar *get_video_src_cap(unsigned int type) -{ +const gchar *get_video_src_cap(unsigned int type) { const char *format; - switch (type) - { + switch (type) { case 0: - format = GST_VIDEO_CAPS_MAKE("video/x-raw, format = (string) { ABGR }, framerate=(fraction)[0/1,MAX]"); + format = GST_VIDEO_CAPS_MAKE("video/x-raw, format = (string) { ABGR }, " + "framerate=(fraction)[0/1,MAX]"); break; default: format = NULL; diff --git a/src/debug.c b/src/debug.c index d50a189..9a948aa 100644 --- a/src/debug.c +++ b/src/debug.c @@ -7,41 +7,44 @@ #include "debug.h" -void gl_error_handler(GstGLContext *context, gpointer data) -{ - GLuint error = context->gl_vtable->GetError(); +void gl_error_handler(GstGLContext *context, gpointer data) { + GLuint error = context->gl_vtable->GetError(); - switch (error) - { - case GL_NO_ERROR: - // No error - break; - case GL_INVALID_ENUM: - g_error("OpenGL Error: GL_INVALID_ENUM - Enumeration parameter is not legal\n"); - break; - case GL_INVALID_VALUE: - g_error("OpenGL Error: GL_INVALID_VALUE - Value parameter is not legal\n"); - break; - case GL_INVALID_OPERATION: - g_error("OpenGL Error: GL_INVALID_OPERATION - Set of state is not legal for the parameters given\n"); - break; - case GL_STACK_OVERFLOW: - g_error("OpenGL Error: GL_STACK_OVERFLOW - Stack pushing operation would overflow\n"); - break; - case GL_STACK_UNDERFLOW: - g_error("OpenGL Error: GL_STACK_UNDERFLOW - Stack popping operation would underflow\n"); - break; - case GL_OUT_OF_MEMORY: - g_error("OpenGL Error: GL_OUT_OF_MEMORY - Memory allocation failed\n"); - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - g_error("OpenGL Error: GL_INVALID_FRAMEBUFFER_OPERATION - Incomplete framebuffer operation\n"); - break; - case GL_CONTEXT_LOST: - g_error("OpenGL Error: GL_CONTEXT_LOST - OpenGL context lost\n"); - break; - default: - g_error("OpenGL Error: Unknown error code - 0x%x\n", error); - break; - } + switch (error) { + case GL_NO_ERROR: + // No error + break; + case GL_INVALID_ENUM: + g_error( + "OpenGL Error: GL_INVALID_ENUM - Enumeration parameter is not legal\n"); + break; + case GL_INVALID_VALUE: + g_error("OpenGL Error: GL_INVALID_VALUE - Value parameter is not legal\n"); + break; + case GL_INVALID_OPERATION: + g_error("OpenGL Error: GL_INVALID_OPERATION - Set of state is not legal " + "for the parameters given\n"); + break; + case GL_STACK_OVERFLOW: + g_error("OpenGL Error: GL_STACK_OVERFLOW - Stack pushing operation would " + "overflow\n"); + break; + case GL_STACK_UNDERFLOW: + g_error("OpenGL Error: GL_STACK_UNDERFLOW - Stack popping operation would " + "underflow\n"); + break; + case GL_OUT_OF_MEMORY: + g_error("OpenGL Error: GL_OUT_OF_MEMORY - Memory allocation failed\n"); + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + g_error("OpenGL Error: GL_INVALID_FRAMEBUFFER_OPERATION - Incomplete " + "framebuffer operation\n"); + break; + case GL_CONTEXT_LOST: + g_error("OpenGL Error: GL_CONTEXT_LOST - OpenGL context lost\n"); + break; + default: + g_error("OpenGL Error: Unknown error code - 0x%x\n", error); + break; + } } \ No newline at end of file diff --git a/src/gstglbaseaudiovisualizer.c b/src/gstglbaseaudiovisualizer.c index e88acc8..e1f786a 100644 --- a/src/gstglbaseaudiovisualizer.c +++ b/src/gstglbaseaudiovisualizer.c @@ -25,7 +25,8 @@ /* * The code in this file is based on code from * GStreamer / gst-plugins-base / 1.19.2: gst-libs/gst/gl/gstglbasesrc.c - * Git Repository: https://github.com/GStreamer/gst-plugins-base/blob/master/gst-libs/gst/gl/gstglbasesrc.c + * Git Repository: + * https://github.com/GStreamer/gst-plugins-base/blob/master/gst-libs/gst/gl/gstglbasesrc.c * Original copyright notice has been retained at the top of this file. */ @@ -33,545 +34,554 @@ #include "config.h" #endif -#include #include "gstglbaseaudiovisualizer.h" +#include /** * SECTION:GstGLBaseAudioVisualizer - * @short_description: #GstAudioVisualizer subclass for injecting OpenGL resources in a pipeline + * @short_description: #GstAudioVisualizer subclass for injecting OpenGL + * resources in a pipeline * @title: GstGLBaseAudioVisualizer * @see_also: #GstAudioVisualizer * * Wrapper for GstAudioVisualizer for handling OpenGL contexts. * - * #GstGLBaseAudioVisualizer handles the nitty gritty details of retrieving an OpenGL - * context. It also provides `gl_start()` and `gl_stop()` virtual methods - * that ensure an OpenGL context is available and current in the calling thread for initializing and cleaning up - * OpenGL dependent resources. - * The `gl_render` virtual method is used to perform OpenGL rendering. + * #GstGLBaseAudioVisualizer handles the nitty gritty details of retrieving an + * OpenGL context. It also provides `gl_start()` and `gl_stop()` virtual methods + * that ensure an OpenGL context is available and current in the calling thread + * for initializing and cleaning up OpenGL dependent resources. The `gl_render` + * virtual method is used to perform OpenGL rendering. */ #define GST_CAT_DEFAULT gst_gl_base_audio_visualizer_debug -GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); +GST_DEBUG_CATEGORY_STATIC(GST_CAT_DEFAULT); -struct _GstGLBaseAudioVisualizerPrivate -{ - GstGLContext *other_context; +struct _GstGLBaseAudioVisualizerPrivate { + GstGLContext *other_context; - gint64 n_frames; /* total frames sent */ - gboolean gl_result; - gboolean gl_started; + gint64 n_frames; /* total frames sent */ + gboolean gl_result; + gboolean gl_started; - GRecMutex context_lock; + GRecMutex context_lock; }; /* Properties */ -enum -{ - PROP_0 -}; +enum { PROP_0 }; #define gst_gl_base_audio_visualizer_parent_class parent_class -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstGLBaseAudioVisualizer, gst_gl_base_audio_visualizer, - GST_TYPE_AUDIO_VISUALIZER, G_ADD_PRIVATE (GstGLBaseAudioVisualizer) - GST_DEBUG_CATEGORY_INIT (gst_gl_base_audio_visualizer_debug, - "glbaseaudiovisualizer", 0, "glbaseaudiovisualizer element"); -); - -static void gst_gl_base_audio_visualizer_finalize (GObject * object); -static void gst_gl_base_audio_visualizer_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_gl_base_audio_visualizer_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static void gst_gl_base_audio_visualizer_set_context (GstElement * element, - GstContext * context); -static GstStateChangeReturn gst_gl_base_audio_visualizer_change_state (GstElement * element, - GstStateChange transition); - -static gboolean gst_gl_base_audio_visualizer_render (GstAudioVisualizer *bscope, GstBuffer *audio, GstVideoFrame *video); -static void gst_gl_base_audio_visualizer_start (GstGLBaseAudioVisualizer * glav); -static void gst_gl_base_audio_visualizer_stop (GstGLBaseAudioVisualizer * glav); -static gboolean gst_gl_base_audio_visualizer_decide_allocation (GstAudioVisualizer * gstav, - GstQuery * query); - -static gboolean gst_gl_base_audio_visualizer_default_setup (GstGLBaseAudioVisualizer * glav); -static gboolean gst_gl_base_audio_visualizer_default_gl_start (GstGLBaseAudioVisualizer * glav); -static void gst_gl_base_audio_visualizer_default_gl_stop (GstGLBaseAudioVisualizer * glav); -static gboolean gst_gl_base_audio_visualizer_default_gl_render (GstGLBaseAudioVisualizer * glav, - GstBuffer* audio, GstVideoFrame *video); - -static gboolean gst_gl_base_audio_visualizer_find_gl_context_unlocked (GstGLBaseAudioVisualizer * glav); - -static gboolean gst_gl_base_audio_visualizer_setup(GstAudioVisualizer * gstav); +G_DEFINE_ABSTRACT_TYPE_WITH_CODE( + GstGLBaseAudioVisualizer, gst_gl_base_audio_visualizer, + GST_TYPE_AUDIO_VISUALIZER, + G_ADD_PRIVATE(GstGLBaseAudioVisualizer) + GST_DEBUG_CATEGORY_INIT(gst_gl_base_audio_visualizer_debug, + "glbaseaudiovisualizer", 0, + "glbaseaudiovisualizer element");); + +static void gst_gl_base_audio_visualizer_finalize(GObject *object); +static void gst_gl_base_audio_visualizer_set_property(GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gst_gl_base_audio_visualizer_get_property(GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gst_gl_base_audio_visualizer_set_context(GstElement *element, + GstContext *context); +static GstStateChangeReturn +gst_gl_base_audio_visualizer_change_state(GstElement *element, + GstStateChange transition); + +static gboolean gst_gl_base_audio_visualizer_render(GstAudioVisualizer *bscope, + GstBuffer *audio, + GstVideoFrame *video); +static void gst_gl_base_audio_visualizer_start(GstGLBaseAudioVisualizer *glav); +static void gst_gl_base_audio_visualizer_stop(GstGLBaseAudioVisualizer *glav); +static gboolean +gst_gl_base_audio_visualizer_decide_allocation(GstAudioVisualizer *gstav, + GstQuery *query); +static gboolean +gst_gl_base_audio_visualizer_default_setup(GstGLBaseAudioVisualizer *glav); +static gboolean +gst_gl_base_audio_visualizer_default_gl_start(GstGLBaseAudioVisualizer *glav); static void -gst_gl_base_audio_visualizer_class_init (GstGLBaseAudioVisualizerClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstAudioVisualizerClass *gstav_class = GST_AUDIO_VISUALIZER_CLASS (klass); - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - gobject_class->finalize = gst_gl_base_audio_visualizer_finalize; - gobject_class->set_property = gst_gl_base_audio_visualizer_set_property; - gobject_class->get_property = gst_gl_base_audio_visualizer_get_property; - - element_class->set_context = GST_DEBUG_FUNCPTR (gst_gl_base_audio_visualizer_set_context); +gst_gl_base_audio_visualizer_default_gl_stop(GstGLBaseAudioVisualizer *glav); +static gboolean gst_gl_base_audio_visualizer_default_gl_render( + GstGLBaseAudioVisualizer *glav, GstBuffer *audio, GstVideoFrame *video); - element_class->change_state = - GST_DEBUG_FUNCPTR (gst_gl_base_audio_visualizer_change_state); +static gboolean gst_gl_base_audio_visualizer_find_gl_context_unlocked( + GstGLBaseAudioVisualizer *glav); - gstav_class->decide_allocation = GST_DEBUG_FUNCPTR (gst_gl_base_audio_visualizer_decide_allocation); - gstav_class->setup = GST_DEBUG_FUNCPTR (gst_gl_base_audio_visualizer_setup); +static gboolean gst_gl_base_audio_visualizer_setup(GstAudioVisualizer *gstav); - gstav_class->render = GST_DEBUG_FUNCPTR (gst_gl_base_audio_visualizer_render); - - klass->supported_gl_api = GST_GL_API_ANY; - klass->gl_start = GST_DEBUG_FUNCPTR (gst_gl_base_audio_visualizer_default_gl_start); - klass->gl_stop = GST_DEBUG_FUNCPTR (gst_gl_base_audio_visualizer_default_gl_stop); - klass->gl_render = GST_DEBUG_FUNCPTR (gst_gl_base_audio_visualizer_default_gl_render); - klass->setup = GST_DEBUG_FUNCPTR (gst_gl_base_audio_visualizer_default_setup); +static void +gst_gl_base_audio_visualizer_class_init(GstGLBaseAudioVisualizerClass *klass) { + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + GstAudioVisualizerClass *gstav_class = GST_AUDIO_VISUALIZER_CLASS(klass); + GstElementClass *element_class = GST_ELEMENT_CLASS(klass); + + gobject_class->finalize = gst_gl_base_audio_visualizer_finalize; + gobject_class->set_property = gst_gl_base_audio_visualizer_set_property; + gobject_class->get_property = gst_gl_base_audio_visualizer_get_property; + + element_class->set_context = + GST_DEBUG_FUNCPTR(gst_gl_base_audio_visualizer_set_context); + + element_class->change_state = + GST_DEBUG_FUNCPTR(gst_gl_base_audio_visualizer_change_state); + + gstav_class->decide_allocation = + GST_DEBUG_FUNCPTR(gst_gl_base_audio_visualizer_decide_allocation); + gstav_class->setup = GST_DEBUG_FUNCPTR(gst_gl_base_audio_visualizer_setup); + + gstav_class->render = GST_DEBUG_FUNCPTR(gst_gl_base_audio_visualizer_render); + + klass->supported_gl_api = GST_GL_API_ANY; + klass->gl_start = + GST_DEBUG_FUNCPTR(gst_gl_base_audio_visualizer_default_gl_start); + klass->gl_stop = + GST_DEBUG_FUNCPTR(gst_gl_base_audio_visualizer_default_gl_stop); + klass->gl_render = + GST_DEBUG_FUNCPTR(gst_gl_base_audio_visualizer_default_gl_render); + klass->setup = GST_DEBUG_FUNCPTR(gst_gl_base_audio_visualizer_default_setup); } -static void -gst_gl_base_audio_visualizer_init (GstGLBaseAudioVisualizer * glav) -{ - glav->priv = gst_gl_base_audio_visualizer_get_instance_private (glav); - glav->priv->gl_started = FALSE; - glav->priv->gl_result = TRUE; - glav->context = NULL; - g_rec_mutex_init (&glav->priv->context_lock); - gst_gl_base_audio_visualizer_start(glav); +static void gst_gl_base_audio_visualizer_init(GstGLBaseAudioVisualizer *glav) { + glav->priv = gst_gl_base_audio_visualizer_get_instance_private(glav); + glav->priv->gl_started = FALSE; + glav->priv->gl_result = TRUE; + glav->context = NULL; + g_rec_mutex_init(&glav->priv->context_lock); + gst_gl_base_audio_visualizer_start(glav); } -static void -gst_gl_base_audio_visualizer_finalize (GObject * object) -{ - GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER (object); - gst_gl_base_audio_visualizer_stop(glav); +static void gst_gl_base_audio_visualizer_finalize(GObject *object) { + GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER(object); + gst_gl_base_audio_visualizer_stop(glav); - g_rec_mutex_clear (&glav->priv->context_lock); + g_rec_mutex_clear(&glav->priv->context_lock); - G_OBJECT_CLASS (parent_class)->finalize (object); + G_OBJECT_CLASS(parent_class)->finalize(object); } -static void -gst_gl_base_audio_visualizer_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER (object); - - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } +static void gst_gl_base_audio_visualizer_set_property(GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { + GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER(object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } } -static void -gst_gl_base_audio_visualizer_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER (object); - - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } +static void gst_gl_base_audio_visualizer_get_property(GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) { + GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER(object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } } - -static void -gst_gl_base_audio_visualizer_set_context (GstElement * element, GstContext * context) -{ - GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER (element); - GstGLBaseAudioVisualizerClass *klass = GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS (glav); - GstGLDisplay *old_display, *new_display; - - g_rec_mutex_lock (&glav->priv->context_lock); - old_display = glav->display ? gst_object_ref (glav->display) : NULL; - gst_gl_handle_set_context (element, context, &glav->display, - &glav->priv->other_context); - if (glav->display) - gst_gl_display_filter_gl_api (glav->display, klass->supported_gl_api); - new_display = glav->display ? gst_object_ref (glav->display) : NULL; - - if (old_display && new_display) { - if (old_display != new_display) { - gst_clear_object (&glav->context); - if (gst_gl_base_audio_visualizer_find_gl_context_unlocked (glav)) { - // TODO does this need to be handled ? - //gst_pad_mark_reconfigure (GST_BASE_SRC_PAD (glav)); - } - } +static void gst_gl_base_audio_visualizer_set_context(GstElement *element, + GstContext *context) { + GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER(element); + GstGLBaseAudioVisualizerClass *klass = + GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS(glav); + GstGLDisplay *old_display, *new_display; + + g_rec_mutex_lock(&glav->priv->context_lock); + old_display = glav->display ? gst_object_ref(glav->display) : NULL; + gst_gl_handle_set_context(element, context, &glav->display, + &glav->priv->other_context); + if (glav->display) + gst_gl_display_filter_gl_api(glav->display, klass->supported_gl_api); + new_display = glav->display ? gst_object_ref(glav->display) : NULL; + + if (old_display && new_display) { + if (old_display != new_display) { + gst_clear_object(&glav->context); + if (gst_gl_base_audio_visualizer_find_gl_context_unlocked(glav)) { + // TODO does this need to be handled ? + // gst_pad_mark_reconfigure (GST_BASE_SRC_PAD (glav)); + } } - gst_clear_object (&old_display); - gst_clear_object (&new_display); - g_rec_mutex_unlock (&glav->priv->context_lock); + } + gst_clear_object(&old_display); + gst_clear_object(&new_display); + g_rec_mutex_unlock(&glav->priv->context_lock); - GST_ELEMENT_CLASS (parent_class)->set_context (element, context); + GST_ELEMENT_CLASS(parent_class)->set_context(element, context); } static gboolean -gst_gl_base_audio_visualizer_default_gl_start (GstGLBaseAudioVisualizer * glav) -{ - return TRUE; +gst_gl_base_audio_visualizer_default_gl_start(GstGLBaseAudioVisualizer *glav) { + return TRUE; } static gboolean -gst_gl_base_audio_visualizer_default_setup (GstGLBaseAudioVisualizer * glav) -{ - return TRUE; +gst_gl_base_audio_visualizer_default_setup(GstGLBaseAudioVisualizer *glav) { + return TRUE; } -static void -gst_gl_base_audio_visualizer_gl_start (GstGLContext * context, gpointer data) -{ - GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER (data); - GstGLBaseAudioVisualizerClass *glav_class = GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS (glav); +static void gst_gl_base_audio_visualizer_gl_start(GstGLContext *context, + gpointer data) { + GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER(data); + GstGLBaseAudioVisualizerClass *glav_class = + GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS(glav); - GST_INFO_OBJECT (glav, "starting"); - gst_gl_insert_debug_marker (glav->context, - "starting element %s", GST_OBJECT_NAME (glav)); + GST_INFO_OBJECT(glav, "starting"); + gst_gl_insert_debug_marker(glav->context, "starting element %s", + GST_OBJECT_NAME(glav)); - glav->priv->gl_started = glav_class->gl_start (glav); + glav->priv->gl_started = glav_class->gl_start(glav); } static void -gst_gl_base_audio_visualizer_default_gl_stop (GstGLBaseAudioVisualizer * glav) -{ -} +gst_gl_base_audio_visualizer_default_gl_stop(GstGLBaseAudioVisualizer *glav) {} -static void -gst_gl_base_audio_visualizer_gl_stop (GstGLContext * context, gpointer data) -{ - GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER (data); - GstGLBaseAudioVisualizerClass *glav_class = GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS (glav); +static void gst_gl_base_audio_visualizer_gl_stop(GstGLContext *context, + gpointer data) { + GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER(data); + GstGLBaseAudioVisualizerClass *glav_class = + GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS(glav); - GST_INFO_OBJECT (glav, "stopping"); - gst_gl_insert_debug_marker (glav->context, - "stopping element %s", GST_OBJECT_NAME (glav)); + GST_INFO_OBJECT(glav, "stopping"); + gst_gl_insert_debug_marker(glav->context, "stopping element %s", + GST_OBJECT_NAME(glav)); - if (glav->priv->gl_started) - glav_class->gl_stop (glav); + if (glav->priv->gl_started) + glav_class->gl_stop(glav); - glav->priv->gl_started = FALSE; + glav->priv->gl_started = FALSE; } -static gboolean gst_gl_base_audio_visualizer_setup(GstAudioVisualizer * gstav) { - GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER (gstav); - GstGLBaseAudioVisualizerClass *glav_class = GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS(gstav); +static gboolean gst_gl_base_audio_visualizer_setup(GstAudioVisualizer *gstav) { + GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER(gstav); + GstGLBaseAudioVisualizerClass *glav_class = + GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS(gstav); - // cascade setup to the derived plugin after gl initialization has been completed - return glav_class->setup(glav); + // cascade setup to the derived plugin after gl initialization has been + // completed + return glav_class->setup(glav); } -static gboolean -gst_gl_base_audio_visualizer_default_gl_render (GstGLBaseAudioVisualizer * glav, GstBuffer* audio, GstVideoFrame *video) -{ - return TRUE; +static gboolean gst_gl_base_audio_visualizer_default_gl_render( + GstGLBaseAudioVisualizer *glav, GstBuffer *audio, GstVideoFrame *video) { + return TRUE; } typedef struct { - GstGLBaseAudioVisualizer *glav; - GstBuffer *in_audio; - GstVideoFrame *out_video; + GstGLBaseAudioVisualizer *glav; + GstBuffer *in_audio; + GstVideoFrame *out_video; } GstGLRenderCallbackParams; static void -gst_gl_base_audio_visualizer_gl_thread_render_callback (gpointer params) -{ - GstGLRenderCallbackParams* cb_params = (GstGLRenderCallbackParams*)params; - GstGLBaseAudioVisualizerClass *klass = GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS (cb_params->glav); - - // inside gl thread: call virtual render function with audio and video - cb_params->glav->priv->gl_result = klass->gl_render (cb_params->glav, cb_params->in_audio, cb_params->out_video); +gst_gl_base_audio_visualizer_gl_thread_render_callback(gpointer params) { + GstGLRenderCallbackParams *cb_params = (GstGLRenderCallbackParams *)params; + GstGLBaseAudioVisualizerClass *klass = + GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS(cb_params->glav); + + // inside gl thread: call virtual render function with audio and video + cb_params->glav->priv->gl_result = klass->gl_render( + cb_params->glav, cb_params->in_audio, cb_params->out_video); } -static gboolean -gst_gl_base_audio_visualizer_render (GstAudioVisualizer *bscope, GstBuffer *audio, GstVideoFrame *video) -{ - GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER (bscope); - GstGLRenderCallbackParams cb_params; - GstGLWindow *window; - - g_rec_mutex_lock (&glav->priv->context_lock); - - // wrap params into cb_params struct to pass them to the GL window/thread via userdata pointer - cb_params.glav = glav; - cb_params.in_audio = audio; - cb_params.out_video = video; - - window = gst_gl_context_get_window (glav->context); - - // dispatch render call through the gl thread - // call is blocking, accessing audio and video params from gl thread *should* be safe - gst_gl_window_send_message(window, GST_GL_WINDOW_CB(gst_gl_base_audio_visualizer_gl_thread_render_callback), - &cb_params); - - gst_object_unref(window); - - g_rec_mutex_unlock (&glav->priv->context_lock); - - if (glav->priv->gl_result) { - glav->priv->n_frames++; - } else { - // gl error - GST_ELEMENT_ERROR (glav, RESOURCE, NOT_FOUND, (("failed to render audio visualizer")), - (("A GL error occurred"))); - } - - return glav->priv->gl_result; +static gboolean gst_gl_base_audio_visualizer_render(GstAudioVisualizer *bscope, + GstBuffer *audio, + GstVideoFrame *video) { + GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER(bscope); + GstGLRenderCallbackParams cb_params; + GstGLWindow *window; + + g_rec_mutex_lock(&glav->priv->context_lock); + + // wrap params into cb_params struct to pass them to the GL window/thread via + // userdata pointer + cb_params.glav = glav; + cb_params.in_audio = audio; + cb_params.out_video = video; + + window = gst_gl_context_get_window(glav->context); + + // dispatch render call through the gl thread + // call is blocking, accessing audio and video params from gl thread *should* + // be safe + gst_gl_window_send_message( + window, + GST_GL_WINDOW_CB(gst_gl_base_audio_visualizer_gl_thread_render_callback), + &cb_params); + + gst_object_unref(window); + + g_rec_mutex_unlock(&glav->priv->context_lock); + + if (glav->priv->gl_result) { + glav->priv->n_frames++; + } else { + // gl error + GST_ELEMENT_ERROR(glav, RESOURCE, NOT_FOUND, + (("failed to render audio visualizer")), + (("A GL error occurred"))); + } + + return glav->priv->gl_result; } -static void -gst_gl_base_audio_visualizer_start (GstGLBaseAudioVisualizer * glav) -{ - glav->priv->n_frames = 0; +static void gst_gl_base_audio_visualizer_start(GstGLBaseAudioVisualizer *glav) { + glav->priv->n_frames = 0; } -static void -gst_gl_base_audio_visualizer_stop (GstGLBaseAudioVisualizer * glav) -{ - g_rec_mutex_lock (&glav->priv->context_lock); +static void gst_gl_base_audio_visualizer_stop(GstGLBaseAudioVisualizer *glav) { + g_rec_mutex_lock(&glav->priv->context_lock); - if (glav->context) { - if (glav->priv->gl_started) - gst_gl_context_thread_add (glav->context, gst_gl_base_audio_visualizer_gl_stop, glav); + if (glav->context) { + if (glav->priv->gl_started) + gst_gl_context_thread_add(glav->context, + gst_gl_base_audio_visualizer_gl_stop, glav); - gst_object_unref (glav->context); - } + gst_object_unref(glav->context); + } - glav->context = NULL; - g_rec_mutex_unlock (&glav->priv->context_lock); + glav->context = NULL; + g_rec_mutex_unlock(&glav->priv->context_lock); } static gboolean -_find_local_gl_context_unlocked (GstGLBaseAudioVisualizer * glav) -{ - GstGLContext *context, *prev_context; - gboolean ret; +_find_local_gl_context_unlocked(GstGLBaseAudioVisualizer *glav) { + GstGLContext *context, *prev_context; + gboolean ret; - if (glav->context && glav->context->display == glav->display) - return TRUE; - - context = prev_context = glav->context; - g_rec_mutex_unlock (&glav->priv->context_lock); - /* we need to drop the lock to query as another element may also be - * performing a context query on us which would also attempt to take the - * context_lock. Our query could block on the same lock in the other element. - */ - ret = - gst_gl_query_local_gl_context (GST_ELEMENT (glav), GST_PAD_SRC, &context); - g_rec_mutex_lock (&glav->priv->context_lock); - if (ret) { - if (glav->context != prev_context) { - /* we need to recheck everything since we dropped the lock and the - * context has changed */ - if (glav->context && glav->context->display == glav->display) { - if (context != glav->context) - gst_clear_object (&context); - return TRUE; - } - } + if (glav->context && glav->context->display == glav->display) + return TRUE; - if (context->display == glav->display) { - glav->context = context; - return TRUE; - } + context = prev_context = glav->context; + g_rec_mutex_unlock(&glav->priv->context_lock); + /* we need to drop the lock to query as another element may also be + * performing a context query on us which would also attempt to take the + * context_lock. Our query could block on the same lock in the other element. + */ + ret = gst_gl_query_local_gl_context(GST_ELEMENT(glav), GST_PAD_SRC, &context); + g_rec_mutex_lock(&glav->priv->context_lock); + if (ret) { + if (glav->context != prev_context) { + /* we need to recheck everything since we dropped the lock and the + * context has changed */ + if (glav->context && glav->context->display == glav->display) { if (context != glav->context) - gst_clear_object (&context); + gst_clear_object(&context); + return TRUE; + } } - return FALSE; -} -static gboolean -gst_gl_base_audio_visualizer_find_gl_context_unlocked (GstGLBaseAudioVisualizer * glav) -{ - GstGLBaseAudioVisualizerClass *klass = GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS (glav); - GError *error = NULL; - gboolean new_context = FALSE; - - GST_DEBUG_OBJECT (glav, "attempting to find an OpenGL context, existing %" - GST_PTR_FORMAT, glav->context); - - if (!glav->context) - new_context = TRUE; - - if (!gst_gl_ensure_element_data (glav, &glav->display, - &glav->priv->other_context)) - return FALSE; - - gst_gl_display_filter_gl_api (glav->display, klass->supported_gl_api); - - _find_local_gl_context_unlocked (glav); - - if (!glav->context) { - GST_OBJECT_LOCK (glav->display); - do { - if (glav->context) { - gst_object_unref (glav->context); - glav->context = NULL; - } - /* just get a GL context. we don't care */ - glav->context = - gst_gl_display_get_gl_context_for_thread (glav->display, NULL); - if (!glav->context) { - if (!gst_gl_display_create_context (glav->display, - glav->priv->other_context, &glav->context, &error)) { - GST_OBJECT_UNLOCK (glav->display); - goto context_error; - } - } - } while (!gst_gl_display_add_context (glav->display, glav->context)); - GST_OBJECT_UNLOCK (glav->display); + if (context->display == glav->display) { + glav->context = context; + return TRUE; } - GST_INFO_OBJECT (glav, "found OpenGL context %" GST_PTR_FORMAT, glav->context); - - if (new_context || !glav->priv->gl_started) { - if (glav->priv->gl_started) - gst_gl_context_thread_add (glav->context, gst_gl_base_audio_visualizer_gl_stop, glav); - - { - if ((gst_gl_context_get_gl_api (glav-> - context) & klass->supported_gl_api) == 0) - goto unsupported_gl_api; - } + if (context != glav->context) + gst_clear_object(&context); + } + return FALSE; +} - gst_gl_context_thread_add (glav->context, gst_gl_base_audio_visualizer_gl_start, glav); +static gboolean gst_gl_base_audio_visualizer_find_gl_context_unlocked( + GstGLBaseAudioVisualizer *glav) { + GstGLBaseAudioVisualizerClass *klass = + GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS(glav); + GError *error = NULL; + gboolean new_context = FALSE; - if (!glav->priv->gl_started) - goto error; - } + GST_DEBUG_OBJECT( + glav, "attempting to find an OpenGL context, existing %" GST_PTR_FORMAT, + glav->context); - return TRUE; + if (!glav->context) + new_context = TRUE; - unsupported_gl_api: - { - GstGLAPI gl_api = gst_gl_context_get_gl_api (glav->context); - gchar *gl_api_str = gst_gl_api_to_string (gl_api); - gchar *supported_gl_api_str = - gst_gl_api_to_string (klass->supported_gl_api); - GST_ELEMENT_ERROR (glav, RESOURCE, BUSY, - ("GL API's not compatible context: %s supported: %s", gl_api_str, - supported_gl_api_str), (NULL)); - - g_free (supported_gl_api_str); - g_free (gl_api_str); - return FALSE; - } - context_error: - { - if (error) { - GST_ELEMENT_ERROR (glav, RESOURCE, NOT_FOUND, ("%s", error->message), - (NULL)); - g_clear_error (&error); - } else { - GST_ELEMENT_ERROR (glav, RESOURCE, NOT_FOUND, (NULL), (NULL)); - } - if (glav->context) - gst_object_unref (glav->context); - glav->context = NULL; - return FALSE; - } - error: - { - GST_ELEMENT_ERROR (glav, LIBRARY, INIT, - ("Subclass failed to initialize."), (NULL)); - return FALSE; - } -} + if (!gst_gl_ensure_element_data(glav, &glav->display, + &glav->priv->other_context)) + return FALSE; -static gboolean -gst_gl_base_audio_visualizer_decide_allocation (GstAudioVisualizer * gstav, GstQuery * query) -{ - GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER (gstav); - GstGLContext *context; - GstBufferPool *pool = NULL; - GstStructure *config; - GstCaps *caps; - guint min, max, size; - gboolean update_pool; - - g_rec_mutex_lock (&glav->priv->context_lock); - if (!gst_gl_base_audio_visualizer_find_gl_context_unlocked (glav)) { - g_rec_mutex_unlock (&glav->priv->context_lock); - return FALSE; - } - context = gst_object_ref (glav->context); - g_rec_mutex_unlock (&glav->priv->context_lock); + gst_gl_display_filter_gl_api(glav->display, klass->supported_gl_api); - gst_query_parse_allocation (query, &caps, NULL); + _find_local_gl_context_unlocked(glav); - if (gst_query_get_n_allocation_pools (query) > 0) { - gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); + if (!glav->context) { + GST_OBJECT_LOCK(glav->display); + do { + if (glav->context) { + gst_object_unref(glav->context); + glav->context = NULL; + } + /* just get a GL context. we don't care */ + glav->context = + gst_gl_display_get_gl_context_for_thread(glav->display, NULL); + if (!glav->context) { + if (!gst_gl_display_create_context(glav->display, + glav->priv->other_context, + &glav->context, &error)) { + GST_OBJECT_UNLOCK(glav->display); + goto context_error; + } + } + } while (!gst_gl_display_add_context(glav->display, glav->context)); + GST_OBJECT_UNLOCK(glav->display); + } + GST_INFO_OBJECT(glav, "found OpenGL context %" GST_PTR_FORMAT, glav->context); - update_pool = TRUE; - } else { - GstVideoInfo vinfo; + if (new_context || !glav->priv->gl_started) { + if (glav->priv->gl_started) + gst_gl_context_thread_add(glav->context, + gst_gl_base_audio_visualizer_gl_stop, glav); - gst_video_info_init (&vinfo); - gst_video_info_from_caps (&vinfo, caps); - size = vinfo.size; - min = max = 0; - update_pool = FALSE; + { + if ((gst_gl_context_get_gl_api(glav->context) & + klass->supported_gl_api) == 0) + goto unsupported_gl_api; } - if (!pool || !GST_IS_GL_BUFFER_POOL (pool)) { - /* can't use this pool */ - if (pool) - gst_object_unref (pool); - pool = gst_gl_buffer_pool_new (context); - } - config = gst_buffer_pool_get_config (pool); + gst_gl_context_thread_add(glav->context, + gst_gl_base_audio_visualizer_gl_start, glav); - gst_buffer_pool_config_set_params (config, caps, size, min, max); - gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); - if (gst_query_find_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, NULL)) - gst_buffer_pool_config_add_option (config, - GST_BUFFER_POOL_OPTION_GL_SYNC_META); - gst_buffer_pool_config_add_option (config, - GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META); + if (!glav->priv->gl_started) + goto error; + } - gst_buffer_pool_set_config (pool, config); + return TRUE; - if (update_pool) - gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); - else - gst_query_add_allocation_pool (query, pool, size, min, max); +unsupported_gl_api: { + GstGLAPI gl_api = gst_gl_context_get_gl_api(glav->context); + gchar *gl_api_str = gst_gl_api_to_string(gl_api); + gchar *supported_gl_api_str = gst_gl_api_to_string(klass->supported_gl_api); + GST_ELEMENT_ERROR(glav, RESOURCE, BUSY, + ("GL API's not compatible context: %s supported: %s", + gl_api_str, supported_gl_api_str), + (NULL)); - gst_object_unref (pool); - gst_object_unref (context); + g_free(supported_gl_api_str); + g_free(gl_api_str); + return FALSE; +} +context_error: { + if (error) { + GST_ELEMENT_ERROR(glav, RESOURCE, NOT_FOUND, ("%s", error->message), + (NULL)); + g_clear_error(&error); + } else { + GST_ELEMENT_ERROR(glav, RESOURCE, NOT_FOUND, (NULL), (NULL)); + } + if (glav->context) + gst_object_unref(glav->context); + glav->context = NULL; + return FALSE; +} +error: { + GST_ELEMENT_ERROR(glav, LIBRARY, INIT, ("Subclass failed to initialize."), + (NULL)); + return FALSE; +} +} - return TRUE; +static gboolean +gst_gl_base_audio_visualizer_decide_allocation(GstAudioVisualizer *gstav, + GstQuery *query) { + GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER(gstav); + GstGLContext *context; + GstBufferPool *pool = NULL; + GstStructure *config; + GstCaps *caps; + guint min, max, size; + gboolean update_pool; + + g_rec_mutex_lock(&glav->priv->context_lock); + if (!gst_gl_base_audio_visualizer_find_gl_context_unlocked(glav)) { + g_rec_mutex_unlock(&glav->priv->context_lock); + return FALSE; + } + context = gst_object_ref(glav->context); + g_rec_mutex_unlock(&glav->priv->context_lock); + + gst_query_parse_allocation(query, &caps, NULL); + + if (gst_query_get_n_allocation_pools(query) > 0) { + gst_query_parse_nth_allocation_pool(query, 0, &pool, &size, &min, &max); + + update_pool = TRUE; + } else { + GstVideoInfo vinfo; + + gst_video_info_init(&vinfo); + gst_video_info_from_caps(&vinfo, caps); + size = vinfo.size; + min = max = 0; + update_pool = FALSE; + } + + if (!pool || !GST_IS_GL_BUFFER_POOL(pool)) { + /* can't use this pool */ + if (pool) + gst_object_unref(pool); + pool = gst_gl_buffer_pool_new(context); + } + config = gst_buffer_pool_get_config(pool); + + gst_buffer_pool_config_set_params(config, caps, size, min, max); + gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META); + if (gst_query_find_allocation_meta(query, GST_GL_SYNC_META_API_TYPE, NULL)) + gst_buffer_pool_config_add_option(config, + GST_BUFFER_POOL_OPTION_GL_SYNC_META); + gst_buffer_pool_config_add_option( + config, GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META); + + gst_buffer_pool_set_config(pool, config); + + if (update_pool) + gst_query_set_nth_allocation_pool(query, 0, pool, size, min, max); + else + gst_query_add_allocation_pool(query, pool, size, min, max); + + gst_object_unref(pool); + gst_object_unref(context); + + return TRUE; } static GstStateChangeReturn -gst_gl_base_audio_visualizer_change_state (GstElement * element, GstStateChange transition) -{ - GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER (element); - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - - GST_DEBUG_OBJECT (glav, "changing state: %s => %s", - gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)), - gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition))); - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_NULL: - g_rec_mutex_lock (&glav->priv->context_lock); - gst_clear_object (&glav->priv->other_context); - gst_clear_object (&glav->display); - g_rec_mutex_unlock (&glav->priv->context_lock); - break; - default: - break; - } - +gst_gl_base_audio_visualizer_change_state(GstElement *element, + GstStateChange transition) { + GstGLBaseAudioVisualizer *glav = GST_GL_BASE_AUDIO_VISUALIZER(element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + GST_DEBUG_OBJECT( + glav, "changing state: %s => %s", + gst_element_state_get_name(GST_STATE_TRANSITION_CURRENT(transition)), + gst_element_state_get_name(GST_STATE_TRANSITION_NEXT(transition))); + + ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) return ret; -} + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + g_rec_mutex_lock(&glav->priv->context_lock); + gst_clear_object(&glav->priv->other_context); + gst_clear_object(&glav->display); + g_rec_mutex_unlock(&glav->priv->context_lock); + break; + default: + break; + } + + return ret; +} diff --git a/src/gstglbaseaudiovisualizer.h b/src/gstglbaseaudiovisualizer.h index 89df80a..a48781b 100644 --- a/src/gstglbaseaudiovisualizer.h +++ b/src/gstglbaseaudiovisualizer.h @@ -24,7 +24,8 @@ /* * The code in this file is based on code from * GStreamer / gst-plugins-base / 1.19.2: gst-libs/gst/gl/gstglbasesrc.h - * Git Repository: https://github.com/GStreamer/gst-plugins-base/blob/master/gst-libs/gst/gl/gstglbasesrc.h + * Git Repository: + * https://github.com/GStreamer/gst-plugins-base/blob/master/gst-libs/gst/gl/gstglbasesrc.h * Original copyright notice has been retained at the top of this file. */ @@ -32,28 +33,36 @@ #define __GST_GL_BASE_AUDIO_VISUALIZER_H__ #include -#include #include +#include #include typedef struct _GstGLBaseAudioVisualizer GstGLBaseAudioVisualizer; typedef struct _GstGLBaseAudioVisualizerClass GstGLBaseAudioVisualizerClass; typedef struct _GstGLBaseAudioVisualizerPrivate GstGLBaseAudioVisualizerPrivate; -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstGLBaseAudioVisualizer , gst_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstGLBaseAudioVisualizer, gst_object_unref) G_BEGIN_DECLS GST_GL_API GType gst_gl_base_audio_visualizer_get_type(void); - -#define GST_TYPE_GL_BASE_AUDIO_VISUALIZER (gst_gl_base_audio_visualizer_get_type()) -#define GST_GL_BASE_AUDIO_VISUALIZER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_BASE_AUDIO_VISUALIZER,GstGLBaseAudioVisualizer)) -#define GST_GL_BASE_AUDIO_VISUALIZER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GL_BASE_AUDIO_VISUALIZER,GstGLBaseAudioVisualizerClass)) -#define GST_IS_GL_BASE_AUDIO_VISUALIZER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_BASE_AUDIO_VISUALIZER)) -#define GST_IS_GL_BASE_AUDIO_VISUALIZER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GL_BASE_AUDIO_VISUALIZER)) -#define GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_BASE_AUDIO_VISUALIZER,GstGLBaseAudioVisualizerClass)) +#define GST_TYPE_GL_BASE_AUDIO_VISUALIZER \ + (gst_gl_base_audio_visualizer_get_type()) +#define GST_GL_BASE_AUDIO_VISUALIZER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_GL_BASE_AUDIO_VISUALIZER, \ + GstGLBaseAudioVisualizer)) +#define GST_GL_BASE_AUDIO_VISUALIZER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_GL_BASE_AUDIO_VISUALIZER, \ + GstGLBaseAudioVisualizerClass)) +#define GST_IS_GL_BASE_AUDIO_VISUALIZER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_GL_BASE_AUDIO_VISUALIZER)) +#define GST_IS_GL_BASE_AUDIO_VISUALIZER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_GL_BASE_AUDIO_VISUALIZER)) +#define GST_GL_BASE_AUDIO_VISUALIZER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_GL_BASE_AUDIO_VISUALIZER, \ + GstGLBaseAudioVisualizerClass)) /** * GstGLBaseAudioVisualizer: @@ -63,16 +72,16 @@ GType gst_gl_base_audio_visualizer_get_type(void); * The parent instance type of a base GL Audio Visualizer. */ struct _GstGLBaseAudioVisualizer { - GstAudioVisualizer parent; + GstAudioVisualizer parent; - /*< public >*/ - GstGLDisplay *display; - GstGLContext *context; + /*< public >*/ + GstGLDisplay *display; + GstGLContext *context; - /*< private >*/ - gpointer _padding[GST_PADDING]; + /*< private >*/ + gpointer _padding[GST_PADDING]; - GstGLBaseAudioVisualizerPrivate *priv; + GstGLBaseAudioVisualizerPrivate *priv; }; /** @@ -81,22 +90,24 @@ struct _GstGLBaseAudioVisualizer { * @gl_start: called in the GL thread to setup the element GL state. * @gl_stop: called in the GL thread to clean up the element GL state. * @gl_render: called in the GL thread to fill the current video texture. - * @setup: called when the format changes (delegate from GstAudioVisualizer.setup) + * @setup: called when the format changes (delegate from + * GstAudioVisualizer.setup) * * The base class for OpenGL based audio visualizers. * */ struct _GstGLBaseAudioVisualizerClass { - GstAudioVisualizerClass parent_class; - - /*< public >*/ - GstGLAPI supported_gl_api; - gboolean (*gl_start) (GstGLBaseAudioVisualizer *glav); - void (*gl_stop) (GstGLBaseAudioVisualizer *glav); - gboolean (*gl_render) (GstGLBaseAudioVisualizer *glav, GstBuffer* audio, GstVideoFrame *video); - gboolean (*setup) (GstGLBaseAudioVisualizer *glav); - /*< private >*/ - gpointer _padding[GST_PADDING]; + GstAudioVisualizerClass parent_class; + + /*< public >*/ + GstGLAPI supported_gl_api; + gboolean (*gl_start)(GstGLBaseAudioVisualizer *glav); + void (*gl_stop)(GstGLBaseAudioVisualizer *glav); + gboolean (*gl_render)(GstGLBaseAudioVisualizer *glav, GstBuffer *audio, + GstVideoFrame *video); + gboolean (*setup)(GstGLBaseAudioVisualizer *glav); + /*< private >*/ + gpointer _padding[GST_PADDING]; }; G_END_DECLS diff --git a/src/projectm.c b/src/projectm.c index ef385f7..9e9ed09 100644 --- a/src/projectm.c +++ b/src/projectm.c @@ -16,6 +16,7 @@ GST_DEBUG_CATEGORY_STATIC(projectm_debug); projectm_handle projectm_init(GstProjectM *plugin) { projectm_handle handle = NULL; projectm_playlist_handle playlist = NULL; + GST_DEBUG_CATEGORY_INIT(projectm_debug, "projectm", 0, "ProjectM"); GstAudioVisualizer *bscope = GST_AUDIO_VISUALIZER(plugin); diff --git a/src/projectm.h b/src/projectm.h index e3311cc..1ba6a37 100644 --- a/src/projectm.h +++ b/src/projectm.h @@ -16,7 +16,8 @@ projectm_handle projectm_init(GstProjectM *plugin); /** * @brief Render ProjectM */ -// void projectm_render(GstProjectM *plugin, gint16 *samples, gint sample_count); +// void projectm_render(GstProjectM *plugin, gint16 *samples, gint +// sample_count); G_END_DECLS From 332dbc8c623cbf51cd5331899d82072e276ccbe2 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Fri, 4 Apr 2025 20:41:54 -0700 Subject: [PATCH 16/17] debug --- src/projectm.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/projectm.c b/src/projectm.c index 9e9ed09..1bac137 100644 --- a/src/projectm.c +++ b/src/projectm.c @@ -47,28 +47,29 @@ projectm_handle projectm_init(GstProjectM *plugin) { } // Log properties - GST_INFO_OBJECT(plugin, - "Using Properties: " - "preset=%s, " - "texture-dir=%s, " - "beat-sensitivity=%f, " - "hard-cut-duration=%f, " - "hard-cut-enabled=%d, " - "hard-cut-sensitivity=%f, " - "soft-cut-duration=%f, " - "preset-duration=%f, " - "mesh-size=(%lu, %lu)" - "aspect-correction=%d, " - "easter-egg=%f, " - "preset-locked=%d, " - "shuffle-presets=%d", - plugin->preset_path, plugin->texture_dir_path, - plugin->beat_sensitivity, plugin->hard_cut_duration, - plugin->hard_cut_enabled, plugin->hard_cut_sensitivity, - plugin->soft_cut_duration, plugin->preset_duration, - plugin->mesh_width, plugin->mesh_height, - plugin->aspect_correction, plugin->easter_egg, - plugin->preset_locked, plugin->shuffle_presets); + GST_INFO_OBJECT( + plugin, + "Using Properties: " + "preset=%s, " + "texture-dir=%s, " + "beat-sensitivity=%f, " + "hard-cut-duration=%f, " + "hard-cut-enabled=%d, " + "hard-cut-sensitivity=%f, " + "soft-cut-duration=%f, " + "preset-duration=%f, " + "mesh-size=(%lu, %lu)" + "aspect-correction=%d, " + "easter-egg=%f, " + "preset-locked=%d, " + "enable-playlist=%d, " + "shuffle-presets=%d", + plugin->preset_path, plugin->texture_dir_path, plugin->beat_sensitivity, + plugin->hard_cut_duration, plugin->hard_cut_enabled, + plugin->hard_cut_sensitivity, plugin->soft_cut_duration, + plugin->preset_duration, plugin->mesh_width, plugin->mesh_height, + plugin->aspect_correction, plugin->easter_egg, plugin->preset_locked, + plugin->enable_playlist, plugin->shuffle_presets); // Load preset file if path is provided if (plugin->preset_path != NULL) { From 72edd89cae6b04478f941e2372f531b60a3e2d87 Mon Sep 17 00:00:00 2001 From: Mischa Spiegelmock Date: Sat, 5 Apr 2025 09:13:27 -0700 Subject: [PATCH 17/17] readme --- README.md | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b63aafe..41a62b2 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@
+ ## Getting Started The documentation has been organized into distinct files, each dedicated to a specific platform. Within each file, you'll find detailed instructions covering the setup of prerequisites, the building process, installation steps, and guidance on utilizing the plugin on the respective platform. @@ -47,9 +48,35 @@ The documentation has been organized into distinct files, each dedicated to a sp - **[OSX](docs/OSX.md)** - **[Windows](docs/WINDOWS.md)** +Once the plugin has been installed, you can use it something like this: + +```shell +gst-launch pipewiresrc ! queue ! audioconvert ! projectm preset=/usr/local/share/projectM/presets preset-duration=5 ! video/x-raw,width=2048,height=1440,framerate=60/1 ! videoconvert ! xvimagesink sync=false +``` + +Or to convert an audio file to video: + +```shell +filesrc location=input.mp3 ! decodebin name=dec \ + decodebin ! tee name=t \ + t. ! queue ! audioconvert ! audioresample ! \ + capsfilter caps="audio/x-raw, format=F32LE, channels=2, rate=44100" ! avenc_aac bitrate=256000 ! queue ! mux. \ + t. ! queue ! audioconvert ! projectm preset=/usr/local/share/projectM/presets preset-duration=3 mesh-size=1024,576 ! \ + identity sync=false ! videoconvert ! videorate ! video/x-raw,framerate=60/1,width=3840,height=2160 ! \ + x264enc bitrate=35000 key-int-max=300 speed-preset=veryslow ! video/x-h264,stream-format=avc,alignment=au ! queue ! mux. \ + mp4mux name=mux ! filesink location=render.mp4; +``` + +Available options + +```shell +gst-inspect projectm +``` +

(back to top)

+ ## Contributing Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. @@ -66,6 +93,7 @@ Don't forget to give the project a star! Thanks again!

(back to top)

+ ## License Distributed under the LGPL-2.1 license. See `LICENSE` for more information. @@ -73,6 +101,7 @@ Distributed under the LGPL-2.1 license. See `LICENSE` for more information.

(back to top)

+ ## Support [![Discord][discord-shield]][discord-url] @@ -80,6 +109,7 @@ Distributed under the LGPL-2.1 license. See `LICENSE` for more information.

(back to top)

+ ## Contact Blaquewithaq (Discord: SoFloppy#1289) - [@anomievision](https://twitter.com/anomievision) - anomievision@gmail.com @@ -89,6 +119,7 @@ Blaquewithaq (Discord: SoFloppy#1289) - [@anomievision](https://twitter.com/anom + [contributors-shield]: https://img.shields.io/github/contributors/projectM-visualizer/gst-projectm.svg?style=for-the-badge [contributors-url]: https://github.com/projectM-visualizer/gst-projectm/graphs/contributors [forks-shield]: https://img.shields.io/github/forks/projectM-visualizer/gst-projectm.svg?style=for-the-badge @@ -104,4 +135,4 @@ Blaquewithaq (Discord: SoFloppy#1289) - [@anomievision](https://twitter.com/anom [crates-dl-shield]: https://img.shields.io/crates/d/gst-projectm?style=for-the-badge [crates-dl-url]: https://crates.io/crates/gst-projectm [discord-shield]: https://img.shields.io/discord/737206408482914387?style=for-the-badge -[discord-url]: https://discord.gg/7fQXN43n9W \ No newline at end of file +[discord-url]: https://discord.gg/7fQXN43n9W