Merge remote-tracking branch 'origin/beta'

This commit is contained in:
Bruno Herbelin
2025-10-30 23:36:46 +01:00
9 changed files with 230 additions and 76 deletions

3
.gitignore vendored
View File

@@ -32,3 +32,6 @@ flatpak/.flatpak-builder
flatpak/repo/
flatpak/build/
build/
.vscode/

View File

@@ -127,53 +127,75 @@ endif()
#
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
find_package(Threads REQUIRED)
find_package(ZLIB REQUIRED)
#
# GSTREAMER
#
find_package(GLIB2 REQUIRED)
macro_log_feature(GLIB2_FOUND "GLib" "GTK general-purpose utility library" "http://www.gtk.org" TRUE)
if (PKG_CONFIG_FOUND)
pkg_check_modules(GSTREAMER REQUIRED gstreamer-base-1.0>=1.24
gstreamer-pbutils-1.0
gstreamer-app-1.0
gstreamer-audio-1.0
gstreamer-video-1.0
gstreamer-gl-1.0
)
find_package(GObject REQUIRED)
macro_log_feature(GOBJECT_FOUND "GObject" "GTK object-oriented framework" "http://www.gtk.org" TRUE)
include_directories(
${GSTREAMER_INCLUDE_DIRS}
)
find_package(GStreamer 1.0.0 COMPONENTS base)
macro_log_feature(GSTREAMER_FOUND "GStreamer"
"Open Source Multiplatform Multimedia Framework"
"http://gstreamer.freedesktop.org/" TRUE "1.0.0")
link_directories(
${GSTREAMER_LIBRARY_DIRS}
)
find_package(GStreamerPluginsBase 1.0.0 COMPONENTS app audio video pbutils gl)
macro_log_feature(GSTREAMER_APP_LIBRARY_FOUND "GStreamerPluginsBase" "GStreamer app library"
"http://gstreamer.freedesktop.org/" TRUE "1.0.0")
else ()
find_package(GLIB2 REQUIRED)
macro_log_feature(GLIB2_FOUND "GLib" "GTK general-purpose utility library" "http://www.gtk.org" TRUE)
macro_log_feature(GSTREAMER_AUDIO_LIBRARY_FOUND "GStreamerPluginsBase" "GStreamer audio library"
"http://gstreamer.freedesktop.org/" TRUE "1.0.0")
find_package(GObject REQUIRED)
macro_log_feature(GOBJECT_FOUND "GObject" "GTK object-oriented framework" "http://www.gtk.org" TRUE)
macro_log_feature(GSTREAMER_VIDEO_LIBRARY_FOUND "GStreamerPluginsBase" "GStreamer video library"
"http://gstreamer.freedesktop.org/" TRUE "1.0.0")
find_package(GStreamer 1.0 COMPONENTS base REQUIRED)
macro_log_feature(GSTREAMER_PBUTILS_LIBRARY_FOUND "GStreamerPluginsBase" "GStreamer pbutils library"
"http://gstreamer.freedesktop.org/" TRUE "1.0.0")
find_package(GStreamerPluginsBase 1.0.0 COMPONENTS app audio video pbutils gl)
macro_log_feature(GSTREAMER_APP_LIBRARY_FOUND "GStreamerPluginsBase" "GStreamer app library"
"http://gstreamer.freedesktop.org/" TRUE "1.0.0")
macro_log_feature(GSTREAMER_AUDIO_LIBRARY_FOUND "GStreamerPluginsBase" "GStreamer audio library"
"http://gstreamer.freedesktop.org/" TRUE "1.0.0")
macro_log_feature(GSTREAMER_VIDEO_LIBRARY_FOUND "GStreamerPluginsBase" "GStreamer video library"
"http://gstreamer.freedesktop.org/" TRUE "1.0.0")
macro_log_feature(GSTREAMER_PBUTILS_LIBRARY_FOUND "GStreamerPluginsBase" "GStreamer pbutils library"
"http://gstreamer.freedesktop.org/" TRUE "1.0.0")
macro_log_feature(GSTREAMER_GL_LIBRARY_FOUND "GStreamerPluginsBase" "GStreamer opengl library"
"http://gstreamer.freedesktop.org/" TRUE "1.0.0")
include_directories(
${GLIB2_INCLUDE_DIR}
${GSTREAMER_INCLUDE_DIR}
${GSTREAMER_AUDIO_INCLUDE_DIR}
${GSTREAMER_VIDEO_INCLUDE_DIR}
${GSTREAMER_BASE_INCLUDE_DIR}
${GSTREAMER_APP_INCLUDE_DIR}
${GSTREAMER_PBUTILS_INCLUDE_DIR}
${GSTREAMER_GL_INCLUDE_DIR}
)
set( GSTREAMER_LIBRARIES
${GOBJECT_LIBRARIES}
${GSTREAMER_BASE_LIBRARY}
${GSTREAMER_APP_LIBRARY}
${GSTREAMER_AUDIO_LIBRARY}
${GSTREAMER_VIDEO_LIBRARY}
${GSTREAMER_PBUTILS_LIBRARY}
${GSTREAMER_GL_LIBRARY}
)
endif ()
macro_log_feature(GSTREAMER_GL_LIBRARY_FOUND "GStreamerPluginsBase" "GStreamer opengl library"
"http://gstreamer.freedesktop.org/" TRUE "1.0.0")
# Various preprocessor definitions for GST
add_definitions(-DGST_DISABLE_XML -DGST_DISABLE_LOADSAVE)
include_directories(
${GLIB2_INCLUDE_DIR}
${GSTREAMER_INCLUDE_DIR}
${GSTREAMER_AUDIO_INCLUDE_DIR}
${GSTREAMER_VIDEO_INCLUDE_DIR}
${GSTREAMER_BASE_INCLUDE_DIR}
${GSTREAMER_APP_INCLUDE_DIR}
${GSTREAMER_PBUTILS_INCLUDE_DIR}
${GSTREAMER_GL_INCLUDE_DIR}
)
#
# ICU4C
#

View File

@@ -1,13 +1,19 @@
#!/bin/bash
# test if there is an nvidia GPU
gpu=$(lspci | grep -i '.* vga .* nvidia .*')
shopt -s nocasematch
if [[ $gpu == *' nvidia '* ]]; then
# with nvidia, request render offload
printf 'Nvidia GPU present: %s\n' "$gpu"
__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia vimix "$@"
# Test if running under wayland
if [ -z "$WAYLAND_DISPLAY" ]; then
# not Wayland, nothing special
vimix "$@"
else
# otherwise, nothing special
printf 'GPU present: %s\n' "$gpu"
vimix "$@"
# Wayland: test if there is an nvidia GPU
gpu=$(lspci | grep -i '.* vga .* nvidia .*')
shopt -s nocasematch
if [[ $gpu == *' nvidia '* ]]; then
# with nvidia, request Wayland render offload
printf 'Nvidia GPU present: %s\n' "$gpu"
__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia vimix "$@"
else
# otherwise, nothing special
vimix "$@"
fi
fi

View File

@@ -171,14 +171,7 @@ target_link_libraries(${VMIX_BINARY} LINK_PRIVATE
${TINYXML2_LIBRARIES}
${ICU_LIBRARIES}
${CMAKE_DL_LIBS}
${GOBJECT_LIBRARIES}
${GSTREAMER_LIBRARY}
${GSTREAMER_BASE_LIBRARY}
${GSTREAMER_APP_LIBRARY}
${GSTREAMER_AUDIO_LIBRARY}
${GSTREAMER_VIDEO_LIBRARY}
${GSTREAMER_PBUTILS_LIBRARY}
${GSTREAMER_GL_LIBRARY}
${GSTREAMER_LIBRARIES}
Threads::Threads
ZLIB::ZLIB
Ableton::Link

View File

@@ -1,7 +1,7 @@
#ifndef GEOMETRYVIEW_H
#define GEOMETRYVIEW_H
// #define ENABLE_CANVAS
#define ENABLE_CANVAS
#include "View.h"

View File

@@ -217,7 +217,7 @@ string GstToolkit::gst_version()
#if GST_GL_HAVE_PLATFORM_GLX
// https://gstreamer.freedesktop.org/documentation/nvcodec/index.html?gi-language=c#plugin-nvcodec
// list ordered with higher priority at the end (e.g. nvidia proprietary before vaapi)
const char *plugins[17] = { "vdpaumpegdec", "omxh264dec", "omxmpeg2dec", "omxmpeg4videodec", "vaapidecodebin",
const char *plugins[17] = { "vdpaumpegdec", "omxh264dec", "omxmpeg2dec", "omxmpeg4videodec", "vaapidecodebin",
"nvh264sldec", "nvh264dec", "nvh265sldec", "nvh265dec", "nvmpegvideodec", "nvmpeg2videodec", "nvmpeg4videodec",
"nvvp8sldec", "nvvp8dec", "nvvp9sldec", "nvvp9dec", "nvav1dec"
};

View File

@@ -70,6 +70,116 @@
#include "RenderingManager.h"
// GDBus for screensaver inhibition (works on both X11 and Wayland)
#ifdef GLFW_EXPOSE_NATIVE_GLX
#include <gio/gio.h>
guint screensaver_inhibit_cookie_ = 0;
GDBusConnection *session_dbus_ = NULL;
void inhibitScreensaver (bool on)
{
/*
* Inhibit or un-inhibit the desktop screensaver via the
* org.freedesktop.ScreenSaver D-Bus API.
*
* When 'on' is true:
* - Obtain a connection to the session bus (cached in session_dbus_).
* - Call the Inhibit method with two strings: the application name
* ("vimix") and a human-readable reason.
* - The call returns a uint 'cookie' which must be kept and later
* passed to UnInhibit to release the inhibition.
*
* When 'on' is false:
* - If we previously inhibited the screensaver (screensaver_inhibit_cookie_ != 0)
* call UnInhibit with that cookie.
* - Release the cached D-Bus connection (session_dbus_) and reset the cookie.
*
* Notes:
* - This function uses synchronous D-Bus calls. They may block briefly.
* - Errors from g_dbus_connection_call_sync are reported to the log and freed.
* - The code is guarded by an #ifdef so it only compiles for platforms
* where GLFW_EXPOSE_NATIVE_GLX is defined (historically used for X11/GLX),
* but the org.freedesktop.ScreenSaver D-Bus interface works on both
* X11 and Wayland session daemons that implement the spec.
*/
if (on ) {
GError *error = NULL;
/* Lazily open a connection to the session bus and cache it. */
if (session_dbus_ == NULL)
session_dbus_ = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
/* Only inhibit once: do nothing if we already have a cookie. */
if (session_dbus_ != NULL && screensaver_inhibit_cookie_ == 0) {
/* Call org.freedesktop.ScreenSaver.Inhibit(application_name, reason) */
GVariant *result = g_dbus_connection_call_sync(
session_dbus_,
"org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
"Inhibit",
g_variant_new("(ss)", "vimix", "Video mixing in progress"),
G_VARIANT_TYPE("(u)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error
);
if (result != NULL) {
/* The returned variant contains a single unsigned integer cookie. */
g_variant_get(result, "(u)", &screensaver_inhibit_cookie_);
g_variant_unref(result);
Log::Info("Screensaver inhibited for vimix (cookie: %u)", screensaver_inhibit_cookie_);
} else {
/* If the call failed, log the error and ensure cookie is zero. */
if (error != NULL) {
Log::Info("Could not inhibit screensaver: %s", error->message);
g_error_free(error);
screensaver_inhibit_cookie_ = 0;
}
}
}
}
else {
/* Un-inhibit only if we have a valid cookie recorded. */
if (screensaver_inhibit_cookie_ != 0) {
GError *error = NULL;
if (session_dbus_ != NULL) {
/* Call org.freedesktop.ScreenSaver.UnInhibit(cookie) */
g_dbus_connection_call_sync(
session_dbus_,
"org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
"UnInhibit",
g_variant_new("(u)", screensaver_inhibit_cookie_),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error
);
if (error != NULL) {
/* Report failure to release inhibition but continue cleanup. */
g_printerr("Could not un-inhibit screensaver: %s\n", error->message);
g_error_free(error);
} else {
g_printerr("Screensaver inhibition disabled\n");
Log::Info("Screensaver inhibition disabled\n");
}
/* Close and drop our cached session bus connection. */
g_object_unref(session_dbus_);
session_dbus_ = NULL;
}
/* Reset cookie so subsequent calls can re-inhibit if needed. */
screensaver_inhibit_cookie_ = 0;
}
}
}
#endif
#ifdef USE_GST_OPENGL_SYNC_HANDLER
GLFW_EXPOSE_NATIVE_X11
@@ -149,13 +259,6 @@ static void glfw_error_callback(int error, const char* description)
static void WindowResizeCallback( GLFWwindow *w, int width, int height)
{
if (Rendering::manager().mainWindow().window() == w) {
// UI manager tries to keep windows in the workspace
WorkspaceWindow::notifyWorkspaceSizeChanged(Rendering::manager().mainWindow().previous_size.x, Rendering::manager().mainWindow().previous_size.y, width, height);
Rendering::manager().mainWindow().previous_size = glm::vec2(width, height);
Rendering::manager().draw();
}
int id = Rendering::manager().window(w)->index();
Settings::application.windows[id].fullscreen = glfwGetWindowMonitor(w) != nullptr;
if (!Settings::application.windows[id].fullscreen) {
@@ -163,6 +266,12 @@ static void WindowResizeCallback( GLFWwindow *w, int width, int height)
Settings::application.windows[id].h = height;
}
if (Rendering::manager().mainWindow().window() == w) {
// UI manager tries to keep windows in the workspace
WorkspaceWindow::notifyWorkspaceSizeChanged(Rendering::manager().mainWindow().previous_size.x, Rendering::manager().mainWindow().previous_size.y, width, height);
Rendering::manager().mainWindow().previous_size = glm::vec2(width, height);
Rendering::manager().draw();
}
}
static void WindowMoveCallback( GLFWwindow *w, int x, int y)
@@ -328,6 +437,17 @@ Rendering::Rendering()
bool Rendering::init()
{
// Forcing X11 on Gnome makes the server use xWayland which has proper Server Side Decorations as opposed to Wayland.
if (strcmp(getenv("XDG_CURRENT_DESKTOP"), "GNOME") == 0 ||
strcmp(getenv("XDG_CURRENT_DESKTOP"), "Unity") == 0 ){
g_printerr("Forcing X11 on GNOME desktop\n");
glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11);
}
else {
g_printerr("Detected %s desktop\n", getenv("XDG_CURRENT_DESKTOP"));
glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM);
}
//
// Setup GLFW
//
@@ -510,6 +630,20 @@ void Rendering::draw()
main_new_title_.clear();
}
// draw output windows and count number of success
int count = 0;
for (auto it = outputs_.begin(); it != outputs_.end(); ++it) {
if ( it->draw( Mixer::manager().session()->frame() ) )
++count;
}
// terminate or initialize output windows to match number of output windows
if (count > Settings::application.num_output_windows)
outputs_[count-1].terminate();
else if (count < Settings::application.num_output_windows) {
outputs_[count].init( count+1, main_.window());
outputs_[count].show();
}
// operate on main window context
main_.makeCurrent();
@@ -528,21 +662,6 @@ void Rendering::draw()
glfwSwapBuffers(main_.window());
// draw output windows and count number of success
int count = 0;
for (auto it = outputs_.begin(); it != outputs_.end(); ++it) {
if ( it->draw( Mixer::manager().session()->frame() ) )
++count;
}
// terminate or initialize output windows to match number of output windows
if (count > Settings::application.num_output_windows)
outputs_[count-1].terminate();
else if (count < Settings::application.num_output_windows) {
outputs_[count].init( count+1, main_.window());
outputs_[count].show();
}
// software framerate limiter < 62 FPS
{
static GTimer *timer = g_timer_new ();
@@ -1143,6 +1262,9 @@ void RenderingWindow::terminate()
fbo_ = 0;
index_ = -1;
textureid_ = Resource::getTextureBlack();
// SCREENSAVER UNINHIBIT
inhibitScreensaver( Settings::application.num_output_windows > 0 );
}
void RenderingWindow::show()
@@ -1156,6 +1278,9 @@ void RenderingWindow::show()
GLFWmonitor *mo = Rendering::manager().monitorNamed(Settings::application.windows[index_].monitor);
setFullscreen_(mo);
}
// SCREENSAVER INHIBIT
inhibitScreensaver( Settings::application.num_output_windows > 0 );
}

View File

@@ -1156,8 +1156,13 @@ void SessionLoader::visit (MediaSource& s)
// set config media player
s.mediaplayer()->accept(*this);
// add a callback to activate play speed
s.call( new PlaySpeed( s.mediaplayer()->playSpeed() ) );
// read source play speed from media player SessionLoader::visit
double r = s.mediaplayer()->playSpeed();
// reset media player to normal speed
s.mediaplayer()->setRate(1.0);
// add a callback to activate source play speed and rewind
s.call( new PlaySpeed( r ) );
s.call( new RePlay( ) );
}
void SessionLoader::visit (SessionFileSource& s)

View File

@@ -541,7 +541,7 @@ void RePlay::update(Source *s, float dt)
SourceCallback::update(s, dt);
// apply when ready
if ( status_ == READY ){
if ( status_ == READY && s->ready()){
// call replay function
s->replay();