Compare commits

..

60 Commits
0.5.3 ... 0.5.4

Author SHA1 Message Date
brunoherbelin
1d45ab1d20 Cleanup DummySource (bad bad bad) 2021-04-05 14:55:21 +02:00
brunoherbelin
ae1c3d85ab bugfix: store mask after applying effect 2021-04-05 13:31:31 +02:00
brunoherbelin
c22df2eb2a (continue) Migrating clipboard manipulation to Session XML management 2021-04-05 13:06:31 +02:00
brunoherbelin
d3a130d9ba (continue) Migrating clipboard manipulation to Session XML management 2021-04-05 13:05:38 +02:00
brunoherbelin
8a57b52fcc Migrating clipboard manipulation to Session XML management 2021-04-05 13:04:44 +02:00
brunoherbelin
dbc9803f9e center on source only if source is not visible 2021-04-04 22:21:42 +02:00
brunoherbelin
3e376eb166 ensure output and media player window are visible 2021-04-04 20:55:55 +02:00
brunoherbelin
12aca05aef prevent potential memoryleak 2021-04-04 14:38:04 +02:00
brunoherbelin
ce1de27618 Improved README 2021-04-04 14:19:36 +02:00
brunoherbelin
66d5148e3a Prevent potential memory leak 2021-04-04 13:40:17 +02:00
brunoherbelin
d2b4a825eb avoid pedantic compilation warning 2021-04-04 13:27:56 +02:00
brunoherbelin
f443720319 Programming style improvement: following Cppcheck suggestions. 2021-04-04 13:13:06 +02:00
brunoherbelin
b4627a1613 Various potential memory leak fixed 2021-04-04 01:25:35 +02:00
brunoherbelin
ceea9c10d5 gstreamer memory cleanup in mediaplayer and stream 2021-04-04 01:24:13 +02:00
brunoherbelin
a143360497 Memory leak fix 2021-04-02 22:38:34 +02:00
brunoherbelin
163757cb69 Improved layout and menu Media Player UI 2021-04-02 13:57:11 +02:00
brunoherbelin
4537b986ca Support to forced software decoding option
With reload of media player when option is changed
2021-04-02 12:14:20 +02:00
Bruno
aafac2a7a8 merge 2021-04-01 20:46:46 +02:00
brunoherbelin
7344258b2f fixed hardware decoding detection OSX 2021-04-01 09:12:41 +02:00
brunoherbelin
c59994b7e5 Implemented a detection of hardware decoding used in pipeline
Simple check for names of decoder inside uridecodebin and cross check
with the list of known hardware Decoder Names
2021-04-01 00:14:02 +02:00
brunoherbelin
b089f59e2a Implemented a detection of hardware decoding used in pipeline
Simple check for names of decoder inside uridecodebin and cross check
with the list of known hardware Decoder Names
2021-04-01 00:11:05 +02:00
Bruno
649d2b7ef7 Merge remote-tracking branch 'origin/master' 2021-03-31 19:24:48 +02:00
brunoherbelin
8bef575e8b minor changes to media player pipeline (improved performance?) 2021-03-31 15:40:02 +02:00
brunoherbelin
559a036e6d BugFix: reload list of recent sessions after any change to history 2021-03-30 23:51:06 +02:00
brunoherbelin
0b845591f9 Improved transition view
Responsive buttons placement and clarified actions.
2021-03-30 23:02:24 +02:00
brunoherbelin
a8ef68ed59 Thought of the day. 2021-03-30 19:14:34 +02:00
brunoherbelin
7293b8b9dd BugFix: clean interrupt stream when ending abruptly 2021-03-30 19:03:33 +02:00
brunoherbelin
8eef5465c9 eyecandy: better icons for file menu 2021-03-29 23:18:14 +02:00
brunoherbelin
c08cb95dc1 Bugfix: restating correct order session loading properties 2021-03-29 23:17:54 +02:00
brunoherbelin
010166e7b0 renaming mediaplayer attribute force software decoding 2021-03-29 23:17:16 +02:00
brunoherbelin
4c7fb94616 Small update of webpage (link to installation guide) 2021-03-29 22:23:00 +02:00
Bruno
46f486a367 Documenting installation for wiki 2021-03-29 22:05:41 +02:00
brunoherbelin
ea195dcf11 add link to vimeo 2021-03-28 21:03:43 +02:00
brunoherbelin
e6979acded Revert "Try to integrate embedded video in Jekyll webpage"
This reverts commit 43b4fc81b9.
2021-03-28 20:59:46 +02:00
brunoherbelin
43b4fc81b9 Try to integrate embedded video in Jekyll webpage 2021-03-28 20:56:30 +02:00
brunoherbelin
d4ce6ebee6 trying snap eextensions: [gnome-3-28] 2021-03-28 18:37:29 +02:00
brunoherbelin
1c9a5ece83 setLocale in C (not std C++) 2021-03-27 23:36:51 +01:00
brunoherbelin
8a75664264 Preventing display glitch from invalid scaling of view 2021-03-27 23:31:18 +01:00
brunoherbelin
6687bdd258 BugFix: mixed-up Locale for XML I/O caused by GTK Dialogs 2021-03-27 23:17:19 +01:00
brunoherbelin
e525ecad36 Cleanup main 2021-03-27 23:15:49 +01:00
brunoherbelin
e8b5dc0649 BugFix: not using GST g_main_context to avoid GTK conflict 2021-03-27 19:21:18 +01:00
brunoherbelin
3a0f96c1ec fixed add extension on saved filename 2021-03-27 18:23:54 +01:00
brunoherbelin
e4c06ba1bb Fixed #ifdef compilation 2021-03-27 18:21:13 +01:00
brunoherbelin
bc4eadfd08 Bugfix view config loading 2021-03-27 18:13:09 +01:00
Bruno
ee2ce3802f Linux Dialogs in GTK for SNAP compatibility
Discarding use of ZENITY under linux (previously used with the tinyfiledialog) because snapcraft makes  it impossible to use :(. Reimplementation of GTK+ dialogs directly inside vimix code. Note: no changes for OSX. Complete cleanup of cmake file.
2021-03-27 13:03:22 +01:00
brunoherbelin
43d44634f7 Trying to fix the tinyfiledialog zenity integration 2021-03-22 16:25:51 +01:00
brunoherbelin
41bd7fc958 problem snap and zenity 2021-03-22 14:34:47 +01:00
brunoherbelin
1e458a642c Merge branch 'master' of github.com:brunoherbelin/vimix 2021-03-22 13:59:05 +01:00
brunoherbelin
b255e41091 Work in progress: force sofware decoder for a media player 2021-03-22 13:58:55 +01:00
brunoherbelin
bc22832ad6 information on tinyfiledialog in about. 2021-03-22 13:35:17 +01:00
brunoherbelin
f59ac505b7 shift grab source (even on rotation) 2021-03-21 14:23:30 +01:00
brunoherbelin
e15b962221 Cosmetics: improve reordering source in left panel 2021-03-21 14:09:11 +01:00
brunoherbelin
28d4d4acc4 Bugfix: prevent crash with current source when reordering 2021-03-21 13:24:54 +01:00
brunoherbelin
5f800f3723 Creating texture only on draw 2021-03-20 23:29:30 +01:00
brunoherbelin
2537ca03c8 fix 2021-03-20 22:05:30 +01:00
brunoherbelin
1860402452 Bugfix un-understandable crash on texture mixing quadratic. 2021-03-20 22:03:57 +01:00
brunoherbelin
bec9385c68 BugFix (apparently problematic memmove under OSX) 2021-03-20 18:38:46 +01:00
brunoherbelin
798139e097 Cosmetic: add label to button in source imgui visitor 2021-03-20 14:57:13 +01:00
brunoherbelin
6683d76222 OSX package name with patch version 2021-03-20 13:49:56 +01:00
brunoherbelin
6d2d16b644 updated OSX icon 2021-03-20 13:31:09 +01:00
88 changed files with 1940 additions and 939 deletions

View File

@@ -11,9 +11,8 @@
#include "BoundingBoxVisitor.h"
BoundingBoxVisitor::BoundingBoxVisitor(bool force): force_(force)
BoundingBoxVisitor::BoundingBoxVisitor(bool force): force_(force), modelview_(glm::identity<glm::mat4>())
{
modelview_ = glm::identity<glm::mat4>();
}
@@ -39,7 +38,7 @@ void BoundingBoxVisitor::visit(Group &n)
if (!n.visible_)
return;
glm::mat4 mv = modelview_;
for (NodeSet::iterator node = n.begin(); node != n.end(); node++) {
for (NodeSet::iterator node = n.begin(); node != n.end(); ++node) {
if ( (*node)->visible_ || force_)
(*node)->accept(*this);
modelview_ = mv;

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.8.0)
cmake_minimum_required(VERSION 3.8.2)
project(vimix VERSION 0.0.1 LANGUAGES CXX C)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@@ -46,6 +46,9 @@ if(UNIX)
set(OpenGL_GL_PREFERENCE "GLVND")
find_package(GTK 3.0 REQUIRED)
macro_log_feature(GTK_FOUND "GTK" "GTK cross-platform widget toolkit" "http://www.gtk.org" TRUE)
endif()
add_definitions(-DUNIX)
elseif(WIN32)
@@ -54,9 +57,21 @@ elseif(WIN32)
endif()
# Include the CMake RC module
include(CMakeRC)
# Basics
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
find_package(Threads REQUIRED)
find_package(GLIB2)
macro_log_feature(GLIB2_FOUND "GLib" "GTK general-purpose utility library" "http://www.gtk.org" TRUE)
find_package(GObject)
macro_log_feature(GOBJECT_FOUND "GObject" "GTK object-oriented framework" "http://www.gtk.org" TRUE)
find_package(PNG REQUIRED)
macro_log_feature(PNG_FOUND "PNG" "Portable Network Graphics" "http://www.libpng.org" TRUE)
find_package(ICU REQUIRED COMPONENTS i18n io uc)
macro_log_feature(ICU_FOUND "ICU" "International Components for Unicode" "http://site.icu-project.org" TRUE)
#
# GSTREAMER
@@ -91,7 +106,6 @@ macro_log_feature(GSTREAMER_GL_LIBRARY_FOUND "GStreamer opengl library"
"${GSTREAMER_GL_LIBRARY}"
"http://gstreamer.freedesktop.org/" TRUE "1.0.0")
#find_package(GStreamerPluginsBad 1.0.0 COMPONENTS player)
#macro_log_feature(GSTREAMER_PLAYER_LIBRARY_FOUND "GStreamer player library"
#"${GSTREAMER_PLAYER_LIBRARY}"
@@ -101,22 +115,6 @@ macro_log_feature(GSTREAMER_GL_LIBRARY_FOUND "GStreamer opengl library"
# Various preprocessor definitions for GST
add_definitions(-DGST_DISABLE_XML -DGST_DISABLE_LOADSAVE)
# Basics
find_package(GLIB2)
macro_log_feature(GLIB2_FOUND "GLib" "GTK general-purpose utility library" "http://www.gtk.org/" TRUE)
find_package(GObject)
macro_log_feature(GOBJECT_FOUND "GObject" "GTK object-oriented framework" "http://www.gtk.org/" TRUE)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
find_package(Threads REQUIRED)
set(THREAD_LIBRARY Threads::Threads)
find_package(PNG REQUIRED)
set(PNG_LIBRARY PNG::PNG)
find_package(ICU REQUIRED COMPONENTS i18n io uc)
macro_log_feature(ICU_FOUND "ICU" "International Components for Unicode" "http://site.icu-project.org" TRUE)
#
@@ -124,10 +122,10 @@ macro_log_feature(ICU_FOUND "ICU" "International Components for Unicode" "http:/
# NB: set glfw3_PATH to /usr/local/Cellar/glfw/3.3.2/lib/cmake/glfw3
#
find_package(glfw3 3.2 REQUIRED)
macro_log_feature(glfw3_FOUND "GLFW3" "Open Source multi-platform library for OpenGL" "http://www.glfw.org/" TRUE)
macro_log_feature(glfw3_FOUND "GLFW3" "Open Source multi-platform library for OpenGL" "http://www.glfw.org" TRUE)
set(GLFW_LIBRARY glfw)
#find_package(OpenGL REQUIRED)
macro_display_feature_log()
# static sub packages in ext
set(BUILD_STATIC_LIBS ON)
@@ -136,14 +134,14 @@ set(BUILD_STATIC_LIBS ON)
# GLM
#
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ext/glm)
message(STATUS "Compiling 'GLM' OpenGL mathematics https://glm.g-truc.net.")
message(STATUS "Compiling 'GLM' OpenGL mathematics https://glm.g-truc.net")
#
# GLAD
#
set(GLAD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/glad/include)
add_library(GLAD "${CMAKE_CURRENT_SOURCE_DIR}/ext/glad/src/glad.c")
message(STATUS "Compiling 'GLAD' Open source multi-language OpenGL loader https://glad.dav1d.de/ -- ${GLAD_INCLUDE_DIR}.")
message(STATUS "Compiling 'GLAD' Open source multi-language OpenGL loader https://glad.dav1d.de -- ${GLAD_INCLUDE_DIR}.")
#
# DEAR IMGUI
@@ -198,7 +196,6 @@ set(OSCPACK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/OSCPack)
add_library(OSCPACK "${OSCPACK_SRCS}")
message(STATUS "Compiling 'OSCPack' from http://www.rossbencina.com/code/oscpack -- ${OSCPACK_INCLUDE_DIR}.")
#
# STB
#
@@ -215,22 +212,29 @@ if(WIN32)
endif( WIN32 )
#
# TINY FILE DIALOG
# FILE DIALOG: use tinyfiledialog for all except Linux
#
set(TINYFD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd)
add_library(TINYFD "${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd/tinyfiledialogs.c")
message(STATUS "Compiling 'TinyFileDialog' from https://github.com/native-toolkit/tinyfiledialogs.git -- ${TINYFD_INCLUDE_DIR}.")
if(UNIX)
if (APPLE)
set(TINYFD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd)
add_library(TINYFD "${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd/tinyfiledialogs.c")
message(STATUS "Compiling 'TinyFileDialog' from https://github.com/native-toolkit/tinyfiledialogs.git -- ${TINYFD_INCLUDE_DIR}.")
set(TINYFD_LIBRARY TINYFD)
endif()
else()
set(TINYFD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd)
add_library(TINYFD "${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd/tinyfiledialogs.c")
message(STATUS "Compiling 'TinyFileDialog' from https://github.com/native-toolkit/tinyfiledialogs.git -- ${TINYFD_INCLUDE_DIR}.")
set(TINYFD_LIBRARY TINYFD)
endif()
#
# OBJ LOADER
# OBJ LOADER - not used
#
#set(OBJLOADER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/obj)
#add_library(OBJLOADER "${CMAKE_CURRENT_SOURCE_DIR}/ext/obj/ObjLoader.cpp")
#message(STATUS "Compiling 'ObjLoader' from https://github.com/mortennobel/OpenGL_3_2_Utils -- ${OBJLOADER_INCLUDE_DIR}.")
# find_package(PkgConfig REQUIRED)
# pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
#
# Application
#
@@ -316,6 +320,7 @@ set(VMIX_SRCS
GstToolkit.cpp
GlmToolkit.cpp
SystemToolkit.cpp
DialogToolkit.cpp
tinyxml2Toolkit.cpp
NetworkToolkit.cpp
Connection.cpp
@@ -426,6 +431,11 @@ set(VMIX_RSC_FILES
./rsc/mesh/h_mark.ply
)
# Include the CMake RC module
include(CMakeRC)
cmrc_add_resource_library(vmix-resources ALIAS vmix::rc NAMESPACE vmix WHENCE rsc ${VMIX_RSC_FILES})
message(STATUS "Using 'CMakeRC ' from https://github.com/vector-of-bool/cmrc.git -- ${CMAKE_MODULE_PATH}.")
### DEFINE THE TARGET (OS specific)
IF(APPLE)
@@ -455,17 +465,19 @@ IF(APPLE)
ELSE(APPLE)
link_directories (${GTK3_LIBRARY_DIRS})
add_executable(${VMIX_BINARY}
${VMIX_SRCS}
${IMGUITEXTEDIT_SRC}
)
set(PLATFORM_LIBS ""
set(PLATFORM_LIBS
GTK::GTK
)
ENDIF(APPLE)
### COMPILE THE TARGET (all OS)
set_property(TARGET ${VMIX_BINARY} PROPERTY CXX_STANDARD 17)
@@ -473,14 +485,12 @@ set_property(TARGET ${VMIX_BINARY} PROPERTY C_STANDARD 11)
target_compile_definitions(${VMIX_BINARY} PUBLIC "IMGUI_IMPL_OPENGL_LOADER_GLAD")
cmrc_add_resource_library(vmix-resources ALIAS vmix::rc NAMESPACE vmix WHENCE rsc ${VMIX_RSC_FILES})
message(STATUS "Using 'CMakeRC ' from https://github.com/vector-of-bool/cmrc.git -- ${CMAKE_MODULE_PATH}.")
target_link_libraries(${VMIX_BINARY} LINK_PRIVATE
${GLFW_LIBRARY}
GLAD
glm::glm
TINYXML2
IMGUI
OSCPACK
${GLFW_LIBRARY}
${CMAKE_DL_LIBS}
${GOBJECT_LIBRARIES}
${GSTREAMER_LIBRARY}
@@ -491,27 +501,23 @@ target_link_libraries(${VMIX_BINARY} LINK_PRIVATE
${GSTREAMER_PBUTILS_LIBRARY}
${GSTREAMER_GL_LIBRARY}
${GSTREAMER_PLAYER_LIBRARY}
${NFD_LIBRARY}
${PNG_LIBRARY}
${THREAD_LIBRARY}
TINYXML2
TINYFD
IMGUI
OSCPACK
vmix::rc
${TINYFD_LIBRARY}
Threads::Threads
PNG::PNG
glm::glm
ICU::i18n
ICU::io
ICU::uc
vmix::rc
${PLATFORM_LIBS}
)
macro_display_feature_log()
### DEFINE THE PACKAGING (all OS)
SET(CPACK_PACKAGE_NAME "vimix")
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "vimix\n Real-time video mixing for live performance.")
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "vimix\nReal-time video mixing for live performance.")
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.md")
SET(CPACK_PACKAGE_CONTACT "bruno.herbelin@gmail.com")
SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING.txt")
@@ -605,7 +611,7 @@ ELSE(APPLE)
ENDIF(APPLE)
# Package full name
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}_${CPACK_SYSTEM_NAME}")
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}_${CPACK_SYSTEM_NAME}")
# To Create a package, run "cpack"
include(CPack)

View File

@@ -114,10 +114,10 @@ ConnectionInfo Connection::info(int index)
struct hasName: public std::unary_function<ConnectionInfo, bool>
{
inline bool operator()(const ConnectionInfo elem) const {
inline bool operator()(const ConnectionInfo &elem) const {
return (elem.name.compare(_a) == 0);
}
hasName(std::string a) : _a(a) { }
explicit hasName(const std::string &a) : _a(a) { }
private:
std::string _a;
};
@@ -185,7 +185,7 @@ void Connection::ask()
// check the list of connections for non responding (disconnected)
std::vector< ConnectionInfo >::iterator it = Connection::manager().connections_.begin();
for(it++; it!=Connection::manager().connections_.end(); ) {
for(++it; it!=Connection::manager().connections_.end(); ) {
// decrease life score
(*it).alive--;
// erase connection if its life score is negative (not responding too many times)
@@ -201,7 +201,7 @@ void Connection::ask()
}
// loop
else
it++;
++it;
}
}

View File

@@ -240,7 +240,6 @@ void Handles::update( float dt )
void Handles::draw(glm::mat4 modelview, glm::mat4 projection)
{
static Mesh *handle_active = new Mesh("mesh/border_handles_overlay_filled.ply");
if ( !initialized() ) {
if(handle_ && !handle_->initialized())
@@ -251,6 +250,7 @@ void Handles::draw(glm::mat4 modelview, glm::mat4 projection)
}
if ( visible_ ) {
static Mesh *handle_active = new Mesh("mesh/border_handles_overlay_filled.ply");
// set color
handle_->shader()->color = color;

View File

@@ -167,9 +167,9 @@ void Device::remove(const char *device)
break;
}
nameit++;
descit++;
coit++;
++nameit;
++descit;
++coit;
}
}
@@ -257,7 +257,7 @@ struct hasDevice: public std::unary_function<DeviceSource*, bool>
inline bool operator()(const DeviceSource* elem) const {
return (elem && elem->device() == _d);
}
hasDevice(std::string d) : _d(d) { }
explicit hasDevice(const std::string &d) : _d(d) { }
private:
std::string _d;
};
@@ -360,7 +360,7 @@ void DeviceSource::setDevice(const std::string &devicename)
DeviceConfigSet confs = Device::manager().config(index);
#ifdef DEVICE_DEBUG
Log::Info("Device %s supported configs:", devicename.c_str());
for( DeviceConfigSet::iterator it = confs.begin(); it != confs.end(); it++ ){
for( DeviceConfigSet::iterator it = confs.begin(); it != confs.end(); ++it ){
float fps = static_cast<float>((*it).fps_numerator) / static_cast<float>((*it).fps_denominator);
Log::Info(" - %s %s %d x %d %.1f fps", (*it).stream.c_str(), (*it).format.c_str(), (*it).width, (*it).height, fps);
}
@@ -431,11 +431,11 @@ DeviceConfigSet Device::getDeviceConfigs(const std::string &src_description)
// get the first pad and its content
GstIterator *iter = gst_element_iterate_src_pads(elem);
GValue vPad = G_VALUE_INIT;
GstPad* ret = NULL;
GstPad* pad_ret = NULL;
if (gst_iterator_next(iter, &vPad) == GST_ITERATOR_OK)
{
ret = GST_PAD(g_value_get_object(&vPad));
GstCaps *device_caps = gst_pad_query_caps (ret, NULL);
pad_ret = GST_PAD(g_value_get_object(&vPad));
GstCaps *device_caps = gst_pad_query_caps (pad_ret, NULL);
// loop over all caps offered by the pad
int C = gst_caps_get_size(device_caps);
@@ -508,8 +508,8 @@ DeviceConfigSet Device::getDeviceConfigs(const std::string &src_description)
gdouble fps_max = 1.0;
// loop over all fractions
int N = gst_value_list_get_size(val);
for (int n = 0; n < N; n++ ){
const GValue *frac = gst_value_list_get_value(val, n);
for (int i = 0; i < N; ++i ){
const GValue *frac = gst_value_list_get_value(val, i);
// read one fraction in the list
if ( GST_VALUE_HOLDS_FRACTION(frac)) {
int n = gst_value_get_fraction_numerator(frac);

323
DialogToolkit.cpp Normal file
View File

@@ -0,0 +1,323 @@
// multiplatform implementation of system dialogs
//
// 'TinyFileDialog' from https://github.com/native-toolkit/tinyfiledialogs.git
// is the prefered solution, but it is not compatible with linux snap packaging
// because of 'zenity' access rights nightmare :(
// Thus this re-implementation of native GTK+ dialogs for linux
#if defined(LINUX)
#define USE_TINYFILEDIALOG 0
#else
#define USE_TINYFILEDIALOG 1
#endif
#include "defines.h"
#include "SystemToolkit.h"
#include "DialogToolkit.h"
#if USE_TINYFILEDIALOG
#include "tinyfiledialogs.h"
#else
#include <stdio.h>
#include <gtk/gtk.h>
static bool gtk_init_ok = false;
static int window_x = -1, window_y = -1;
void add_filter_any_file_dialog( GtkWidget *dialog)
{
/* append a wildcard option */
GtkFileFilter *filter;
filter = gtk_file_filter_new();
gtk_file_filter_set_name( filter, "Any file" );
gtk_file_filter_add_pattern( filter, "*" );
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter );
}
void add_filter_file_dialog( GtkWidget *dialog, int const countfilterPatterns, char const * const * const filterPatterns, char const * const filterDescription)
{
GtkFileFilter *filter;
filter = gtk_file_filter_new();
gtk_file_filter_set_name( filter, filterDescription );
for (int i = 0; i < countfilterPatterns; i++ )
gtk_file_filter_add_pattern( filter, filterPatterns[i] );
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter );
}
void wait_for_event(void)
{
while ( gtk_events_pending() )
gtk_main_iteration();
}
bool gtk_init()
{
if (!gtk_init_ok) {
if ( gtk_init_check( NULL, NULL ) )
gtk_init_ok = true;
}
return gtk_init_ok;
}
#endif
std::string DialogToolkit::saveSessionFileDialog(const std::string &path)
{
std::string filename = "";
char const * save_pattern[1] = { "*.mix" };
#if USE_TINYFILEDIALOG
char const * save_file_name;
save_file_name = tinyfd_saveFileDialog( "Save a session file", path.c_str(), 1, save_pattern, "vimix session");
if (save_file_name)
filename = std::string(save_file_name);
#else
if (!gtk_init()) {
ErrorDialog("Could not initialize GTK+ for dialog");
return filename;
}
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Save Session File", NULL,
GTK_FILE_CHOOSER_ACTION_SAVE,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Save", GTK_RESPONSE_ACCEPT, NULL );
gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE );
// set file filters
add_filter_file_dialog(dialog, 1, save_pattern, "vimix session");
add_filter_any_file_dialog(dialog);
// Set the default path
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), path.c_str() );
// ensure front and centered
gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE );
if (window_x > 0 && window_y > 0)
gtk_window_move( GTK_WINDOW(dialog), window_x, window_y);
// display and get filename
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
char *save_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
if (save_file_name)
filename = std::string(save_file_name);
g_free( save_file_name );
}
// remember position
gtk_window_get_position( GTK_WINDOW(dialog), &window_x, &window_y);
// done
gtk_widget_destroy(dialog);
wait_for_event();
#endif
std::string extension = filename.substr(filename.find_last_of(".") + 1);
if (extension != "mix")
filename += ".mix";
return filename;
}
std::string DialogToolkit::openSessionFileDialog(const std::string &path)
{
std::string filename = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
char const * open_pattern[1] = { "*.mix" };
#if USE_TINYFILEDIALOG
char const * open_file_name;
open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 1, open_pattern, "vimix session", 0);
if (open_file_name)
filename = std::string(open_file_name);
#else
if (!gtk_init()) {
ErrorDialog("Could not initialize GTK+ for dialog");
return filename;
}
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Open Session File", NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL );
// set file filters
add_filter_file_dialog(dialog, 1, open_pattern, "vimix session");
add_filter_any_file_dialog(dialog);
// Set the default path
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), startpath.c_str() );
// ensure front and centered
gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE );
if (window_x > 0 && window_y > 0)
gtk_window_move( GTK_WINDOW(dialog), window_x, window_y);
// display and get filename
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
char *open_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
if (open_file_name)
filename = std::string(open_file_name);
g_free( open_file_name );
}
// remember position
gtk_window_get_position( GTK_WINDOW(dialog), &window_x, &window_y);
// done
gtk_widget_destroy(dialog);
wait_for_event();
#endif
return filename;
}
std::string DialogToolkit::ImportFileDialog(const std::string &path)
{
std::string filename = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
char const * open_pattern[18] = { "*.mix", "*.mp4", "*.mpg",
"*.avi", "*.mov", "*.mkv",
"*.webm", "*.mod", "*.wmv",
"*.mxf", "*.ogg", "*.flv",
"*.asf", "*.jpg", "*.png",
"*.gif", "*.tif", "*.svg" };
#if USE_TINYFILEDIALOG
char const * open_file_name;
open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 18, open_pattern, "All supported formats", 0);
if (open_file_name)
filename = std::string(open_file_name);
#else
if (!gtk_init()) {
ErrorDialog("Could not initialize GTK+ for dialog");
return filename;
}
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Import Media File", NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL );
// set file filters
add_filter_file_dialog(dialog, 18, open_pattern, "All supported formats");
add_filter_any_file_dialog(dialog);
// Set the default path
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), startpath.c_str() );
// ensure front and centered
gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE );
if (window_x > 0 && window_y > 0)
gtk_window_move( GTK_WINDOW(dialog), window_x, window_y);
// display and get filename
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
char *open_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
if (open_file_name)
filename = std::string(open_file_name);
g_free( open_file_name );
}
// remember position
gtk_window_get_position( GTK_WINDOW(dialog), &window_x, &window_y);
// done
gtk_widget_destroy(dialog);
wait_for_event();
#endif
return filename;
}
std::string DialogToolkit::FolderDialog(const std::string &path)
{
std::string foldername = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
#if USE_TINYFILEDIALOG
char const * open_folder_name;
open_folder_name = tinyfd_selectFolderDialog("Select folder", startpath.c_str());
if (open_folder_name)
foldername = std::string(open_folder_name);
#else
if (!gtk_init()) {
ErrorDialog("Could not initialize GTK+ for dialog");
return foldername;
}
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Select folder", NULL,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Select", GTK_RESPONSE_ACCEPT, NULL );
gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE );
// Set the default path
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), startpath.c_str() );
// ensure front and centered
gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE );
if (window_x > 0 && window_y > 0)
gtk_window_move( GTK_WINDOW(dialog), window_x, window_y);
// display and get filename
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
char *open_folder_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
if (open_folder_name)
foldername = std::string(open_folder_name);
g_free( open_folder_name );
}
// remember position
gtk_window_get_position( GTK_WINDOW(dialog), &window_x, &window_y);
// done
gtk_widget_destroy(dialog);
wait_for_event();
#endif
return foldername;
}
void DialogToolkit::ErrorDialog(const char* message)
{
#if USE_TINYFILEDIALOG
tinyfd_messageBox( APP_TITLE, message, "ok", "error", 0);
#else
if (!gtk_init()) {
return;
}
GtkWidget *dialog = gtk_message_dialog_new( NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
"Error: %s", message);
gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE );
static int x = 0, y = 0;
if (x != 0)
gtk_window_move( GTK_WINDOW(dialog), x, y);
// show
gtk_dialog_run( GTK_DIALOG(dialog) );
// remember position
gtk_window_get_position( GTK_WINDOW(dialog), &x, &y);
// done
gtk_widget_destroy( dialog );
wait_for_event();
#endif
}

24
DialogToolkit.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef DIALOGTOOLKIT_H
#define DIALOGTOOLKIT_H
#include <string>
namespace DialogToolkit
{
std::string saveSessionFileDialog(const std::string &path);
std::string openSessionFileDialog(const std::string &path);
std::string ImportFileDialog(const std::string &path);
std::string FolderDialog(const std::string &path);
void ErrorDialog(const char* message);
}
#endif // DIALOGTOOLKIT_H

View File

@@ -16,13 +16,11 @@ DrawVisitor::DrawVisitor(Node *nodetodraw, glm::mat4 projection, bool force): fo
transform_duplicat_ = glm::identity<glm::mat4>();
}
DrawVisitor::DrawVisitor(std::vector<Node *> nodestodraw, glm::mat4 projection, bool force): force_(force)
DrawVisitor::DrawVisitor(const std::vector<Node *> &nodestodraw, glm::mat4 projection, bool force):
force_(force), targets_(nodestodraw), modelview_(glm::identity<glm::mat4>()),
projection_(projection), num_duplicat_(1), transform_duplicat_(glm::identity<glm::mat4>())
{
targets_ = nodestodraw;
modelview_ = glm::identity<glm::mat4>();
projection_ = projection;
num_duplicat_ = 1;
transform_duplicat_ = glm::identity<glm::mat4>();
}
void DrawVisitor::loop(int num, glm::mat4 transform)
@@ -70,7 +68,7 @@ void DrawVisitor::visit(Group &n)
// traverse children
glm::mat4 mv = modelview_;
for (NodeSet::iterator node = n.begin(); !targets_.empty() && node != n.end(); node++) {
for (NodeSet::iterator node = n.begin(); !targets_.empty() && node != n.end(); ++node) {
if ( (*node)->visible_ || force_)
(*node)->accept(*this);
modelview_ = mv;

View File

@@ -15,7 +15,7 @@ class DrawVisitor : public Visitor
public:
DrawVisitor(Node *nodetodraw, glm::mat4 projection, bool force = false);
DrawVisitor(std::vector<Node *> nodestodraw, glm::mat4 projection, bool force = false);
DrawVisitor(const std::vector<Node *> &nodestodraw, glm::mat4 projection, bool force = false);
void loop(int num, glm::mat4 transform);

View File

@@ -16,7 +16,7 @@
FrameGrabbing::FrameGrabbing(): pbo_index_(0), pbo_next_index_(0), size_(0), width_(0), height_(0), use_alpha_(0), caps_(nullptr)
FrameGrabbing::FrameGrabbing(): pbo_index_(0), pbo_next_index_(0), size_(0), width_(0), height_(0), use_alpha_(0), caps_(NULL)
{
pbo_[0] = 0;
pbo_[1] = 0;
@@ -28,7 +28,7 @@ FrameGrabbing::~FrameGrabbing()
clearAll();
// cleanup
if (caps_!=nullptr)
if (caps_)
gst_caps_unref (caps_);
if (pbo_[0])
glDeleteBuffers(2, pbo_);
@@ -53,7 +53,7 @@ struct fgId: public std::unary_function<FrameGrabber*, bool>
inline bool operator()(const FrameGrabber* elem) const {
return (elem && elem->id() == _id);
}
fgId(uint64_t id) : _id(id) { }
explicit fgId(uint64_t id) : _id(id) { }
private:
uint64_t _id;
};
@@ -73,7 +73,7 @@ FrameGrabber *FrameGrabbing::get(uint64_t id)
void FrameGrabbing::stopAll()
{
std::list<FrameGrabber *>::iterator iter;
for (iter=grabbers_.begin(); iter != grabbers_.end(); iter++ )
for (iter=grabbers_.begin(); iter != grabbers_.end(); ++iter )
(*iter)->stop();
}
@@ -121,7 +121,7 @@ void FrameGrabbing::grabFrame(FrameBuffer *frame_buffer, float dt)
pbo_next_index_ = 0;
// new caps
if (caps_!=nullptr)
if (caps_)
gst_caps_unref (caps_);
caps_ = gst_caps_new_simple ("video/x-raw",
"format", G_TYPE_STRING, use_alpha_ ? "RGBA" : "RGB",
@@ -193,7 +193,7 @@ void FrameGrabbing::grabFrame(FrameBuffer *frame_buffer, float dt)
delete rec;
}
else
iter++;
++iter;
}
// unref / free the frame

View File

@@ -58,7 +58,7 @@ void GarbageVisitor::visit(Group &n)
// loop over members of a group
// and stop when found
for (NodeSet::iterator node = n.begin(); !found_ && node != n.end(); node++) {
for (NodeSet::iterator node = n.begin(); !found_ && node != n.end(); ++node) {
// visit the child node
(*node)->accept(*this);
// un-stack recursive browsing

View File

@@ -29,11 +29,11 @@
GeometryView::GeometryView() : View(GEOMETRY)
{
scene.root()->scale_ = glm::vec3(GEOMETRY_DEFAULT_SCALE, GEOMETRY_DEFAULT_SCALE, 1.0f);
// read default settings
if ( Settings::application.views[mode_].name.empty() ) {
// no settings found: store application default
Settings::application.views[mode_].name = "Geometry";
scene.root()->scale_ = glm::vec3(GEOMETRY_DEFAULT_SCALE, GEOMETRY_DEFAULT_SCALE, 1.0f);
saveSettings();
}
else
@@ -136,14 +136,19 @@ void GeometryView::update(float dt)
FrameBuffer *output = Mixer::manager().session()->frame();
if (output){
float aspect_ratio = output->aspectRatio();
for (NodeSet::iterator node = scene.bg()->begin(); node != scene.bg()->end(); node++) {
for (NodeSet::iterator node = scene.bg()->begin(); node != scene.bg()->end(); ++node) {
(*node)->scale_.x = aspect_ratio;
}
for (NodeSet::iterator node = scene.fg()->begin(); node != scene.fg()->end(); node++) {
for (NodeSet::iterator node = scene.fg()->begin(); node != scene.fg()->end(); ++node) {
(*node)->scale_.x = aspect_ratio;
}
output_surface_->setTextureIndex( output->texture() );
}
// prevent invalid scaling
float s = CLAMP(scene.root()->scale_.x, GEOMETRY_MIN_SCALE, GEOMETRY_MAX_SCALE);
scene.root()->scale_.x = s;
scene.root()->scale_.y = s;
}
// the current view is the geometry view
@@ -266,7 +271,6 @@ void GeometryView::draw()
show_context_menu_ = MENU_NONE;
}
if (ImGui::BeginPopup("GeometrySourceContextMenu")) {
Source *s = Mixer::manager().currentSource();
if (s != nullptr) {
if (ImGui::Selectable( ICON_FA_EXPAND " Fit" )){
s->group(mode_)->scale_ = glm::vec3(output_surface_->scale_.x/ s->frame()->aspectRatio(), 1.f, 1.f);
@@ -594,8 +598,8 @@ View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::p
// cancel out scaling with SHIFT modifier key
if (UserInterface::manager().shiftModifier()) {
overlay_rotation_fix_->visible_ = true;
float factor = glm::length( glm::vec2( overlay_selection_->scale_ ) ) / glm::length( glm::vec2( overlay_selection_stored_status_->scale_ ) );
S = glm::scale(glm::identity<glm::mat4>(), glm::vec3(factor, factor, 1.f));
float scale_factor = glm::length( glm::vec2( overlay_selection_->scale_ ) ) / glm::length( glm::vec2( overlay_selection_stored_status_->scale_ ) );
S = glm::scale(glm::identity<glm::mat4>(), glm::vec3(scale_factor, scale_factor, 1.f));
}
// compute rotation angle
@@ -903,9 +907,9 @@ View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::p
overlay_rotation_fix_->copyTransform(overlay_rotation_);
overlay_rotation_clock_->visible_ = false;
// rotation center to center of source (disregarding scale)
glm::mat4 T = glm::translate(glm::identity<glm::mat4>(), s->stored_status_->translation_);
source_from = glm::inverse(T) * glm::vec4( scene_from, 1.f );
source_to = glm::inverse(T) * glm::vec4( scene_to, 1.f );
glm::mat4 M = glm::translate(glm::identity<glm::mat4>(), s->stored_status_->translation_);
source_from = glm::inverse(M) * glm::vec4( scene_from, 1.f );
source_to = glm::inverse(M) * glm::vec4( scene_to, 1.f );
// compute rotation angle
float angle = glm::orientedAngle( glm::normalize(glm::vec2(source_from)), glm::normalize(glm::vec2(source_to)));
// apply rotation on Z axis

View File

@@ -59,9 +59,9 @@ void GlmToolkit::inverse_transform(glm::mat4 M, glm::vec3 &translation, glm::vec
// return angle;
//}
GlmToolkit::AxisAlignedBoundingBox::AxisAlignedBoundingBox() {
mMin = glm::vec3(1.f);
mMax = glm::vec3(-1.f);
GlmToolkit::AxisAlignedBoundingBox::AxisAlignedBoundingBox() :
mMin(glm::vec3(1.f)), mMax(glm::vec3(-1.f))
{
}
void GlmToolkit::AxisAlignedBoundingBox::extend(const glm::vec3& point)

View File

@@ -134,39 +134,76 @@ string GstToolkit::gst_version()
return oss.str();
}
// see https://developer.ridgerun.com/wiki/index.php?title=GStreamer_modify_the_elements_rank
std::list<std::string> GstToolkit::enable_gpu_decoding_plugins(bool enable)
{
static list<string> pluginslist;
static GstRegistry* plugins_register = nullptr;
if ( plugins_register == nullptr ) {
plugins_register = gst_registry_get();
#if GST_GL_HAVE_PLATFORM_GLX
// https://gstreamer.freedesktop.org/documentation/nvcodec/index.html?gi-language=c#plugin-nvcodec
const char *plugins[6] = { "nvh264dec", "nvh265dec", "nvmpeg2videodec",
"nvmpeg4videodec", "nvvp8dec", "nvvp9dec" };
const int N = 6;
// https://gstreamer.freedesktop.org/documentation/nvcodec/index.html?gi-language=c#plugin-nvcodec
const char *plugins[10] = { "omxmpeg4videodec", "omxmpeg2dec", "omxh264dec", "vdpaumpegdec",
"nvh264dec", "nvh265dec", "nvmpeg2videodec",
"nvmpeg4videodec", "nvvp8dec", "nvvp9dec"
};
const int N = 10;
#elif GST_GL_HAVE_PLATFORM_CGL
const char *plugins[1] = { "vtdec_hw" };
const int N = 1;
const char *plugins[2] = { "vtdec_hw", "vtdechw" };
const int N = 2;
#else
const char *plugins[0] = { };
const int N = 0;
const char *plugins[0] = { };
const int N = 0;
#endif
// see https://developer.ridgerun.com/wiki/index.php?title=GStreamer_modify_the_elements_rank
std::list<std::string> GstToolkit::enable_gpu_decoding_plugins(bool enable)
{
list<string> plugins_list_;
static GstRegistry* plugins_register = nullptr;
if ( plugins_register == nullptr )
plugins_register = gst_registry_get();
static bool enabled_ = false;
if (enabled_ != enable) {
enabled_ = enable;
for (int i = 0; i < N; i++) {
GstPluginFeature* feature = gst_registry_lookup_feature(plugins_register, plugins[i]);
if(feature != NULL) {
pluginslist.push_front( string( plugins[i] ) );
plugins_list_.push_front( string( plugins[i] ) );
gst_plugin_feature_set_rank(feature, enable ? GST_RANK_PRIMARY + 1 : GST_RANK_MARGINAL);
gst_object_unref(feature);
}
}
}
return pluginslist;
return plugins_list_;
}
std::string GstToolkit::used_gpu_decoding_plugins(GstElement *gstbin)
{
std::string found = "";
GstIterator* it = gst_bin_iterate_recurse(GST_BIN(gstbin));
GValue value = G_VALUE_INIT;
for(GstIteratorResult r = gst_iterator_next(it, &value); r != GST_ITERATOR_DONE; r = gst_iterator_next(it, &value))
{
if ( r == GST_ITERATOR_OK )
{
GstElement *e = static_cast<GstElement*>(g_value_peek_pointer(&value));
if (e) {
gchar *name = gst_element_get_name(e);
// g_print(" - %s", name);
std::string e_name(name);
g_free(name);
for (int i = 0; i < N; i++) {
if (e_name.find(plugins[i]) != std::string::npos) {
found = plugins[i];
break;
}
}
}
}
g_value_unset(&value);
}
gst_iterator_free(it);
return found;
}

View File

@@ -22,6 +22,7 @@ std::string gst_version();
std::list<std::string> all_plugins();
std::list<std::string> enable_gpu_decoding_plugins(bool enable = true);
std::string used_gpu_decoding_plugins(GstElement *gstbin);
std::list<std::string> all_plugin_features(std::string pluginname);
bool enable_feature (std::string name, bool enable);

View File

@@ -82,7 +82,7 @@ void ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* hel
// animation
ImGuiContext& g = *GImGui;
float ANIM_SPEED = 0.1f;
const float ANIM_SPEED = 0.1f;
if (g.LastActiveId == g.CurrentWindow->GetID(label))// && g.LastActiveIdTimer < ANIM_SPEED)
{
float t_anim = ImSaturate(g.LastActiveIdTimer / ANIM_SPEED);
@@ -278,7 +278,7 @@ bool ImGuiToolkit::ComboIcon (std::vector<std::pair<int, int> > icons, std::vect
{
std::vector<std::pair<int, int> >::iterator it_icon = icons.begin();
std::vector<std::string>::iterator it_label = labels.begin();
for(int i = 0 ; it_icon != icons.end(); i++, it_icon++, it_label++) {
for(int i = 0 ; it_icon != icons.end(); i++, ++it_icon, ++it_label) {
ImGui::PushID( id.sum + i + 1);
ImVec2 pos = ImGui::GetCursorScreenPos();
// combo selectable item
@@ -309,9 +309,7 @@ void ImGuiToolkit::ShowIconsWindow(bool* p_open)
if (textureicons == 0)
textureicons = Resource::getTextureDDS("images/icons.dds");
ImGuiIO& io = ImGui::GetIO();
float my_tex_w = 640.0;
float my_tex_h = 640.0;
const ImGuiIO& io = ImGui::GetIO();
ImGui::Begin("Icons", p_open);
@@ -319,6 +317,8 @@ void ImGuiToolkit::ShowIconsWindow(bool* p_open)
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(640, 640));
if (ImGui::IsItemHovered())
{
float my_tex_w = 640.0;
float my_tex_h = 640.0;
float zoom = 4.0f;
float region_sz = 32.0f; // 64 x 64 icons
float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
@@ -394,9 +394,9 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 star
return false;
// get style & id
const ImGuiStyle& style = ImGui::GetStyle();
const float fontsize = ImGui::GetFontSize();
ImGuiContext& g = *GImGui;
const ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const float fontsize = g.FontSize;
const ImGuiID id = window->GetID(label);
//
@@ -499,16 +499,12 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 star
ImU32 color = ImGui::GetColorU32( style.Colors[ImGuiCol_Text] );
pos = timeline_bbox.GetTL();
guint64 tick = 0;
float tick_percent = 0.f;
char overlay_buf[24];
ImVec2 overlay_size = ImVec2(0.f, 0.f);
ImVec2 mini = ImVec2(0.f, 0.f);
ImVec2 maxi = ImVec2(0.f, 0.f);
// render text duration
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%s",
GstToolkit::time_to_string(end, GstToolkit::TIME_STRING_MINIMAL).c_str());
overlay_size = ImGui::CalcTextSize(overlay_buf, NULL);
ImVec2 overlay_size = ImGui::CalcTextSize(overlay_buf, NULL);
ImVec2 duration_label = bbox.GetBR() - overlay_size - ImVec2(3.f, 3.f);
if (overlay_size.x > 0.0f)
ImGui::RenderTextClipped( duration_label, bbox.Max, overlay_buf, NULL, &overlay_size);
@@ -526,8 +522,8 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 star
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%s",
GstToolkit::time_to_string(tick, GstToolkit::TIME_STRING_MINIMAL).c_str());
overlay_size = ImGui::CalcTextSize(overlay_buf, NULL);
mini = ImVec2( pos.x - overlay_size.x / 2.f, pos.y + tick_length );
maxi = ImVec2( pos.x + overlay_size.x / 2.f, pos.y + tick_length + overlay_size.y );
ImVec2 mini = ImVec2( pos.x - overlay_size.x / 2.f, pos.y + tick_length );
ImVec2 maxi = ImVec2( pos.x + overlay_size.x / 2.f, pos.y + tick_length + overlay_size.y );
// do not overlap with label for duration
if (maxi.x < duration_label.x)
ImGui::RenderTextClipped(mini, maxi, overlay_buf, NULL, &overlay_size);
@@ -538,7 +534,7 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 star
// next tick
tick += tick_step;
tick_percent = static_cast<float> ( static_cast<double>(tick) / static_cast<double>(end) );
float tick_percent = static_cast<float> ( static_cast<double>(tick) / static_cast<double>(end) );
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
}
@@ -616,7 +612,6 @@ bool ImGuiToolkit::InvisibleSliderInt(const char* label, uint *index, uint min,
bool ImGuiToolkit::EditPlotLines(const char* label, float *array, int values_count, float values_min, float values_max, const ImVec2 size)
{
static uint previous_index = UINT32_MAX;
bool array_changed = false;
// get window
@@ -661,12 +656,12 @@ bool ImGuiToolkit::EditPlotLines(const char* label, float *array, int values_cou
// enter edit if widget is active
if (ImGui::GetActiveID() == id) {
static uint previous_index = UINT32_MAX;
bg_color = colors[ImGuiCol_FrameBgActive];
// keep active area while mouse is pressed
if (left_mouse_press)
{
float x = (float) values_count * mouse_pos_in_canvas.x / bbox.GetWidth();
uint index = CLAMP( (int) floor(x), 0, values_count-1);
@@ -710,8 +705,6 @@ bool ImGuiToolkit::EditPlotLines(const char* label, float *array, int values_cou
bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array, float *lines_array,
int values_count, float values_min, float values_max, bool *released, const ImVec2 size)
{
static bool active = false;
static uint previous_index = UINT32_MAX;
bool array_changed = false;
// get window
@@ -763,6 +756,8 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array,
bg_color = colors[ImGuiCol_FrameBgActive];
// keep active area while mouse is pressed
static bool active = false;
static uint previous_index = UINT32_MAX;
if (mouse_press)
{

View File

@@ -9,6 +9,9 @@
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/constants.hpp>
#include <tinyxml2.h>
#include "tinyxml2Toolkit.h"
#include "defines.h"
#include "Log.h"
#include "Scene.h"
@@ -21,6 +24,8 @@
#include "PatternSource.h"
#include "DeviceSource.h"
#include "NetworkSource.h"
#include "SessionCreator.h"
#include "SessionVisitor.h"
#include "Settings.h"
#include "Mixer.h"
#include "ActionManager.h"
@@ -237,11 +242,7 @@ void ImGuiVisitor::visit(ImageProcessingShader &n)
{
ImGui::PushID(std::to_string(n.id()).c_str());
if (ImGuiToolkit::ButtonIcon(6, 2)) {
ImageProcessingShader defaultvalues;
n = defaultvalues;
Action::manager().store("Reset Filters");
}
ImGuiToolkit::Icon(6, 2);
ImGui::SameLine(0, 10);
ImGui::Text("Filters");
@@ -463,13 +464,45 @@ void ImGuiVisitor::visit (Source& s)
}
s.setImageProcessingEnabled(on);
ImGui::SetCursorPosY(pos.y + preview_height); // ...come back
// image processing pannel
if (s.imageProcessingEnabled())
s.processingShader()->accept(*this);
if (s.imageProcessingEnabled()) {
// geometry direct control
// menu icon for image processing
ImGui::SetCursorPos( ImVec2( preview_width - ImGui::GetTextLineHeight(), pos.y + 4.5f * ImGui::GetFrameHeightWithSpacing())); // ...come back
if (ImGuiToolkit::IconButton(5, 8))
ImGui::OpenPopup( "MenuImageProcessing" );
if (ImGui::BeginPopup( "MenuImageProcessing" ))
{
if (ImGui::MenuItem("Reset" )){
ImageProcessingShader defaultvalues;
s.processingShader()->copy(defaultvalues);
std::ostringstream oss;
oss << s.name() << ": " << "Reset Filter";
Action::manager().store(oss.str());
}
if (ImGui::MenuItem("Copy" )){
std::string clipboard = SessionVisitor::getClipboard(s.processingShader());
if (!clipboard.empty())
ImGui::SetClipboardText(clipboard.c_str());
}
const char *clipboard = ImGui::GetClipboardText();
const bool can_paste = (clipboard != nullptr && SessionLoader::isClipboard(clipboard));
if (ImGui::MenuItem("Paste", NULL, false, can_paste)) {
SessionLoader::applyImageProcessing(s, clipboard);
std::ostringstream oss;
oss << s.name() << ": " << "Change Filter";
Action::manager().store(oss.str());
}
ImGui::EndPopup();
}
// full panel for image processing
ImGui::SetCursorPos( ImVec2( pos.x, pos.y + preview_height)); // ...come back
s.processingShader()->accept(*this);
}
// geometry direct control for DEBUG
// s.groupNode(View::GEOMETRY)->accept(*this);
// s.groupNode((View::Mode) Settings::application.current_view)->accept(*this);
@@ -488,6 +521,8 @@ void ImGuiVisitor::visit (MediaSource& s)
if ( ImGui::Button(IMGUI_TITLE_MEDIAPLAYER, ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
UserInterface::manager().showMediaPlayer( s.mediaplayer());
ImGuiToolkit::ButtonOpenUrl( SystemToolkit::path_filename(s.path()).c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::SameLine();
ImGui::Text("Folder");
}
void ImGuiVisitor::visit (SessionFileSource& s)
@@ -513,8 +548,12 @@ void ImGuiVisitor::visit (SessionFileSource& s)
}
if ( ImGui::Button( ICON_FA_FILE_UPLOAD " Open Session", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
Mixer::manager().set( s.detach() );
ImGui::SameLine();
ImGui::Text("File");
ImGuiToolkit::ButtonOpenUrl( SystemToolkit::path_filename(s.path()).c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::SameLine();
ImGui::Text("Folder");
ImGui::Text("Contains %d sources.", s.session()->numSource());
if ( ImGui::Button( ICON_FA_FILE_EXPORT " Import", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
@@ -553,6 +592,8 @@ void ImGuiVisitor::visit (CloneSource& s)
ImGui::Text("Clone");
if ( ImGui::Button(s.origin()->name().c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
Mixer::manager().setCurrentSource(s.origin());
ImGui::SameLine();
ImGui::Text("Source");
}
void ImGuiVisitor::visit (PatternSource& s)
@@ -574,6 +615,8 @@ void ImGuiVisitor::visit (PatternSource& s)
}
ImGui::EndCombo();
}
ImGui::SameLine();
ImGui::Text("Generator");
}
void ImGuiVisitor::visit (DeviceSource& s)

View File

@@ -15,25 +15,6 @@ ImageProcessingShader::ImageProcessingShader(): Shader()
reset();
}
ImageProcessingShader::ImageProcessingShader(const ImageProcessingShader &S): Shader()
{
program_ = &imageProcessingShadingProgram;
reset();
brightness = S.brightness;
contrast = S.contrast;
saturation = S.saturation;
hueshift = S.hueshift;
threshold = S.threshold;
lumakey = S.lumakey;
nbColors = S.nbColors;
invert = S.invert;
filterid = S.filterid;
gamma = S.gamma;
levels = S.levels;
chromakey = S.chromakey;
chromadelta = S.chromadelta;
}
void ImageProcessingShader::use()
{
Shader::use();
@@ -56,7 +37,6 @@ void ImageProcessingShader::use()
}
void ImageProcessingShader::reset()
{
Shader::reset();
@@ -78,7 +58,7 @@ void ImageProcessingShader::reset()
}
void ImageProcessingShader::operator = (const ImageProcessingShader &S )
void ImageProcessingShader::copy(const ImageProcessingShader &S)
{
brightness = S.brightness;
contrast = S.contrast;
@@ -95,6 +75,11 @@ void ImageProcessingShader::operator = (const ImageProcessingShader &S )
chromadelta = S.chromadelta;
}
void ImageProcessingShader::operator = (const ImageProcessingShader &S )
{
copy(S);
}
void ImageProcessingShader::accept(Visitor& v)
{

View File

@@ -11,13 +11,13 @@ class ImageProcessingShader : public Shader
public:
ImageProcessingShader();
ImageProcessingShader(const ImageProcessingShader &model);
void use() override;
void reset() override;
void accept(Visitor& v) override;
void operator = (const ImageProcessingShader &S);
void copy(const ImageProcessingShader &S);
// color effects
float brightness; // [-1 1]

View File

@@ -9,12 +9,20 @@
ShadingProgram imageShadingProgram("shaders/image.vs", "shaders/image.fs");
ShadingProgram imageAlphaProgram ("shaders/image.vs", "shaders/imageblending.fs");
std::vector< ShadingProgram > maskPrograms = {
ShadingProgram("shaders/simple.vs", "shaders/simple.fs"),
ShadingProgram("shaders/image.vs", "shaders/mask_draw.fs"),
ShadingProgram("shaders/simple.vs", "shaders/mask_elipse.fs"),
ShadingProgram("shaders/simple.vs", "shaders/mask_round.fs"),
ShadingProgram("shaders/simple.vs", "shaders/mask_box.fs"),
ShadingProgram("shaders/simple.vs", "shaders/mask_horizontal.fs"),
ShadingProgram("shaders/simple.vs", "shaders/mask_vertical.fs")
};
const char* MaskShader::mask_names[3] = { ICON_FA_EXPAND, ICON_FA_EDIT, ICON_FA_SHAPES };
const char* MaskShader::mask_shapes[5] = { "Elipse", "Oblong", "Rectangle", "Horizontal", "Vertical" };
std::vector< ShadingProgram* > MaskShader::mask_programs;
ImageShader::ImageShader(): Shader(), stipple(0.0)
ImageShader::ImageShader(): Shader(), stipple(0.f), mask_texture(0)
{
// static program shader
program_ = &imageShadingProgram;
@@ -29,6 +37,10 @@ void ImageShader::use()
// set stippling
program_->setUniform("stipple", stipple);
// default mask
if (mask_texture == 0)
mask_texture = Resource::getTextureWhite();
// setup mask texture
glActiveTexture(GL_TEXTURE1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -40,9 +52,8 @@ void ImageShader::use()
void ImageShader::reset()
{
Shader::reset();
mask_texture = 0;
// default mask
mask_texture = Resource::getTextureWhite();
// no stippling
stipple = 0.f;
}
@@ -75,28 +86,18 @@ AlphaShader::AlphaShader(): ImageShader()
MaskShader::MaskShader(): Shader(), mode(0)
{
// first initialization
if ( mask_programs.empty() ) {
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/simple.fs"));
mask_programs.push_back(new ShadingProgram("shaders/image.vs", "shaders/mask_draw.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_elipse.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_round.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_box.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_horizontal.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_vertical.fs"));
}
// reset instance
reset();
// static program shader
program_ = mask_programs[0];
program_ = &maskPrograms[0];
}
void MaskShader::use()
{
// select program to use
mode = CLAMP(mode, 0, 2);
shape = CLAMP(shape, 0, 4);
program_ = mode < 2 ? mask_programs[mode] : mask_programs[shape+2] ;
mode = MINI(mode, 2);
shape = MINI(shape, 4);
program_ = mode < 2 ? &maskPrograms[mode] : &maskPrograms[shape+2] ;
// actual use of shader program
Shader::use();

View File

@@ -74,7 +74,6 @@ public:
static const char* mask_names[3];
static const char* mask_shapes[5];
static std::vector< ShadingProgram* > mask_programs;
};
#endif // IMAGESHADER_H

View File

@@ -25,16 +25,17 @@
LayerView::LayerView() : View(LAYER), aspect_ratio(1.f)
{
scene.root()->scale_ = glm::vec3(LAYER_DEFAULT_SCALE, LAYER_DEFAULT_SCALE, 1.0f);
scene.root()->translation_ = glm::vec3(2.2f, 1.2f, 0.0f);
// read default settings
if ( Settings::application.views[mode_].name.empty() ) {
// no settings found: store application default
Settings::application.views[mode_].name = "Layer";
scene.root()->scale_ = glm::vec3(LAYER_DEFAULT_SCALE, LAYER_DEFAULT_SCALE, 1.0f);
scene.root()->translation_ = glm::vec3(2.2f, 1.2f, 0.0f);
saveSettings();
}
else
else {
restoreSettings();
}
// Geometry Scene background
frame_ = new Group;
@@ -79,7 +80,7 @@ void LayerView::draw()
// start loop on selection
SourceList::iterator it = Mixer::selection().begin();
float depth_first = (*it)->depth();
for (; it != Mixer::selection().end(); it++) {
for (; it != Mixer::selection().end(); ++it) {
// test if selection is contiguous in layer (i.e. not interrupted)
SourceList::iterator inter = Mixer::manager().session()->find(depth_first, (*it)->depth());
if ( inter != Mixer::manager().session()->end() && !Mixer::selection().contains(*inter)){
@@ -117,7 +118,7 @@ void LayerView::draw()
SourceList::iterator it = dsl.begin();
float depth = (*it)->depth();
float depth_inc = (dsl.back()->depth() - depth) / static_cast<float>(Mixer::selection().size()-1);
for (it++; it != dsl.end(); it++) {
for (++it; it != dsl.end(); ++it) {
depth += depth_inc;
(*it)->setDepth(depth);
}
@@ -128,7 +129,7 @@ void LayerView::draw()
SourceList dsl = depth_sorted(Mixer::selection().getCopy());
SourceList::iterator it = dsl.begin();
float depth = (*it)->depth();
for (it++; it != dsl.end(); it++) {
for (++it; it != dsl.end(); ++it) {
depth += LAYER_STEP;
(*it)->setDepth(depth);
}
@@ -139,7 +140,7 @@ void LayerView::draw()
SourceList dsl = depth_sorted(Mixer::selection().getCopy());
SourceList::iterator it = dsl.begin();
SourceList::reverse_iterator rit = dsl.rbegin();
for (; it != dsl.end(); it++, rit++) {
for (; it != dsl.end(); ++it, ++rit) {
(*it)->setDepth((*rit)->depth());
}
Action::manager().store(std::string("Selection Layer Reverse order."));
@@ -168,6 +169,11 @@ void LayerView::update(float dt)
persp_left_->translation_.x = -aspect_ratio;
persp_right_->translation_.x = aspect_ratio + 0.06;
}
// prevent invalid scaling
float s = CLAMP(scene.root()->scale_.x, LAYER_MIN_SCALE, LAYER_MAX_SCALE);
scene.root()->scale_.x = s;
scene.root()->scale_.y = s;
}
if (Mixer::manager().view() == this )
@@ -262,7 +268,7 @@ float LayerView::setDepth(Source *s, float d)
depth = LAYER_BACKGROUND + LAYER_STEP;
// find the front-most souce in the workspace (behind FOREGROUND)
for (NodeSet::iterator node = scene.ws()->begin(); node != scene.ws()->end(); node++) {
for (NodeSet::iterator node = scene.ws()->begin(); node != scene.ws()->end(); ++node) {
// place in front of previous sources
depth = MAX(depth, (*node)->translation_.z + LAYER_STEP);

View File

@@ -24,7 +24,6 @@ private:
void updateSelectionOverlay() override;
float aspect_ratio;
Mesh *persp_layer_;
Mesh *persp_left_, *persp_right_;
Group *frame_;

21
Log.cpp
View File

@@ -1,4 +1,7 @@
#include "Log.h"
#include <string>
#include <list>
#include <mutex>
using namespace std;
#include "imgui.h"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
@@ -6,16 +9,11 @@
#endif
#include "imgui_internal.h"
#include "ImGuiToolkit.h"
#include "defines.h"
#include "ImGuiToolkit.h"
#include "DialogToolkit.h"
#include "Log.h"
// multiplatform
#include <tinyfiledialogs.h>
#include <string>
#include <list>
#include <mutex>
using namespace std;
static std::mutex mtx;
@@ -207,7 +205,7 @@ void Log::Render(bool *showWarnings)
if (!show_notification && !show_warnings)
return;
ImGuiIO& io = ImGui::GetIO();
const ImGuiIO& io = ImGui::GetIO();
float width = io.DisplaySize.x * 0.4f;
float pos = io.DisplaySize.x * 0.3f;
@@ -296,7 +294,8 @@ void Log::Error(const char* fmt, ...)
buf.appendfv(fmt, args);
va_end(args);
tinyfd_messageBox( APP_TITLE, buf.c_str(), "ok", "error", 0);
DialogToolkit::ErrorDialog(buf.c_str());
Log::Info("Error - %s\n", buf.c_str());
}

View File

@@ -21,8 +21,6 @@ using namespace std;
#define MEDIA_PLAYER_DEBUG
#endif
#define USE_GST_APPSINK_CALLBACKS
std::list<MediaPlayer*> MediaPlayer::registered_;
MediaPlayer::MediaPlayer()
@@ -37,6 +35,8 @@ MediaPlayer::MediaPlayer()
failed_ = false;
seeking_ = false;
enabled_ = true;
force_software_decoding_ = false;
hardware_decoder_ = "";
rate_ = 1.0;
position_ = GST_CLOCK_TIME_NONE;
desired_state_ = GST_STATE_PAUSED;
@@ -59,6 +59,11 @@ MediaPlayer::MediaPlayer()
MediaPlayer::~MediaPlayer()
{
close();
// cleanup opengl texture
if (textureindex_)
glDeleteTextures(1, &textureindex_);
}
void MediaPlayer::accept(Visitor& v) {
@@ -163,7 +168,8 @@ static MediaInfo UriDiscoverer_(std::string uri)
gchar *container = NULL;
if ( gst_tag_list_get_string (tags, GST_TAG_CONTAINER_FORMAT, &container) )
video_stream_info.codec_name += " " + std::string(container);
g_free(container);
if (container)
g_free(container);
}
// exit loop
// inform that it succeeded
@@ -177,6 +183,7 @@ static MediaInfo UriDiscoverer_(std::string uri)
}
}
gst_discoverer_info_unref (info);
g_object_unref (discoverer);
}
@@ -202,14 +209,29 @@ void MediaPlayer::open(string path)
}
void MediaPlayer::reopen()
{
// re-openning is meaningfull only if it was already open
if (pipeline_ != nullptr) {
// reload : terminate pipeline and re-create it
close();
execute_open();
}
}
void MediaPlayer::execute_open()
{
// Create gstreamer pipeline :
// " uridecodebin uri=file:///path_to_file/filename.mp4 ! videoconvert ! appsink "
// equivalent to gst-launch-1.0 uridecodebin uri=file:///path_to_file/filename.mp4 ! videoconvert ! ximagesink
string description = "uridecodebin uri=" + uri_ + " ! ";
// "uridecodebin uri=file:///path_to_file/filename.mp4 ! videoconvert ! appsink "
// equivalent to command line
// "gst-launch-1.0 uridecodebin uri=file:///path_to_file/filename.mp4 ! videoconvert ! ximagesink"
string description = "uridecodebin name=decoder uri=" + uri_ + " ! queue max-size-time=0 ! ";
// NB: queue adds some control over the buffer, thereby limiting the frame delay. zero size means no buffering
// video deinterlacing method
// string description = "uridecodebin name=decoder uri=" + uri_ + " decoder. ! ";
// description += "audioconvert ! autoaudiosink decoder. ! ";
// video deinterlacing method (if media is interlaced)
// tomsmocomp (0) Motion Adaptive: Motion Search
// greedyh (1) Motion Adaptive: Advanced Detection
// greedyl (2) Motion Adaptive: Simple Detection
@@ -219,12 +241,19 @@ void MediaPlayer::execute_open()
if (media_.interlaced)
description += "deinterlace method=2 ! ";
// video convertion chroma-resampler
// video convertion algorithm (should only do colorspace conversion, no scaling)
// chroma-resampler:
// Duplicates the samples when upsampling and drops when downsampling 0
// Uses linear interpolation 1 (default)
// Uses cubic interpolation 2
// Uses sinc interpolation 3
description += "videoconvert chroma-resampler=2 n-threads=2 ! ";
// dither:
// no dithering 0
// propagate rounding errors downwards 1
// Dither with floyd-steinberg error diffusion 2
// Dither with Sierra Lite error diffusion 3
// ordered dither using a bayer pattern 4 (default)
description += "videoconvert chroma-resampler=1 dither=0 ! "; // fast
// hack to compensate for lack of PTS in gif animations
if (media_.codec_name.compare("image/gst-libav-gif") == 0){
@@ -245,7 +274,9 @@ void MediaPlayer::execute_open()
failed_ = true;
return;
}
// setup pipeline
g_object_set(G_OBJECT(pipeline_), "name", std::to_string(id_).c_str(), NULL);
gst_pipeline_set_auto_flush_bus( GST_PIPELINE(pipeline_), true);
// GstCaps *caps = gst_static_caps_get (&frame_render_caps);
string capstring = "video/x-raw,format=RGBA,width="+ std::to_string(media_.width) +
@@ -257,6 +288,11 @@ void MediaPlayer::execute_open()
return;
}
// setup uridecodebin
if (force_software_decoding_) {
g_object_set (G_OBJECT (gst_bin_get_by_name (GST_BIN (pipeline_), "decoder")), "force-sw-decoders", true, NULL);
}
// setup appsink
GstElement *sink = gst_bin_get_by_name (GST_BIN (pipeline_), "sink");
if (sink) {
@@ -343,12 +379,21 @@ bool MediaPlayer::failed() const
return failed_;
}
void MediaPlayer::Frame::unmap()
{
if ( full ) {
gst_video_frame_unmap(&vframe);
full = false;
}
}
void MediaPlayer::close()
{
// not openned?
if (!ready_ && discoverer_.valid()) {
if (!ready_) {
// wait for loading to finish
discoverer_.wait();
if (discoverer_.valid())
discoverer_.wait();
// nothing else to change
return;
}
@@ -358,9 +403,16 @@ void MediaPlayer::close()
// clean up GST
if (pipeline_ != nullptr) {
// force flush
GstState state;
gst_element_send_event(pipeline_, gst_event_new_seek (1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_END, 0) );
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
// end pipeline
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_NULL);
if (ret == GST_STATE_CHANGE_ASYNC) {
GstState state;
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
}
gst_object_unref (pipeline_);
@@ -368,22 +420,20 @@ void MediaPlayer::close()
}
// cleanup eventual remaining frame memory
for(guint i = 0; i < N_VFRAME; i++){
if ( frame_[i].full ) {
gst_video_frame_unmap(&frame_[i].vframe);
frame_[i].status = INVALID;
}
for(guint i = 0; i < N_VFRAME; i++) {
frame_[i].access.lock();
frame_[i].unmap();
frame_[i].access.unlock();
}
// cleanup opengl texture
if (textureindex_)
glDeleteTextures(1, &textureindex_);
textureindex_ = 0;
write_index_ = 0;
last_index_ = 0;
// cleanup picture buffer
if (pbo_[0])
glDeleteBuffers(2, pbo_);
pbo_size_ = 0;
pbo_index_ = 0;
pbo_next_index_ = 0;
#ifdef MEDIA_PLAYER_DEBUG
Log::Info("MediaPlayer %s closed", std::to_string(id_).c_str());
@@ -457,6 +507,28 @@ bool MediaPlayer::isImage() const
return media_.isimage;
}
std::string MediaPlayer::hardwareDecoderName()
{
return hardware_decoder_;
}
bool MediaPlayer::softwareDecodingForced()
{
return force_software_decoding_;
}
void MediaPlayer::setSoftwareDecodingForced(bool on)
{
bool need_reload = force_software_decoding_ != on;
// set parameter
force_software_decoding_ = on;
// changing state requires reload
if (need_reload)
reopen();
}
void MediaPlayer::play(bool on)
{
// ignore if disabled, and cannot play an image
@@ -660,6 +732,10 @@ void MediaPlayer::init_texture(guint index)
#ifdef MEDIA_PLAYER_DEBUG
Log::Info("MediaPlayer %s Using Pixel Buffer Object texturing.", std::to_string(id_).c_str());
#endif
// now that a frame is ready, and once only, browse into the pipeline
// for possible hadrware decoding plugins used. Empty string means none.
hardware_decoder_ = GstToolkit::used_gpu_decoding_plugins(pipeline_);
}
}
@@ -671,7 +747,6 @@ void MediaPlayer::fill_texture(guint index)
{
// initialize texture
init_texture(index);
}
else {
glBindTexture(GL_TEXTURE_2D, textureindex_);
@@ -719,17 +794,19 @@ void MediaPlayer::update()
return;
// not ready yet
if (!ready_ && discoverer_.valid()) {
// try to get info from discoverer
if (discoverer_.wait_for( std::chrono::milliseconds(4) ) == std::future_status::ready )
{
media_ = discoverer_.get();
// if its ok, open the media
if (media_.valid)
execute_open();
else {
Log::Warning("MediaPlayer %s Loading cancelled", std::to_string(id_).c_str());
failed_ = true;
if (!ready_) {
if (discoverer_.valid()) {
// try to get info from discoverer
if (discoverer_.wait_for( std::chrono::milliseconds(4) ) == std::future_status::ready )
{
media_ = discoverer_.get();
// if its ok, open the media
if (media_.valid)
execute_open();
else {
Log::Warning("MediaPlayer %s Loading cancelled", std::to_string(id_).c_str());
failed_ = true;
}
}
}
// wait next frame to display
@@ -764,7 +841,6 @@ void MediaPlayer::update()
// do not fill a frame twice
if (frame_[read_index].status != INVALID ) {
// is this an End-of-Stream frame ?
if (frame_[read_index].status == EOS )
{
@@ -780,6 +856,9 @@ void MediaPlayer::update()
// double update for pre-roll frame and dual PBO (ensure frame is displayed now)
if ( (frame_[read_index].status == PREROLL || seeking_ ) && pbo_size_ > 0)
fill_texture(read_index);
// free frame
frame_[read_index].unmap();
}
// we just displayed a vframe : set position time to frame PTS
@@ -787,11 +866,6 @@ void MediaPlayer::update()
// avoid reading it again
frame_[read_index].status = INVALID;
// // TODO : try to do something when the update is too slow :(
// if ( timecount_.dt() > frame_duration_ * 2) {
// Log::Info("frame late %d", 2 * frame_duration_);
// }
}
// unkock frame after reading it
@@ -923,7 +997,7 @@ float MediaPlayer::currentTimelineFading()
return media_.timeline.fadingAt(position_);
}
void MediaPlayer::setTimeline(Timeline tl)
void MediaPlayer::setTimeline(const Timeline &tl)
{
media_.timeline = tl;
}
@@ -971,10 +1045,7 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, FrameStatus status)
frame_[write_index_].access.lock();
// always empty frame before filling it again
if ( frame_[write_index_].full ) {
gst_video_frame_unmap(&frame_[write_index_].vframe);
frame_[write_index_].full = false;
}
frame_[write_index_].unmap();
// accept status of frame received
frame_[write_index_].status = status;
@@ -1008,8 +1079,14 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, FrameStatus status)
}
// full but invalid frame : will be deleted next iteration
// (should never happen)
else
else {
#ifdef MEDIA_PLAYER_DEBUG
Log::Info("MediaPlayer %s Received an Invalid frame", std::to_string(id_).c_str());
#endif
frame_[write_index_].status = INVALID;
frame_[write_index_].access.unlock();
return false;
}
}
// else; null buffer for EOS: give a position
else {
@@ -1038,7 +1115,7 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, FrameStatus status)
void MediaPlayer::callback_end_of_stream (GstAppSink *, gpointer p)
{
MediaPlayer *m = (MediaPlayer *)p;
MediaPlayer *m = static_cast<MediaPlayer *>(p);
if (m && m->ready_) {
m->fill_frame(NULL, MediaPlayer::EOS);
}
@@ -1054,13 +1131,13 @@ GstFlowReturn MediaPlayer::callback_new_preroll (GstAppSink *sink, gpointer p)
// if got a valid sample
if (sample != NULL) {
// get buffer from sample
GstBuffer *buf = gst_sample_get_buffer (sample);
// send frames to media player only if ready
MediaPlayer *m = (MediaPlayer *)p;
MediaPlayer *m = static_cast<MediaPlayer *>(p);
if (m && m->ready_) {
// get buffer from sample
GstBuffer *buf = gst_sample_get_buffer (sample);
// fill frame from buffer
if ( !m->fill_frame(buf, MediaPlayer::PREROLL) )
ret = GST_FLOW_ERROR;
@@ -1089,12 +1166,13 @@ GstFlowReturn MediaPlayer::callback_new_sample (GstAppSink *sink, gpointer p)
// if got a valid sample
if (sample != NULL && !gst_app_sink_is_eos (sink)) {
// get buffer from sample (valid until sample is released)
GstBuffer *buf = gst_sample_get_buffer (sample) ;
// send frames to media player only if ready
MediaPlayer *m = (MediaPlayer *)p;
MediaPlayer *m = static_cast<MediaPlayer *>(p);
if (m && m->ready_) {
// get buffer from sample (valid until sample is released)
GstBuffer *buf = gst_sample_get_buffer (sample) ;
// fill frame with buffer
if ( !m->fill_frame(buf, MediaPlayer::SAMPLE) )
ret = GST_FLOW_ERROR;

View File

@@ -18,7 +18,7 @@ class Visitor;
#define MAX_PLAY_SPEED 20.0
#define MIN_PLAY_SPEED 0.1
#define N_VFRAME 3
#define N_VFRAME 5
struct MediaInfo {
@@ -90,6 +90,7 @@ public:
* Open a media using gstreamer URI
* */
void open( std::string path);
void reopen();
/**
* Get name of the media
* */
@@ -204,7 +205,7 @@ public:
* - frame duration : timeline.step()
*/
Timeline *timeline();
void setTimeline(Timeline tl);
void setTimeline(const Timeline &tl);
float currentTimelineFading();
/**
@@ -238,6 +239,17 @@ public:
* Must be called in OpenGL context
* */
guint texture() const;
/**
* Get the name of the hardware decoder used
* Empty string if none (i.e. software decoding)
* */
std::string hardwareDecoderName();
/**
* Forces open using software decoding
* (i.e. without hadrware decoding)
* */
void setSoftwareDecodingForced(bool on);
bool softwareDecodingForced();
/**
* Accept visitors
* Used for saving session file
@@ -274,6 +286,8 @@ private:
std::atomic<bool> failed_;
bool seeking_;
bool enabled_;
bool force_software_decoding_;
std::string hardware_decoder_;
// fps counter
struct TimeCounter {
@@ -311,6 +325,7 @@ private:
status = INVALID;
position = GST_CLOCK_TIME_NONE;
}
void unmap();
};
Frame frame_[N_VFRAME];
guint write_index_;

View File

@@ -31,11 +31,8 @@ typedef struct prop {
std::string name;
bool is_float;
bool is_list;
prop(std::string n, bool t, bool l = false){
name = n;
is_float = t;
is_list = l;
}
prop(const std::string &n, bool t, bool l = false) :
name(n), is_float(t), is_list(l) { }
} plyProperty;
typedef std::map<std::string, std::vector<plyProperty> > plyElementProperties;
@@ -45,9 +42,9 @@ template <typename T>
T parseValue(std::istream& istream) {
T v;
char space = ' ';
istream >> v;
if (!istream.eof()) {
char space = ' ';
istream >> space >> std::ws;
}

1
Mesh.h
View File

@@ -18,6 +18,7 @@ public:
Mesh(const std::string& ply_path, const std::string& tex_path = "");
void setTexture(uint textureindex);
inline uint texture() const { return textureindex_; }
void init () override;
void draw (glm::mat4 modelview, glm::mat4 projection) override;

View File

@@ -12,7 +12,6 @@
#include <tinyxml2.h>
#include "tinyxml2Toolkit.h"
using namespace tinyxml2;
#include "defines.h"
#include "Settings.h"
@@ -431,19 +430,13 @@ bool Mixer::recreateSource(Source *s)
// get the xml description from this source, and exit if not wellformed
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLError eResult = xmlDoc.Parse(Source::xml(s).c_str());
if ( XMLResultError(eResult))
return false;
tinyxml2::XMLElement *root = xmlDoc.FirstChildElement(APP_NAME);
if ( root == nullptr )
return false;
XMLElement* sourceNode = root->FirstChildElement("Source");
tinyxml2::XMLElement* sourceNode = SessionLoader::firstSourceElement(SessionVisitor::getClipboard(s), xmlDoc);
if ( sourceNode == nullptr )
return false;
// actually create the source with SessionLoader using xml description
SessionLoader loader( session_ );
Source *replacement = loader.createSource(sourceNode, false); // not clone
Source *replacement = loader.createSource(sourceNode, SessionLoader::DUPLICATE); // not clone
if (replacement == nullptr)
return false;
@@ -736,7 +729,7 @@ SourceList Mixer::findSources (float depth_from, float depth_to)
SourceList found;
SourceList dsl = session_->getDepthSortedList();
SourceList::iterator it = dsl.begin();
for (; it != dsl.end(); it++) {
for (; it != dsl.end(); ++it) {
if ( (*it)->depth() > depth_to )
break;
if ( (*it)->depth() >= depth_from )
@@ -772,12 +765,25 @@ void Mixer::setCurrentIndex(int index)
setCurrentSource( session_->at(index) );
}
void Mixer::moveIndex (int current_index, int target_index)
{
// remember ptr to current source
Source *previous_current_source_ = currentSource();
// change order
session_->move(current_index, target_index);
// restore current
unsetCurrentSource();
setCurrentSource(previous_current_source_);
}
void Mixer::setCurrentNext()
{
if (session_->numSource() > 0) {
SourceList::iterator it = current_source_;
it++;
++it;
if (it == session_->end()) {
it = session_->begin();
@@ -797,7 +803,7 @@ void Mixer::setCurrentPrevious()
it = session_->end();
}
it--;
--it;
setCurrentSource( it );
}
}
@@ -831,11 +837,10 @@ int Mixer::indexCurrentSource()
Source *Mixer::currentSource()
{
if ( current_source_ == session_->end() )
if ( current_source_ != session_->end() )
return (*current_source_);
else
return nullptr;
else {
return *(current_source_);
}
}
// management of view
@@ -1051,7 +1056,7 @@ void Mixer::merge(SessionSource *source)
float next_depth = to_be_moved.front()->depth();
if ( next_depth < target_depth + need_depth) {
SourceList::iterator it = to_be_moved.begin();
for (; it != to_be_moved.end(); it++) {
for (; it != to_be_moved.end(); ++it) {
float scale_depth = (MAX_DEPTH-(*it)->depth()) / (MAX_DEPTH-next_depth);
(*it)->setDepth( (*it)->depth() + scale_depth );
}
@@ -1216,40 +1221,23 @@ void Mixer::set(Session *s)
sessionSwapRequested_ = true;
}
bool Mixer::isClipboard (const std::string& text)
{
if (text.size() > 6 && text.substr(0, 6) == "<vimix")
return true;
return false;
}
void Mixer::paste(const std::string& clipboard)
{
if (clipboard.empty())
return;
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLError eResult = xmlDoc.Parse(clipboard.c_str());
if ( XMLResultError(eResult))
return;
tinyxml2::XMLElement* sourceNode = SessionLoader::firstSourceElement(clipboard, xmlDoc);
if (sourceNode) {
tinyxml2::XMLElement *root = xmlDoc.FirstChildElement(APP_NAME);
if ( root == nullptr )
return;
SessionLoader loader( session_ );
SessionLoader loader( session_ );
XMLElement* sourceNode = root->FirstChildElement("Source");
for( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement())
{
Source *s = loader.createSource(sourceNode);
if (s) {
// Add source to Session
session_->addSource(s);
// Add source to Mixer
addSource(s);
for( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement())
{
Source *s = loader.createSource(sourceNode);
if (s) {
// Add source to Session
session_->addSource(s);
// Add source to Mixer
addSource(s);
}
}
}
}

View File

@@ -73,6 +73,7 @@ public:
void unsetCurrentSource ();
void setCurrentIndex (int index);
void moveIndex (int current_index, int target_index);
int indexCurrentSource ();
// browsing into sources
@@ -106,7 +107,6 @@ public:
void open (const std::string& filename);
// create sources if clipboard contains well-formed xml text
bool isClipboard (const std::string& text);
void paste (const std::string& clipboard);
protected:

View File

@@ -32,12 +32,12 @@ uint textureMixingQuadratic();
MixingView::MixingView() : View(MIXING), limbo_scale_(MIXING_LIMBO_SCALE)
{
scene.root()->scale_ = glm::vec3(MIXING_DEFAULT_SCALE, MIXING_DEFAULT_SCALE, 1.0f);
scene.root()->translation_ = glm::vec3(0.0f, 0.0f, 0.0f);
// read default settings
if ( Settings::application.views[mode_].name.empty() ) {
// no settings found: store application default
Settings::application.views[mode_].name = "Mixing";
scene.root()->scale_ = glm::vec3(MIXING_DEFAULT_SCALE, MIXING_DEFAULT_SCALE, 1.0f);
scene.root()->translation_ = glm::vec3(0.0f, 0.0f, 0.0f);
saveSettings();
}
else
@@ -50,7 +50,6 @@ MixingView::MixingView() : View(MIXING), limbo_scale_(MIXING_LIMBO_SCALE)
scene.bg()->attach(tmp);
mixingCircle_ = new Mesh("mesh/disk.ply");
mixingCircle_->setTexture(textureMixingQuadratic());
mixingCircle_->shader()->color = glm::vec4( 1.f, 1.f, 1.f, 1.f );
scene.bg()->attach(mixingCircle_);
@@ -107,12 +106,15 @@ MixingView::MixingView() : View(MIXING), limbo_scale_(MIXING_LIMBO_SCALE)
stashCircle_->color = glm::vec4( COLOR_STASH_CIRCLE, 0.6f );
// scene.bg()->attach(stashCircle_);
}
void MixingView::draw()
{
// set texture
if (mixingCircle_->texture() == 0)
mixingCircle_->setTexture(textureMixingQuadratic());
// temporarily force shaders to use opacity blending for rendering icons
Shader::force_blending_opacity = true;
// draw scene of this view
@@ -160,13 +162,13 @@ void MixingView::draw()
// manipulation of sources in Mixing view
if (ImGui::Selectable( ICON_FA_CROSSHAIRS " Center" )){
glm::vec2 center = glm::vec2(0.f, 0.f);
for (SourceList::iterator it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
for (SourceList::iterator it = Mixer::selection().begin(); it != Mixer::selection().end(); ++it) {
// compute barycenter (1)
center += glm::vec2((*it)->group(View::MIXING)->translation_);
}
// compute barycenter (2)
center /= Mixer::selection().size();
for (SourceList::iterator it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
for (SourceList::iterator it = Mixer::selection().begin(); it != Mixer::selection().end(); ++it) {
(*it)->group(View::MIXING)->translation_ -= glm::vec3(center, 0.f);
(*it)->touch();
}
@@ -175,7 +177,7 @@ void MixingView::draw()
if (ImGui::Selectable( ICON_FA_HAYKAL " Distribute" )){
SourceList list;
glm::vec2 center = glm::vec2(0.f, 0.f);
for (SourceList::iterator it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
for (SourceList::iterator it = Mixer::selection().begin(); it != Mixer::selection().end(); ++it) {
list.push_back(*it);
// compute barycenter (1)
center += glm::vec2((*it)->group(View::MIXING)->translation_);
@@ -186,13 +188,13 @@ void MixingView::draw()
list = mixing_sorted( list, center);
// average distance
float d = 0.f;
for (SourceList::iterator it = list.begin(); it != list.end(); it++) {
for (SourceList::iterator it = list.begin(); it != list.end(); ++it) {
d += glm::distance(glm::vec2((*it)->group(View::MIXING)->translation_), center);
}
d /= list.size();
// distribute with equal angle
float angle = 0.f;
for (SourceList::iterator it = list.begin(); it != list.end(); it++) {
for (SourceList::iterator it = list.begin(); it != list.end(); ++it) {
glm::vec2 P = center + glm::rotate(glm::vec2(0.f, d), angle);
(*it)->group(View::MIXING)->translation_.x = P.x;
(*it)->group(View::MIXING)->translation_.y = P.y;
@@ -203,14 +205,14 @@ void MixingView::draw()
}
if (ImGui::Selectable( ICON_FA_CLOUD_SUN " Expand & hide" )){
SourceList::iterator it = Mixer::selection().begin();
for (; it != Mixer::selection().end(); it++) {
for (; it != Mixer::selection().end(); ++it) {
(*it)->setAlpha(0.f);
}
Action::manager().store(std::string("Selection Mixing Expand & hide."));
}
if (ImGui::Selectable( ICON_FA_SUN " Compress & show" )){
SourceList::iterator it = Mixer::selection().begin();
for (; it != Mixer::selection().end(); it++) {
for (; it != Mixer::selection().end(); ++it) {
(*it)->setAlpha(0.99f);
}
Action::manager().store(std::string("Selection Mixing Compress & show."));
@@ -242,16 +244,22 @@ int MixingView::size ()
void MixingView::centerSource(Source *s)
{
// setup view so that the center of the source ends at screen coordinates (650, 150)
// -> this is just next to the navigation pannel
glm::vec2 screenpoint = glm::vec2(500.f, 20.f) * Rendering::manager().mainWindow().dpiScale();
glm::vec3 pos_to = Rendering::manager().unProject(screenpoint, scene.root()->transform_);
glm::vec3 pos_from( - s->group(View::MIXING)->scale_.x, s->group(View::MIXING)->scale_.y, 0.f);
pos_from += s->group(View::MIXING)->translation_;
glm::vec4 pos_delta = glm::vec4(pos_to.x, pos_to.y, 0.f, 0.f) - glm::vec4(pos_from.x, pos_from.y, 0.f, 0.f);
pos_delta = scene.root()->transform_ * pos_delta;
scene.root()->translation_ += glm::vec3(pos_delta);
// calculate screen area visible in the default view
GlmToolkit::AxisAlignedBoundingBox view_box;
glm::mat4 modelview = GlmToolkit::transform(scene.root()->translation_, scene.root()->rotation_, scene.root()->scale_);
view_box.extend( Rendering::manager().unProject(glm::vec2(0.f, Rendering::manager().mainWindow().height()), modelview) );
view_box.extend( Rendering::manager().unProject(glm::vec2(Rendering::manager().mainWindow().width(), 0.f), modelview) );
// check if upper-left corner of source is in view box
glm::vec3 pos_source = s->group(mode_)->translation_ + glm::vec3( -s->group(mode_)->scale_.x, s->group(mode_)->scale_.y, 0.f);
if ( !view_box.contains(pos_source)) {
// not visible so shift view
glm::vec2 screenpoint = glm::vec2(500.f, 20.f) * Rendering::manager().mainWindow().dpiScale();
glm::vec3 pos_to = Rendering::manager().unProject(screenpoint, scene.root()->transform_);
glm::vec4 pos_delta = glm::vec4(pos_to.x, pos_to.y, 0.f, 0.f) - glm::vec4(pos_source.x, pos_source.y, 0.f, 0.f);
pos_delta = scene.root()->transform_ * pos_delta;
scene.root()->translation_ += glm::vec3(pos_delta);
}
}
void MixingView::update(float dt)
@@ -278,6 +286,10 @@ void MixingView::update(float dt)
f = 1.f - f;
mixingCircle_->shader()->color = glm::vec4(f, f, f, 1.f);
// prevent invalid scaling
float s = CLAMP(scene.root()->scale_.x, MIXING_MIN_SCALE, MIXING_MAX_SCALE);
scene.root()->scale_.x = s;
scene.root()->scale_.y = s;
}
// the current view is the mixing view
@@ -358,7 +370,10 @@ std::pair<Node *, glm::vec2> MixingView::pick(glm::vec2 P)
}
// pick on the mixing group rotation icon
else if ( pick.first == s->rotation_mixingroup_ ) {
s->mixinggroup_->setAction( MixingGroup::ACTION_ROTATE_ALL );
if (UserInterface::manager().shiftModifier())
s->mixinggroup_->setAction( MixingGroup::ACTION_GRAB_ONE );
else
s->mixinggroup_->setAction( MixingGroup::ACTION_ROTATE_ALL );
}
// pick source of a mixing group
else if ( s->mixinggroup_ != nullptr ) {
@@ -429,6 +444,7 @@ View::Cursor MixingView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pai
// inform mixing groups to follow the current source
if (Source::isCurrent(s) && s->mixinggroup_->action() > MixingGroup::ACTION_UPDATE) {
s->mixinggroup_->follow(s);
// special cursor for rotation
if (s->mixinggroup_->action() == MixingGroup::ACTION_ROTATE_ALL)
ret.type = Cursor_Hand;
}
@@ -576,7 +592,7 @@ void MixingView::setAlpha(Source *s)
Group *sourceNode = s->group(mode_);
glm::vec2 mix_pos = glm::vec2(DEFAULT_MIXING_TRANSLATION);
for(NodeSet::iterator it = scene.ws()->begin(); it != scene.ws()->end(); it++) {
for(NodeSet::iterator it = scene.ws()->begin(); it != scene.ws()->end(); ++it) {
// avoid superposing icons: distribute equally
if ( glm::distance(glm::vec2((*it)->translation_), mix_pos) < DELTA_ALPHA) {
mix_pos += glm::vec2(-0.03f, 0.03f);
@@ -625,36 +641,29 @@ uint textureMixingQuadratic()
if (texid == 0) {
// generate the texture with alpha exactly as computed for sources
GLubyte matrix[CIRCLE_PIXELS*CIRCLE_PIXELS * 4];
GLubyte color[4] = {0,0,0,0};
GLfloat luminance = 1.f;
GLfloat alpha = 0.f;
GLfloat distance = 0.f;
int l = -CIRCLE_PIXELS / 2 + 1, c = 0;
int l = -CIRCLE_PIXELS / 2 + 1;
for (int i = 0; i < CIRCLE_PIXELS / 2; ++i) {
c = -CIRCLE_PIXELS / 2 + 1;
for (int j=0; j < CIRCLE_PIXELS / 2; ++j) {
for (int i = 0; i < CIRCLE_PIXELS ; ++i) {
int c = -CIRCLE_PIXELS / 2 + 1;
for (int j = 0; j < CIRCLE_PIXELS ; ++j) {
// distance to the center
distance = sin_quad_texture( (float) c , (float) l );
GLfloat distance = sin_quad_texture( (float) c , (float) l );
// distance = 1.f - (GLfloat) ((c * c) + (l * l)) / CIRCLE_PIXEL_RADIUS; // quadratic
// distance = 1.f - (GLfloat) sqrt( (GLfloat) ((c * c) + (l * l))) / (GLfloat) sqrt(CIRCLE_PIXEL_RADIUS); // linear
// transparency
alpha = 255.f * CLAMP( distance , 0.f, 1.f);
color[3] = static_cast<GLubyte>(alpha);
GLfloat alpha = 255.f * CLAMP( distance , 0.f, 1.f);
GLubyte A = static_cast<GLubyte>(alpha);
// luminance adjustment
luminance = 255.f * CLAMP( 0.2f + 0.75f * distance, 0.f, 1.f);
color[0] = color[1] = color[2] = static_cast<GLubyte>(luminance);
GLfloat luminance = 255.f * CLAMP( 0.2f + 0.75f * distance, 0.f, 1.f);
GLubyte L = static_cast<GLubyte>(luminance);
// 1st quadrant
memmove(&matrix[ j * 4 + i * CIRCLE_PIXELS * 4 ], color, 4 * sizeof(GLubyte));
// 4nd quadrant
memmove(&matrix[ (CIRCLE_PIXELS -j -1)* 4 + i * CIRCLE_PIXELS * 4 ], color, 4 * sizeof(GLubyte));
// 3rd quadrant
memmove(&matrix[ j * 4 + (CIRCLE_PIXELS -i -1) * CIRCLE_PIXELS * 4 ], color, 4 * sizeof(GLubyte));
// 4th quadrant
memmove(&matrix[ (CIRCLE_PIXELS -j -1) * 4 + (CIRCLE_PIXELS -i -1) * CIRCLE_PIXELS * 4 ], color, 4 * sizeof(GLubyte));
// fill pixel RGBA
matrix[ i * CIRCLE_PIXELS * 4 + j * 4 + 0 ] = L;
matrix[ i * CIRCLE_PIXELS * 4 + j * 4 + 1 ] = L;
matrix[ i * CIRCLE_PIXELS * 4 + j * 4 + 2 ] = L;
matrix[ i * CIRCLE_PIXELS * 4 + j * 4 + 3 ] = A;
++c;
}

View File

@@ -67,10 +67,10 @@ void StreamerResponseListener::ProcessMessage( const osc::ReceivedMessage& m,
}
NetworkStream::NetworkStream(): Stream(), receiver_(nullptr)
NetworkStream::NetworkStream(): Stream(),
receiver_(nullptr), received_config_(false), connected_(false)
{
received_config_ = false;
connected_ = false;
}
glm::ivec2 NetworkStream::resolution() const
@@ -271,7 +271,7 @@ void NetworkStream::update()
NetworkSource::NetworkSource() : StreamSource()
{
// create stream
stream_ = (Stream *) new NetworkStream;
stream_ = static_cast<Stream *>( new NetworkStream );
// set symbol
symbol_ = new Symbol(Symbol::SHARE, glm::vec3(0.75f, 0.75f, 0.01f));

View File

@@ -95,10 +95,10 @@ std::vector<unsigned long> iplongs_;
void add_interface(int fd, const char *name) {
struct ifreq ifreq;
char host[128];
memset(&ifreq, 0, sizeof ifreq);
strncpy(ifreq.ifr_name, name, IFNAMSIZ);
if(ioctl(fd, SIOCGIFADDR, &ifreq)==0) {
char host[128];
int family;
switch(family=ifreq.ifr_addr.sa_family) {
case AF_INET:
@@ -122,14 +122,14 @@ void add_interface(int fd, const char *name) {
void list_interfaces()
{
struct ifreq *ifreq;
struct ifconf ifconf;
char buf[16384];
int fd=socket(PF_INET, SOCK_DGRAM, 0);
if(fd > -1) {
struct ifconf ifconf;
ifconf.ifc_len=sizeof buf;
ifconf.ifc_buf=buf;
if(ioctl(fd, SIOCGIFCONF, &ifconf)==0) {
struct ifreq *ifreq;
ifreq=ifconf.ifc_req;
for(int i=0;i<ifconf.ifc_len;) {
size_t len;

View File

@@ -134,7 +134,7 @@ void Pattern::open( uint pattern, glm::ivec2 res )
PatternSource::PatternSource() : StreamSource()
{
// create stream
stream_ = (Stream *) new Pattern;
stream_ = static_cast<Stream *>( new Pattern );
// set symbol
symbol_ = new Symbol(Symbol::PATTERN, glm::vec3(0.75f, 0.75f, 0.01f));

View File

@@ -11,15 +11,15 @@
#include <glm/gtx/vector_angle.hpp>
PickingVisitor::PickingVisitor(glm::vec3 coordinates, bool force) : Visitor(), force_(force)
PickingVisitor::PickingVisitor(glm::vec3 coordinates, bool force) : Visitor(),
force_(force), modelview_(glm::mat4(1.f))
{
modelview_ = glm::mat4(1.f);
points_.push_back( coordinates );
}
PickingVisitor::PickingVisitor(glm::vec3 selectionstart, glm::vec3 selection_end, bool force) : Visitor(), force_(force)
PickingVisitor::PickingVisitor(glm::vec3 selectionstart, glm::vec3 selection_end, bool force) : Visitor(),
force_(force), modelview_(glm::mat4(1.f))
{
modelview_ = glm::mat4(1.f);
points_.push_back( selectionstart );
points_.push_back( selection_end );
}
@@ -36,7 +36,7 @@ void PickingVisitor::visit(Group &n)
return;
glm::mat4 mv = modelview_;
for (NodeSet::iterator node = n.begin(); node != n.end(); node++) {
for (NodeSet::iterator node = n.begin(); node != n.end(); ++node) {
if ( (*node)->visible_ || force_)
(*node)->accept(*this);
modelview_ = mv;

View File

@@ -121,9 +121,8 @@ void ImageSurface::accept(Visitor& v)
v.visit(*this);
}
MediaSurface::MediaSurface(const std::string& p, Shader *s) : Surface(s)
MediaSurface::MediaSurface(const std::string& p, Shader *s) : Surface(s), path_(p)
{
path_ = p;
mediaplayer_ = new MediaPlayer;
}
@@ -419,9 +418,9 @@ void LineSquare::setColor(glm::vec4 c)
}
LineStrip::LineStrip(std::vector<glm::vec2> path, float linewidth) : Primitive(new Shader), arrayBuffer_(0)
LineStrip::LineStrip(const std::vector<glm::vec2> &path, float linewidth) : Primitive(new Shader),
arrayBuffer_(0), path_(path)
{
path_ = path;
linewidth_ = 0.002f * linewidth;
for(size_t i = 1; i < path_.size(); ++i)
@@ -574,7 +573,7 @@ void LineStrip::accept(Visitor& v)
}
LineLoop::LineLoop(std::vector<glm::vec2> path, float linewidth) : LineStrip(path, linewidth)
LineLoop::LineLoop(const std::vector<glm::vec2> &path, float linewidth) : LineStrip(path, linewidth)
{
// close linestrip loop
glm::vec3 begin = glm::vec3(path_[path_.size()-1], 0.f);

View File

@@ -180,7 +180,7 @@ public:
class LineStrip : public Primitive {
public:
LineStrip(std::vector<glm::vec2> path, float linewidth = 1.f);
LineStrip(const std::vector<glm::vec2> &path, float linewidth = 1.f);
virtual ~LineStrip();
virtual void init () override;
@@ -207,7 +207,7 @@ protected:
class LineLoop : public LineStrip {
public:
LineLoop(std::vector<glm::vec2> path, float linewidth = 1.f);
LineLoop(const std::vector<glm::vec2> &path, float linewidth = 1.f);
protected:
void updatePath() override;

View File

@@ -14,11 +14,15 @@ vimix is the successor for GLMixer - https://sourceforge.net/projects/glmixer/
# Install
Check the [Quick Installation Guide](https://github.com/brunoherbelin/vimix/wiki/Quick-Installation-Guide)
### Linux
Download and install a release package from https://snapcraft.io/vimix
snap install vimix
NB: You'll need to setup the snap permissions.
### Mac OSX
@@ -30,7 +34,11 @@ NB: You'll need to accept the exception in OSX security preference.
git clone --recursive https://github.com/brunoherbelin/vimix.git
This will create the directory 'vimix', download the latest version of vimix code,
and (recursively) clone all the internal git Dependencies.
and (recursively) clone all the internal git dependencies.
**To only update a cloned git copy:**
git pull
## Compile
@@ -46,20 +54,34 @@ and (recursively) clone all the internal git Dependencies.
- gcc
- make
- cmake
- git
**Libraries:**
- gstreamer
- gst-plugins : base, good, bad & ugly
- libpng
- libglfw3
- libicu
#### Install Dependencies
**Ubuntu**
apt-get install build-essential cmake libpng-dev libglfw3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libicu-dev
apt-get install build-essential cmake libpng-dev libglfw3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libicu-dev libgtk-3-dev
**OSX with Brew**
brew install cmake libpng glfw gstreamer gst-libav gst-plugins-bad gst-plugins-base gst-plugins-good gst-plugins-ugly icu4c
#### Generate snap
From vimix root directory
snapcraft
snap install --dangerous vimix_0.5_amd64.snap
### Memcheck
G_SLICE=always-malloc valgrind --tool=massif ./vimix
G_SLICE=always-malloc valgrind --leak-check=full --log-file=vimix_mem.txt ./vimix

View File

@@ -95,10 +95,9 @@ static void WindowEscapeFullscreen( GLFWwindow *w, int key, int, int action, int
static void WindowToggleFullscreen( GLFWwindow *w, int button, int action, int)
{
static double seconds = 0.f;
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
{
static double seconds = 0.f;
// detect double clic
if ( glfwGetTime() - seconds < 0.2f ) {
// toggle fullscreen
@@ -161,10 +160,13 @@ bool Rendering::init()
std::list<std::string> gpuplugins = GstToolkit::enable_gpu_decoding_plugins(Settings::application.render.gpu_decoding);
if (Settings::application.render.gpu_decoding) {
if (gpuplugins.size() > 0) {
Log::Info("Video decoding favoring the following GPU decoding plugin(s):");
Log::Info("Fond the following GPU decoding plugin(s):");
for(auto it = gpuplugins.begin(); it != gpuplugins.end(); it++)
Log::Info(" - %s", (*it).c_str());
}
else {
Log::Info("No GPU decoding plugin found.");
}
}
//#if GST_GL_HAVE_PLATFORM_WGL
@@ -239,7 +241,7 @@ void Rendering::draw()
// Custom draw
std::list<Rendering::RenderingCallback>::iterator iter;
for (iter=draw_callbacks_.begin(); iter != draw_callbacks_.end(); iter++)
for (iter=draw_callbacks_.begin(); iter != draw_callbacks_.end(); ++iter)
{
(*iter)();
}
@@ -272,14 +274,16 @@ void Rendering::draw()
main_.toggleFullscreen_();
output_.toggleFullscreen_();
#ifndef USE_GST_APPSINK_CALLBACKS
// no g_main_loop_run(loop) : update global GMainContext
g_main_context_iteration(NULL, FALSE);
#endif
// software framerate limiter 60FPS if not v-sync
if ( Settings::application.render.vsync < 1 ) {
static GTimer *timer = g_timer_new ();
double elapsed = g_timer_elapsed (timer, NULL) * 1000000.0;
if (elapsed < 16000)
if ( (elapsed < 16000.0) && (elapsed > 0.0) )
g_usleep( 16000 - (gulong)elapsed );
g_timer_start(timer);
}

View File

@@ -10,11 +10,8 @@
// Desktop OpenGL function loader
#include <glad/glad.h>
// multiplatform message box
#include <tinyfiledialogs.h>
// standalone image loader
#include "stb_image.h"
#include <stb_image.h>
// CMake Ressource Compiler
#include <cmrc/cmrc.hpp>
@@ -102,7 +99,7 @@ const char *Resource::getData(const std::string& path, size_t* out_file_size){
cmrc::file::iterator it = file.begin();
data = static_cast<const char *>(it);
}
catch (std::system_error e) {
catch (const std::system_error &e) {
Log::Error("Could not access ressource %s", std::string(path).c_str());
}
@@ -118,7 +115,7 @@ std::string Resource::getText(const std::string& path){
file = fs.open(path.c_str());
file_stream << std::string(file.begin(), file.end()) << std::endl;
}
catch (std::system_error e) {
catch (const std::system_error &e) {
Log::Error("Could not access ressource %s", std::string(path).c_str());
}
@@ -209,11 +206,11 @@ uint Resource::getTextureDDS(const std::string& path, float *aspect_ratio)
// load the mipmaps
for (uint level = 0; level < mipMapCount && (width || height); ++level)
{
uint size = ((width+3)/4)*((height+3)/4)*blockSize;
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, size, buffer + offset);
uint s = ((width+3)/4)*((height+3)/4)*blockSize;
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, s, buffer + offset);
glGenerateMipmap(GL_TEXTURE_2D);
offset += size;
offset += s;
width /= 2;
height /= 2;

View File

@@ -18,6 +18,9 @@
#include "Scene.h"
#define DEBUG_SCENE 0
static int num_nodes_ = 0;
// Node
Node::Node() : initialized_(false), visible_(true), refcount_(0)
{
@@ -29,11 +32,17 @@ Node::Node() : initialized_(false), visible_(true), refcount_(0)
rotation_ = glm::vec3(0.f);
translation_ = glm::vec3(0.f);
crop_ = glm::vec3(1.f);
#if DEBUG_SCENE
num_nodes_++;
#endif
}
Node::~Node ()
{
clearCallbacks();
#if DEBUG_SCENE
num_nodes_--;
#endif
}
void Node::clearCallbacks()
@@ -73,7 +82,7 @@ void Node::update( float dt)
delete callback;
}
else {
iter++;
++iter;
}
}
@@ -274,7 +283,7 @@ void Group::update( float dt )
// update every child node
for (NodeSet::iterator node = children_.begin();
node != children_.end(); node++) {
node != children_.end(); ++node) {
(*node)->update ( dt );
}
}
@@ -291,7 +300,7 @@ void Group::draw(glm::mat4 modelview, glm::mat4 projection)
// draw every child node
for (NodeSet::iterator node = children_.begin();
node != children_.end(); node++) {
node != children_.end(); ++node) {
(*node)->draw ( ctm, projection );
}
}
@@ -385,13 +394,13 @@ void Switch::accept(Visitor& v)
void Switch::setActive (uint index)
{
active_ = CLAMP(index, 0, children_.size() - 1);
active_ = MINI(index, children_.size() - 1);
}
Node *Switch::child(uint index) const
{
if (!children_.empty()) {
uint i = CLAMP(index, 0, children_.size() - 1);
uint i = MINI(index, children_.size() - 1);
return children_.at(i);
}
return nullptr;
@@ -448,6 +457,9 @@ Scene::~Scene()
clear();
// bg and fg are deleted as children of root
delete root_;
#if DEBUG_SCENE
Log::Info("Total scene nodes %d", num_nodes_);
#endif
}

View File

@@ -25,7 +25,7 @@ void SearchVisitor::visit(Group &n)
if (found_)
return;
for (NodeSet::iterator node = n.begin(); node != n.end(); node++) {
for (NodeSet::iterator node = n.begin(); node != n.end(); ++node) {
(*node)->accept(*this);
if (found_)
break;
@@ -59,7 +59,7 @@ void SearchFileVisitor::visit(Node &n)
void SearchFileVisitor::visit(Group &n)
{
for (NodeSet::iterator node = n.begin(); node != n.end(); node++) {
for (NodeSet::iterator node = n.begin(); node != n.end(); ++node) {
(*node)->accept(*this);
}
}

View File

@@ -1,7 +1,5 @@
#include <algorithm>
#include <tinyxml2.h>
#include "defines.h"
#include "SessionVisitor.h"
#include "Source.h"
@@ -159,41 +157,9 @@ SourceList::iterator Selection::end()
return selection_.end();
}
std::string Selection::xml() const
std::string Selection::clipboard() const
{
std::string x = "";
if (!selection_.empty()) {
// create xml doc and root node
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
selectionNode->SetAttribute("size", (int) selection_.size());
xmlDoc.InsertEndChild(selectionNode);
// fill doc
SourceList selection_clones_;
SessionVisitor sv(&xmlDoc, selectionNode);
for (auto iter = selection_.begin(); iter != selection_.end(); iter++, sv.setRoot(selectionNode) ){
// start with clones
CloneSource *clone = dynamic_cast<CloneSource *>(*iter);
if (clone)
(*iter)->accept(sv);
else
selection_clones_.push_back(*iter);
}
// add others in front
for (auto iter = selection_clones_.begin(); iter != selection_clones_.end(); iter++, sv.setRoot(selectionNode) ){
(*iter)->accept(sv);
}
// get compact string
tinyxml2::XMLPrinter xmlPrint(0, true);
xmlDoc.Print( &xmlPrint );
x = xmlPrint.CStr();
}
return x;
return SessionVisitor::getClipboard(selection_);
}
SourceList Selection::getCopy() const

View File

@@ -33,7 +33,7 @@ public:
uint size () const;
// extract
std::string xml() const;
std::string clipboard() const;
SourceList getCopy() const;
protected:

View File

@@ -11,10 +11,8 @@
#include "Log.h"
Session::Session() : failedSource_(nullptr), active_(true), fading_target_(0.f)
Session::Session() : failedSource_(nullptr), active_(true), fading_target_(0.f), filename_("")
{
filename_ = "";
config_[View::RENDERING] = new Group;
config_[View::RENDERING]->scale_ = glm::vec3(0.f);
@@ -77,7 +75,7 @@ void Session::update(float dt)
// pre-render of all sources
failedSource_ = nullptr;
for( SourceList::iterator it = sources_.begin(); it != sources_.end(); it++){
for( SourceList::iterator it = sources_.begin(); it != sources_.end(); ++it){
// ensure the RenderSource is rendering this session
RenderSource *s = dynamic_cast<RenderSource *>( *it );
@@ -125,13 +123,11 @@ void Session::update(float dt)
SourceList::iterator Session::addSource(Source *s)
{
SourceList::iterator its = sources_.end();
// lock before change
access_.lock();
// find the source
its = find(s);
SourceList::iterator its = find(s);
// ok, its NOT in the list !
if (its == sources_.end()) {
@@ -297,7 +293,7 @@ SourceList::iterator Session::at(int index)
SourceList::iterator it = sources_.begin();
while ( i < index && it != sources_.end() ){
i++;
it++;
++it;
}
return it;
}
@@ -325,7 +321,7 @@ void Session::move(int current_index, int target_index)
SourceList::iterator from = at(current_index);
SourceList::iterator to = at(target_index);
if ( target_index > current_index )
to++;
++to;
Source *s = (*from);
sources_.erase(from);

View File

@@ -16,6 +16,7 @@
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "MediaPlayer.h"
#include "SystemToolkit.h"
#include <tinyxml2.h>
#include "tinyxml2Toolkit.h"
@@ -26,27 +27,30 @@ std::string SessionCreator::info(const std::string& filename)
{
std::string ret = "";
XMLDocument doc;
XMLError eResult = doc.LoadFile(filename.c_str());
if ( XMLResultError(eResult)) {
Log::Warning("%s could not be openned.", filename.c_str());
return ret;
}
// if the file exists
if (SystemToolkit::file_exists(filename)) {
// try to load the file
XMLDocument doc;
XMLError eResult = doc.LoadFile(filename.c_str());
// silently ignore on error
if ( !XMLResultError(eResult, false)) {
XMLElement *header = doc.FirstChildElement(APP_NAME);
if (header != nullptr && header->Attribute("date") != 0) {
int s = header->IntAttribute("size");
ret = std::to_string( s ) + " source" + ( s > 1 ? "s\n" : "\n");
const char *att_string = header->Attribute("resolution");
if (att_string)
ret += std::string( att_string ) + "\n";
att_string = header->Attribute("date");
if (att_string) {
std::string date( att_string );
ret += date.substr(6,2) + "/" + date.substr(4,2) + "/" + date.substr(0,4) + " @ ";
ret += date.substr(8,2) + ":" + date.substr(10,2);
XMLElement *header = doc.FirstChildElement(APP_NAME);
if (header != nullptr && header->Attribute("date") != 0) {
int s = header->IntAttribute("size");
ret = std::to_string( s ) + " source" + ( s > 1 ? "s\n" : "\n");
const char *att_string = header->Attribute("resolution");
if (att_string)
ret += std::string( att_string ) + "\n";
att_string = header->Attribute("date");
if (att_string) {
std::string date( att_string );
ret += date.substr(6,2) + "/" + date.substr(4,2) + "/" + date.substr(0,4) + " @ ";
ret += date.substr(8,2) + ":" + date.substr(10,2);
}
}
}
}
return ret;
@@ -113,10 +117,18 @@ void SessionCreator::loadConfig(XMLElement *viewsNode)
}
}
SessionLoader::SessionLoader(): Visitor(),
session_(nullptr), xmlCurrent_(nullptr), recursion_(0)
{
// impose C locale
setlocale(LC_ALL, "C");
}
SessionLoader::SessionLoader(Session *session, int recursion): Visitor(),
session_(session), xmlCurrent_(nullptr), recursion_(recursion)
{
// impose C locale
setlocale(LC_ALL, "C");
}
@@ -264,12 +276,10 @@ void SessionLoader::load(XMLElement *sessionNode)
}
}
}
}
}
Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, bool clone_duplicates)
Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, Mode mode)
{
xmlCurrent_ = sourceNode;
@@ -279,13 +289,13 @@ Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, bool clone
SourceList::iterator sit = session_->end();
// check if a source with the given id exists in the session
if (clone_duplicates) {
if (mode == CLONE) {
uint64_t id__ = 0;
xmlCurrent_->QueryUnsigned64Attribute("id", &id__);
sit = session_->find(id__);
}
// no source with this id exists
// no source with this id exists or Mode DUPLICATE
if ( sit == session_->end() ) {
// create a new source depending on type
const char *pType = xmlCurrent_->Attribute("type");
@@ -342,6 +352,99 @@ Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, bool clone
}
bool SessionLoader::isClipboard(std::string clipboard)
{
if (clipboard.size() > 6 && clipboard.substr(0, 6) == "<" APP_NAME )
return true;
return false;
}
tinyxml2::XMLElement* SessionLoader::firstSourceElement(std::string clipboard, XMLDocument &xmlDoc)
{
tinyxml2::XMLElement* sourceNode = nullptr;
if ( !isClipboard(clipboard) )
return sourceNode;
// header
tinyxml2::XMLError eResult = xmlDoc.Parse(clipboard.c_str());
if ( XMLResultError(eResult))
return sourceNode;
tinyxml2::XMLElement *root = xmlDoc.FirstChildElement(APP_NAME);
if ( root == nullptr )
return sourceNode;
// find node
sourceNode = root->FirstChildElement("Source");
return sourceNode;
}
void SessionLoader::applyImageProcessing(const Source &s, std::string clipboard)
{
if ( !isClipboard(clipboard) )
return;
// header
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLError eResult = xmlDoc.Parse(clipboard.c_str());
if ( XMLResultError(eResult))
return;
tinyxml2::XMLElement *root = xmlDoc.FirstChildElement(APP_NAME);
if ( root == nullptr )
return;
// find node
tinyxml2::XMLElement* imgprocNode = nullptr;
tinyxml2::XMLElement* sourceNode = root->FirstChildElement("Source");
if (sourceNode == nullptr)
imgprocNode = root->FirstChildElement("ImageProcessing");
else
imgprocNode = sourceNode->FirstChildElement("ImageProcessing");
if (imgprocNode == nullptr)
return;
// create session visitor and browse
SessionLoader loader;
loader.xmlCurrent_ = imgprocNode;
s.processingShader()->accept(loader);
}
//void SessionLoader::applyMask(const Source &s, std::string clipboard)
//{
// if ( !isClipboard(clipboard) )
// return;
// // header
// tinyxml2::XMLDocument xmlDoc;
// tinyxml2::XMLError eResult = xmlDoc.Parse(clipboard.c_str());
// if ( XMLResultError(eResult))
// return;
// tinyxml2::XMLElement *root = xmlDoc.FirstChildElement(APP_NAME);
// if ( root == nullptr )
// return;
// // find node
// tinyxml2::XMLElement* naskNode = nullptr;
// tinyxml2::XMLElement* sourceNode = root->FirstChildElement("Source");
// if (sourceNode == nullptr)
// naskNode = root->FirstChildElement("Mask");
// else
// naskNode = sourceNode->FirstChildElement("ImageProcessing");
// if (naskNode == nullptr)
// return;
// // create session visitor and browse
// SessionLoader loader;
// loader.xmlCurrent_ = naskNode;
//// s.processingShader()->accept(loader);
//}
void SessionLoader::XMLToNode(tinyxml2::XMLElement *xml, Node &n)
{
if (xml != nullptr){
@@ -413,6 +516,10 @@ void SessionLoader::visit(MediaPlayer &n)
mediaplayerNode->QueryIntAttribute("loop", &loop);
n.setLoop( (MediaPlayer::LoopMode) loop);
bool gpudisable = false;
mediaplayerNode->QueryBoolAttribute("software_decoding", &gpudisable);
n.setSoftwareDecodingForced(gpudisable);
bool play = true;
mediaplayerNode->QueryBoolAttribute("play", &play);
n.play(play);
@@ -564,10 +671,10 @@ void SessionLoader::visit (Source& s)
xmlCurrent_ = sourceNode->FirstChildElement("MixingGroup");
if (xmlCurrent_) {
SourceIdList idlist;
XMLElement* sourceNode = xmlCurrent_->FirstChildElement("source");
for ( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement()) {
XMLElement* mixingSourceNode = xmlCurrent_->FirstChildElement("source");
for ( ; mixingSourceNode ; mixingSourceNode = mixingSourceNode->NextSiblingElement()) {
uint64_t id__ = 0;
sourceNode->QueryUnsigned64Attribute("id", &id__);
mixingSourceNode->QueryUnsigned64Attribute("id", &id__);
idlist.push_back(id__);
}
groups_sources_id_.push_back(idlist);
@@ -664,5 +771,27 @@ void SessionLoader::visit (NetworkSource& s)
s.setConnection(connect);
}
// dirty hack wich can be useful ?
//class DummySource : public Source
//{
// friend class SessionLoader;
//public:
// uint texture() const override { return 0; }
// bool failed() const override { return true; }
// void accept (Visitor& v) override { Source::accept(v); }
//protected:
// DummySource() : Source() {}
// void init() override {}
//};
//Source *SessionLoader::createDummy(tinyxml2::XMLElement *sourceNode)
//{
// SessionLoader loader;
// loader.xmlCurrent_ = sourceNode;
// DummySource *dum = new DummySource;
// dum->accept(loader);
// return dum;
//}

View File

@@ -12,6 +12,8 @@ class Session;
class SessionLoader : public Visitor {
SessionLoader();
public:
SessionLoader(Session *session, int recursion = 0);
@@ -21,7 +23,16 @@ public:
std::map< uint64_t, Source* > getSources() const;
std::list< SourceList > getMixingGroups() const;
Source *createSource(tinyxml2::XMLElement *sourceNode, bool clone_duplicates = true);
typedef enum {
CLONE,
DUPLICATE
} Mode;
Source *createSource(tinyxml2::XMLElement *sourceNode, Mode mode = CLONE);
static bool isClipboard(std::string clipboard);
static tinyxml2::XMLElement* firstSourceElement(std::string clipboard, tinyxml2::XMLDocument &xmlDoc);
static void applyImageProcessing(const Source &s, std::string clipboard);
//TODO static void applyMask(const Source &s, std::string clipboard);
// Elements of Scene
void visit (Node& n) override;

View File

@@ -17,9 +17,8 @@
#include "Mixer.h"
SessionSource::SessionSource() : Source()
SessionSource::SessionSource() : Source(), failed_(false)
{
failed_ = false;
session_ = new Session;
}
@@ -174,7 +173,7 @@ void SessionFileSource::init()
// check that every source is ready..
bool ready = true;
for (SourceList::iterator iter = session_->begin(); iter != session_->end(); iter++)
for (SourceList::iterator iter = session_->begin(); iter != session_->end(); ++iter)
{
// interrupt if any source is NOT ready
if ( !(*iter)->ready() ){

View File

@@ -18,6 +18,7 @@
#include "SystemToolkit.h"
#include <iostream>
#include <locale>
#include <tinyxml2.h>
using namespace tinyxml2;
@@ -25,6 +26,9 @@ using namespace tinyxml2;
bool SessionVisitor::saveSession(const std::string& filename, Session *session)
{
// impose C locale
setlocale(LC_ALL, "C");
// creation of XML doc
XMLDocument xmlDoc;
@@ -77,6 +81,9 @@ SessionVisitor::SessionVisitor(tinyxml2::XMLDocument *doc,
tinyxml2::XMLElement *root,
bool recursive) : Visitor(), recursive_(recursive), xmlCurrent_(root)
{
// impose C locale
setlocale(LC_ALL, "C");
if (doc == nullptr)
xmlDoc_ = new XMLDocument;
else
@@ -127,7 +134,7 @@ void SessionVisitor::visit(Group &n)
if (recursive_) {
// loop over members of a group
XMLElement *group = xmlCurrent_;
for (NodeSet::iterator node = n.begin(); node != n.end(); node++) {
for (NodeSet::iterator node = n.begin(); node != n.end(); ++node) {
(*node)->accept(*this);
// revert to group as current
xmlCurrent_ = group;
@@ -210,6 +217,7 @@ void SessionVisitor::visit(MediaPlayer &n)
newelement->SetAttribute("play", n.isPlaying());
newelement->SetAttribute("loop", (int) n.loop());
newelement->SetAttribute("speed", n.playSpeed());
newelement->SetAttribute("software_decoding", n.softwareDecodingForced());
// timeline
XMLElement *timelineelement = xmlDoc_->NewElement("Timeline");
@@ -532,3 +540,90 @@ void SessionVisitor::visit (MixingGroup& g)
xmlCurrent_->InsertEndChild(sour);
}
}
std::string SessionVisitor::getClipboard(SourceList list)
{
std::string x = "";
if (!list.empty()) {
// create xml doc and root node
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
selectionNode->SetAttribute("size", (int) list.size());
xmlDoc.InsertEndChild(selectionNode);
// fill doc by visiting sources
SourceList selection_clones_;
SessionVisitor sv(&xmlDoc, selectionNode);
for (auto iter = list.begin(); iter != list.end(); iter++, sv.setRoot(selectionNode) ){
// start with clones
CloneSource *clone = dynamic_cast<CloneSource *>(*iter);
if (clone)
(*iter)->accept(sv);
else
selection_clones_.push_back(*iter);
}
// add others in front
for (auto iter = selection_clones_.begin(); iter != selection_clones_.end(); iter++, sv.setRoot(selectionNode) ){
(*iter)->accept(sv);
}
// get compact string
tinyxml2::XMLPrinter xmlPrint(0, true);
xmlDoc.Print( &xmlPrint );
x = xmlPrint.CStr();
}
return x;
}
std::string SessionVisitor::getClipboard(Source *s)
{
std::string x = "";
if (s != nullptr) {
// create xml doc and root node
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
selectionNode->SetAttribute("size", 1);
xmlDoc.InsertEndChild(selectionNode);
// visit source
SessionVisitor sv(&xmlDoc, selectionNode);
s->accept(sv);
// get compact string
tinyxml2::XMLPrinter xmlPrint(0, true);
xmlDoc.Print( &xmlPrint );
x = xmlPrint.CStr();
}
return x;
}
std::string SessionVisitor::getClipboard(ImageProcessingShader *s)
{
std::string x = "";
if (s != nullptr) {
// create xml doc and root node
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
xmlDoc.InsertEndChild(selectionNode);
tinyxml2::XMLElement *imgprocNode = xmlDoc.NewElement( "ImageProcessing" );
selectionNode->InsertEndChild(imgprocNode);
// visit source
SessionVisitor sv(&xmlDoc, imgprocNode);
s->accept(sv);
// get compact string
tinyxml2::XMLPrinter xmlPrint(0, true);
xmlDoc.Print( &xmlPrint );
x = xmlPrint.CStr();
}
return x;
}

View File

@@ -3,6 +3,7 @@
#include "Visitor.h"
#include "tinyxml2Toolkit.h"
#include "SourceList.h"
class Session;
@@ -21,6 +22,10 @@ public:
static bool saveSession(const std::string& filename, Session *session);
static std::string getClipboard(SourceList list);
static std::string getClipboard(Source *s);
static std::string getClipboard(ImageProcessingShader *s);
// Elements of Scene
void visit(Scene& n) override;
void visit(Node& n) override;

View File

@@ -1,5 +1,6 @@
#include <algorithm>
#include <iostream>
#include <locale>
using namespace std;
#include <tinyxml2.h>
@@ -17,6 +18,9 @@ static string settingsFilename = "";
void Settings::Save()
{
// impose C locale for all app
setlocale(LC_ALL, "C");
XMLDocument xmlDoc;
XMLDeclaration *pDec = xmlDoc.NewDeclaration();
xmlDoc.InsertFirstChild(pDec);
@@ -98,7 +102,6 @@ void Settings::Save()
// Transition
XMLElement *TransitionNode = xmlDoc.NewElement( "Transition" );
TransitionNode->SetAttribute("auto_open", application.transition.auto_open);
TransitionNode->SetAttribute("hide_windows", application.transition.hide_windows);
TransitionNode->SetAttribute("cross_fade", application.transition.cross_fade);
TransitionNode->SetAttribute("duration", application.transition.duration);
@@ -141,19 +144,19 @@ void Settings::Save()
viewsNode->SetAttribute("workspace", application.current_workspace);
map<int, Settings::ViewConfig>::iterator iter;
for (iter=application.views.begin(); iter != application.views.end(); iter++)
for (iter=application.views.begin(); iter != application.views.end(); ++iter)
{
const Settings::ViewConfig& v = iter->second;
const Settings::ViewConfig& view_config = iter->second;
XMLElement *view = xmlDoc.NewElement( "View" );
view->SetAttribute("name", v.name.c_str());
view->SetAttribute("name", view_config.name.c_str());
view->SetAttribute("id", iter->first);
XMLElement *scale = xmlDoc.NewElement("default_scale");
scale->InsertEndChild( XMLElementFromGLM(&xmlDoc, v.default_scale) );
scale->InsertEndChild( XMLElementFromGLM(&xmlDoc, view_config.default_scale) );
view->InsertEndChild(scale);
XMLElement *translation = xmlDoc.NewElement("default_translation");
translation->InsertEndChild( XMLElementFromGLM(&xmlDoc, v.default_translation) );
translation->InsertEndChild( XMLElementFromGLM(&xmlDoc, view_config.default_translation) );
view->InsertEndChild(translation);
viewsNode->InsertEndChild(view);
@@ -216,6 +219,9 @@ void Settings::Save()
void Settings::Load()
{
// impose C locale for all app
setlocale(LC_ALL, "C");
XMLDocument xmlDoc;
if (settingsFilename.empty())
settingsFilename = SystemToolkit::full_filename(SystemToolkit::settings_path(), APP_SETTINGS);
@@ -305,7 +311,6 @@ void Settings::Load()
XMLElement * transitionnode = pRoot->FirstChildElement("Transition");
if (transitionnode != nullptr) {
transitionnode->QueryBoolAttribute("hide_windows", &application.transition.hide_windows);
transitionnode->QueryBoolAttribute("auto_open", &application.transition.auto_open);
transitionnode->QueryBoolAttribute("cross_fade", &application.transition.cross_fade);
transitionnode->QueryFloatAttribute("duration", &application.transition.duration);
transitionnode->QueryIntAttribute("profile", &application.transition.profile);
@@ -343,10 +348,10 @@ void Settings::Load()
// bloc views
{
application.views.clear(); // trash existing list
XMLElement * pElement = pRoot->FirstChildElement("Views");
if (pElement)
{
application.views.clear(); // trash existing list
pElement->QueryIntAttribute("current", &application.current_view);
pElement->QueryIntAttribute("workspace", &application.current_workspace);

View File

@@ -84,12 +84,14 @@ struct History
bool front_is_valid;
bool load_at_start;
bool save_on_exit;
bool changed;
History() {
path = IMGUI_LABEL_RECENT_FILES;
front_is_valid = false;
load_at_start = false;
save_on_exit = false;
changed = false;
}
void push(const std::string &filename) {
if (filename.empty()) {
@@ -101,6 +103,7 @@ struct History
if (filenames.size() > MAX_RECENT_HISTORY)
filenames.pop_back();
front_is_valid = true;
changed = true;
}
void remove(const std::string &filename) {
if (filename.empty())
@@ -108,20 +111,19 @@ struct History
if (filenames.front() == filename)
front_is_valid = false;
filenames.remove(filename);
changed = true;
}
};
struct TransitionConfig
{
bool cross_fade;
bool auto_open;
bool hide_windows;
float duration;
int profile;
TransitionConfig() {
cross_fade = true;
auto_open = true;
hide_windows = true;
duration = 1.f;
profile = 0;

View File

@@ -52,10 +52,9 @@ GLenum blending_destination_function[9] = {GL_ONE_MINUS_SRC_ALPHA,// normal
GL_ONE, // lighten only
GL_ZERO};
ShadingProgram::ShadingProgram(const std::string& vertex_file, const std::string& fragment_file) : vertex_id_(0), fragment_id_(0), id_(0)
ShadingProgram::ShadingProgram(const std::string& vertex_file, const std::string& fragment_file) :
vertex_id_(0), fragment_id_(0), id_(0), vertex_file_(vertex_file), fragment_file_(fragment_file)
{
vertex_file_ = vertex_file;
fragment_file_ = fragment_file;
}
void ShadingProgram::init()
@@ -189,10 +188,10 @@ void ShadingProgram::checkCompileErr()
void ShadingProgram::checkLinkingErr()
{
int success;
char infoLog[1024];
int success;
glGetProgramiv(id_, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[1024];
glGetProgramInfoLog(id_, 1024, NULL, infoLog);
Log::Warning("Error linking ShadingProgram:\n%s", infoLog);
}
@@ -262,6 +261,7 @@ void Shader::reset()
modelview = glm::identity<glm::mat4>();
iTransform = glm::identity<glm::mat4>();
color = glm::vec4(1.f, 1.f, 1.f, 1.f);
blending = BLEND_OPACITY;
}

View File

@@ -209,7 +209,7 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
// filtered image shader (with texturing and processing) for rendering
processingshader_ = new ImageProcessingShader;
// default rendering with image processing enabled
renderingshader_ = (Shader *) processingshader_;
renderingshader_ = static_cast<Shader *>(processingshader_);
// for drawing in mixing view
mixingshader_ = new ImageShader;
@@ -247,6 +247,8 @@ Source::~Source()
delete maskbuffer_;
if (maskimage_)
delete maskimage_;
if (masksurface_)
delete masksurface_; // deletes maskshader_
// all groups and their children are deleted in the scene
// this includes rendersurface_, overlays, blendingshader_ and rendershader_
@@ -303,7 +305,7 @@ void Source::setMode(Source::Mode m)
// show overlay if current
bool current = m >= Source::CURRENT;
for (auto o = overlays_.begin(); o != overlays_.end(); o++)
(*o).second->visible_ = current & !locked_;
(*o).second->visible_ = (current && !locked_);
// the lock icon
locker_->setActive( locked_ ? 0 : 1);
@@ -329,7 +331,7 @@ void Source::setImageProcessingEnabled (bool on)
if (on) {
// set the current rendering shader to be the
// (previously prepared) processing shader
renderingshader_ = (Shader *) processingshader_;
renderingshader_ = static_cast<Shader *>(processingshader_);
}
else {
// clone the current Image processing shader
@@ -341,7 +343,7 @@ void Source::setImageProcessingEnabled (bool on)
// and keep it for later
processingshader_ = tmp;
// set the current rendering shader to a simple one
renderingshader_ = (Shader *) new ImageShader;
renderingshader_ = static_cast<Shader *>(new ImageShader);
}
// apply to nodes in subclasses
@@ -430,7 +432,7 @@ void Source::attach(FrameBuffer *renderbuffer)
for (int v = View::MIXING; v < View::INVALID; v++) {
NodeSet::iterator node;
for (node = groups_[(View::Mode) v]->begin();
node != groups_[(View::Mode) v]->end(); node++) {
node != groups_[(View::Mode) v]->end(); ++node) {
(*node)->scale_.x = renderbuffer_->aspectRatio();
}
}
@@ -713,28 +715,6 @@ void Source::setMask(FrameBufferImage *img)
mask_need_update_ = false;
}
std::string Source::xml(Source *s)
{
std::string x = "";
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
selectionNode->SetAttribute("size", 1);
xmlDoc.InsertEndChild(selectionNode);
SessionVisitor sv(&xmlDoc, selectionNode);
s->accept(sv);
// get compact string
tinyxml2::XMLPrinter xmlPrint(0, true);
xmlDoc.Print( &xmlPrint );
x = xmlPrint.CStr();
return x;
}
bool Source::hasNode::operator()(const Source* elem) const
{
if (_n && elem)

View File

@@ -198,9 +198,6 @@ public:
// class-dependent icon
virtual glm::ivec2 icon () const { return glm::ivec2(12, 11); }
// get the xml description text of a source
static std::string xml(Source *s);
protected:
// name
std::string name_;
@@ -273,7 +270,6 @@ protected:
};
class CloneSource : public Source
{
friend class Source;

View File

@@ -12,7 +12,7 @@ bool compare_depth (Source * first, Source * second)
return ( first->depth() < second->depth() );
}
SourceList depth_sorted(SourceList list)
SourceList depth_sorted(const SourceList &list)
{
SourceList sl = list;
sl.sort(compare_depth);
@@ -23,7 +23,7 @@ SourceList depth_sorted(SourceList list)
// utility to sort Sources in MixingView in a clockwise order
// in reference to a center point
struct clockwise_centered {
clockwise_centered(glm::vec2 c) : center(c) { }
explicit clockwise_centered(glm::vec2 c) : center(c) { }
bool operator() (Source * first, Source * second) {
glm::vec2 pos_first = glm::vec2(first->group(View::MIXING)->translation_)-center;
float angle_first = glm::orientedAngle( glm::normalize(pos_first), glm::vec2(1.f, 0.f) );
@@ -34,7 +34,7 @@ struct clockwise_centered {
glm::vec2 center;
};
SourceList mixing_sorted(SourceList list, glm::vec2 center)
SourceList mixing_sorted(const SourceList &list, glm::vec2 center)
{
SourceList sl = list;
sl.sort(clockwise_centered(center));
@@ -43,7 +43,7 @@ SourceList mixing_sorted(SourceList list, glm::vec2 center)
}
SourceIdList ids (SourceList list)
SourceIdList ids (const SourceList &list)
{
SourceIdList idlist;
@@ -57,10 +57,10 @@ SourceIdList ids (SourceList list)
}
SourceListCompare compare (SourceList first, SourceList second)
SourceListCompare compare (const SourceList &first, const SourceList &second)
{
SourceListCompare ret = SOURCELIST_DISTINCT;
if (first.empty() || first.empty())
if (first.empty() || second.empty())
return ret;
// a new test list: start with the second list and remove all commons with first list
@@ -93,7 +93,7 @@ SourceListCompare compare (SourceList first, SourceList second)
}
SourceList intersect (SourceList first, SourceList second)
SourceList intersect (const SourceList &first, const SourceList &second)
{
// take second list and remove all elements also in first list
// -> builds the list of what remains in second list
@@ -109,7 +109,7 @@ SourceList intersect (SourceList first, SourceList second)
}
SourceList join (SourceList first, SourceList second)
SourceList join (const SourceList &first, const SourceList &second)
{
SourceList l = second;
for (auto it = first.begin(); it != first.end(); it++)

View File

@@ -8,10 +8,10 @@ class Source;
typedef std::list<Source *> SourceList;
SourceList depth_sorted (SourceList list);
SourceList mixing_sorted (SourceList list, glm::vec2 center = glm::vec2(0.f, 0.f));
SourceList intersect (SourceList first, SourceList second);
SourceList join (SourceList first, SourceList second);
SourceList depth_sorted (const SourceList &list);
SourceList mixing_sorted (const SourceList &list, glm::vec2 center = glm::vec2(0.f, 0.f));
SourceList intersect (const SourceList &first, const SourceList &second);
SourceList join (const SourceList &first, const SourceList &second);
typedef enum {
SOURCELIST_DISTINCT = 0,
@@ -20,9 +20,9 @@ typedef enum {
SOURCELIST_FIRST_IN_SECOND = 3,
SOURCELIST_SECOND_IN_FIRST = 4
} SourceListCompare;
SourceListCompare compare (SourceList first, SourceList second);
SourceListCompare compare (const SourceList &first, const SourceList &second);
typedef std::list<uint64_t> SourceIdList;
SourceIdList ids (SourceList list);
SourceIdList ids (const SourceList &list);
#endif // SOURCELIST_H

View File

@@ -20,8 +20,6 @@ using namespace std;
#define STREAM_DEBUG
#endif
#define USE_GST_APPSINK_CALLBACKS_
Stream::Stream()
{
@@ -137,7 +135,7 @@ void Stream::execute_open()
gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 30);
gst_app_sink_set_drop (GST_APP_SINK(sink), true);
#ifdef USE_GST_APPSINK_CALLBACKS_
#ifdef USE_GST_APPSINK_CALLBACKS
// set the callbacks
GstAppSinkCallbacks callbacks;
if (single_frame_) {
@@ -197,6 +195,14 @@ bool Stream::failed() const
return failed_;
}
void Stream::Frame::unmap()
{
if ( full ) {
gst_video_frame_unmap(&vframe);
full = false;
}
}
void Stream::close()
{
// not openned?
@@ -210,9 +216,14 @@ void Stream::close()
// clean up GST
if (pipeline_ != nullptr) {
// force flush
GstState state;
gst_element_send_event(pipeline_, gst_event_new_seek (1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_END, 0) );
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_NULL);
if (ret == GST_STATE_CHANGE_ASYNC) {
GstState state;
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
}
gst_object_unref (pipeline_);
@@ -222,10 +233,9 @@ void Stream::close()
// cleanup eventual remaining frame memory
for(guint i = 0; i < N_FRAME; i++){
if ( frame_[i].full ) {
gst_video_frame_unmap(&frame_[i].vframe);
frame_[i].status = INVALID;
}
frame_[i].access.lock();
frame_[i].unmap();
frame_[i].access.unlock();
}
write_index_ = 0;
last_index_ = 0;
@@ -512,6 +522,9 @@ void Stream::update()
// double update for pre-roll frame and dual PBO (ensure frame is displayed now)
if (frame_[read_index].status == PREROLL && pbo_size_ > 0)
fill_texture(read_index);
// free frame
frame_[read_index].unmap();
}
// avoid reading it again
@@ -547,11 +560,7 @@ bool Stream::fill_frame(GstBuffer *buf, FrameStatus status)
frame_[write_index_].access.lock();
// always empty frame before filling it again
if ( frame_[write_index_].full ) {
if ( GST_MINI_OBJECT_REFCOUNT_VALUE( &frame_[write_index_].vframe.buffer->mini_object ) > 0)
gst_video_frame_unmap(&frame_[write_index_].vframe);
frame_[write_index_].full = false;
}
frame_[write_index_].unmap();
// accept status of frame received
frame_[write_index_].status = status;
@@ -581,7 +590,9 @@ bool Stream::fill_frame(GstBuffer *buf, FrameStatus status)
// full but invalid frame : will be deleted next iteration
// (should never happen)
else {
#ifdef STREAM_DEBUG
Log::Info("Stream %s Received an Invalid frame", std::to_string(id_).c_str());
#endif
frame_[write_index_].status = INVALID;
frame_[write_index_].access.unlock();
return false;
@@ -615,7 +626,7 @@ bool Stream::fill_frame(GstBuffer *buf, FrameStatus status)
void Stream::callback_end_of_stream (GstAppSink *, gpointer p)
{
Stream *m = (Stream *)p;
Stream *m = static_cast<Stream *>(p);
if (m && m->ready_) {
m->fill_frame(NULL, Stream::EOS);
}
@@ -631,7 +642,7 @@ GstFlowReturn Stream::callback_new_preroll (GstAppSink *sink, gpointer p)
// if got a valid sample
if (sample != NULL) {
// send frames to media player only if ready
Stream *m = (Stream *)p;
Stream *m = static_cast<Stream *>(p);
if (m && m->ready_) {
// get buffer from sample
@@ -665,7 +676,7 @@ GstFlowReturn Stream::callback_new_sample (GstAppSink *sink, gpointer p)
if (sample != NULL && !gst_app_sink_is_eos (sink)) {
// send frames to media player only if ready
Stream *m = (Stream *)p;
Stream *m = static_cast<Stream *>(p);
if (m && m->ready_) {
// get buffer from sample (valid until sample is released)

View File

@@ -169,6 +169,7 @@ protected:
status = INVALID;
position = GST_CLOCK_TIME_NONE;
}
void unmap();
};
Frame frame_[N_FRAME];
guint write_index_;

View File

@@ -100,7 +100,7 @@ bool Streaming::busy()
streamers_lock_.lock();
std::vector<VideoStreamer *>::const_iterator sit = streamers_.begin();
for (; sit != streamers_.end() && !b; sit++)
for (; sit != streamers_.end() && !b; ++sit)
b = (*sit)->busy() ;
streamers_lock_.unlock();
@@ -114,7 +114,7 @@ std::vector<std::string> Streaming::listStreams()
streamers_lock_.lock();
std::vector<VideoStreamer *>::const_iterator sit = streamers_.begin();
for (; sit != streamers_.end(); sit++)
for (; sit != streamers_.end(); ++sit)
ls.push_back( (*sit)->info() );
streamers_lock_.unlock();
@@ -148,7 +148,7 @@ void Streaming::removeStream(const std::string &sender, int port)
// parse the list for a streamers matching IP and port
streamers_lock_.lock();
std::vector<VideoStreamer *>::const_iterator sit = streamers_.begin();
for (; sit != streamers_.end(); sit++){
for (; sit != streamers_.end(); ++sit){
NetworkToolkit::StreamConfig config = (*sit)->config_;
if (config.client_address.compare(sender_ip) == 0 && config.port == port ) {
#ifdef STREAMER_DEBUG
@@ -182,11 +182,33 @@ void Streaming::removeStreams(const std::string &clientname)
sit = streamers_.erase(sit);
}
else
sit++;
++sit;
}
streamers_lock_.unlock();
}
void Streaming::removeStream(const VideoStreamer *vs)
{
if ( vs!= nullptr && streamers_lock_.try_lock()) {
std::vector<VideoStreamer *>::const_iterator sit = streamers_.begin();
while ( sit != streamers_.end() ){
if ( *(sit) == vs) {
#ifdef STREAMER_DEBUG
NetworkToolkit::StreamConfig config = vs->config_;
Log::Info("Ending streaming to %s:%d", config.client_address.c_str(), config.port);
#endif
// remove from list
streamers_.erase(sit);
break;
}
else
++sit;
}
streamers_lock_.unlock();
}
}
void Streaming::refuseStream(const std::string &sender, int reply_to)
{
// get ip of client
@@ -263,7 +285,7 @@ void Streaming::addStream(const std::string &sender, int reply_to, const std::st
}
VideoStreamer::VideoStreamer(NetworkToolkit::StreamConfig conf): FrameGrabber(), config_(conf)
VideoStreamer::VideoStreamer(const NetworkToolkit::StreamConfig &conf): FrameGrabber(), config_(conf), stopped_(false)
{
}
@@ -386,6 +408,10 @@ void VideoStreamer::stop ()
// stop recording
FrameGrabber::stop ();
// inform streaming manager to remove myself
// NB: will not be effective if called inside a locked streamers_lock_
Streaming::manager().removeStream(this);
// force finished
finished_ = true;
}

View File

@@ -46,6 +46,7 @@ public:
inline bool enabled() const { return enabled_; }
void removeStreams(const std::string &clientname);
void removeStream(const std::string &sender, int port);
void removeStream(const VideoStreamer *vs);
bool busy();
std::vector<std::string> listStreams();
@@ -74,10 +75,12 @@ class VideoStreamer : public FrameGrabber
// connection information
NetworkToolkit::StreamConfig config_;
std::atomic<bool> stopped_;
public:
VideoStreamer(NetworkToolkit::StreamConfig conf);
VideoStreamer(const NetworkToolkit::StreamConfig &conf);
virtual ~VideoStreamer() {}
std::string info() const override;
};

View File

@@ -55,11 +55,11 @@ long SystemToolkit::memory_usage()
FILE *file = fopen("/proc/self/statm", "r");
if (file) {
unsigned long m = 0;
int ret = 0;
int ret = 0, ret2 = 0;
ret = fscanf (file, "%lu", &m); // virtual mem program size,
ret = fscanf (file, "%lu", &m); // resident set size,
ret2 = fscanf (file, "%lu", &m); // resident set size,
fclose (file);
if (ret>0)
if (ret>0 && ret2>0)
size = (size_t)m * getpagesize();
}
return (long)size;
@@ -102,7 +102,7 @@ string SystemToolkit::byte_to_string(long b)
while(numbytes >= 1024.0 && i != list.end())
{
i++;
++i;
numbytes /= 1024.0;
}
oss << std::fixed << std::setprecision(2) << numbytes << *i;
@@ -119,7 +119,7 @@ string SystemToolkit::bits_to_string(long b)
while(numbytes >= 1000.0 && i != list.end())
{
i++;
++i;
numbytes /= 1000.0;
}
oss << std::fixed << std::setprecision(2) << numbytes << *i;
@@ -189,18 +189,17 @@ string SystemToolkit::extension_filename(const string& filename)
std::string SystemToolkit::home_path()
{
// 1. find home
char *mHomePath;
string homePath;
// try the system user info
// NB: avoids depending on changes of the $HOME env. variable
struct passwd* pwd = getpwuid(getuid());
if (pwd)
mHomePath = pwd->pw_dir;
homePath = std::string(pwd->pw_dir);
else
// try the $HOME environment variable
mHomePath = getenv("HOME");
homePath = std::string(getenv("HOME"));
return string(mHomePath) + PATH_SEP;
return homePath + PATH_SEP;
}
@@ -216,17 +215,16 @@ std::string SystemToolkit::cwd_path()
std::string SystemToolkit::username()
{
// 1. find home
char *user;
string userName;
// try the system user info
struct passwd* pwd = getpwuid(getuid());
if (pwd)
user = pwd->pw_name;
userName = std::string(pwd->pw_name);
else
// try the $USER environment variable
user = getenv("USER");
userName = std::string(getenv("USER"));
return string(user);
return userName;
}
bool SystemToolkit::create_directory(const string& path)
@@ -307,7 +305,7 @@ bool SystemToolkit::file_exists(const string& path)
return access(path.c_str(), R_OK) == 0;
// TODO : WIN32 implementation
// TODO : WIN32 implementation (see tinyfd)
}
@@ -330,18 +328,18 @@ list<string> SystemToolkit::list_directory(const string& path, const string& fil
list<string> ls;
DIR *dir;
struct dirent *ent;
if ((dir = opendir (path.c_str())) != NULL) {
// list all the files and directories within directory
while ((ent = readdir (dir)) != NULL) {
if ( ent->d_type == DT_REG)
{
string filename = string(ent->d_name);
if ( extension_filename(filename) == filter)
ls.push_back( full_filename(path, filename) );
}
}
closedir (dir);
// list all the files and directories within directory
struct dirent *ent;
while ((ent = readdir (dir)) != NULL) {
if ( ent->d_type == DT_REG)
{
string filename = string(ent->d_name);
if ( extension_filename(filename) == filter)
ls.push_back( full_filename(path, filename) );
}
}
closedir (dir);
}
return ls;
@@ -349,27 +347,29 @@ list<string> SystemToolkit::list_directory(const string& path, const string& fil
void SystemToolkit::open(const string& url)
{
int ignored __attribute__((unused));
#ifdef WIN32
ShellExecuteA( nullptr, nullptr, url.c_str(), nullptr, nullptr, 0 );
#elif defined APPLE
char buf[2048];
sprintf( buf, "open '%s'", url.c_str() );
system( buf );
ignored = system( buf );
#else
char buf[2048];
sprintf( buf, "xdg-open '%s'", url.c_str() );
int r = system( buf );
ignored = system( buf );
#endif
}
void SystemToolkit::execute(const string& command)
{
int ignored __attribute__((unused));
#ifdef WIN32
ShellExecuteA( nullptr, nullptr, url.c_str(), nullptr, nullptr, 0 );
#elif defined APPLE
int r = system( command.c_str() );
(void) system( command.c_str() );
#else
int r = system( command.c_str() );
ignored = system( command.c_str() );
#endif
}
// example :
@@ -388,12 +388,15 @@ std::string SystemToolkit::transliterate(std::string input)
icu::Transliterator *firstTrans = icu::Transliterator::createInstance(
"any-NFKD ; [:Nonspacing Mark:] Remove; NFKC; Latin", UTRANS_FORWARD, status);
firstTrans->transliterate(ucs);
delete firstTrans;
icu::Transliterator *secondTrans = icu::Transliterator::createInstance(
"any-NFKD ; [:Nonspacing Mark:] Remove; [@!#$*%~] Remove; NFKC", UTRANS_FORWARD, status);
secondTrans->transliterate(ucs);
delete secondTrans;
std::ostringstream output;
output << ucs;
return output.str();
}

View File

@@ -25,15 +25,16 @@
#include "TextureView.h"
#define MASK_PAINT_ACTION_LABEL "Mask Paint"
TextureView::TextureView() : View(TEXTURE), edit_source_(nullptr), need_edit_update_(true)
{
scene.root()->scale_ = glm::vec3(APPEARANCE_DEFAULT_SCALE, APPEARANCE_DEFAULT_SCALE, 1.0f);
scene.root()->translation_ = glm::vec3(0.8f, 0.f, 0.0f);
// read default settings
if ( Settings::application.views[mode_].name.empty() ) {
// no settings found: store application default
Settings::application.views[mode_].name = "Texture";
scene.root()->scale_ = glm::vec3(APPEARANCE_DEFAULT_SCALE, APPEARANCE_DEFAULT_SCALE, 1.0f);
scene.root()->translation_ = glm::vec3(0.8f, 0.f, 0.0f);
saveSettings();
}
else
@@ -258,7 +259,7 @@ void TextureView::select(glm::vec2 A, glm::vec2 B)
// create a list of source matching the list of picked nodes
SourceList selection;
// loop over the nodes and add all sources found.
for(std::vector< std::pair<Node *, glm::vec2> >::const_reverse_iterator p = pv.rbegin(); p != pv.rend(); p++){
for(std::vector< std::pair<Node *, glm::vec2> >::const_reverse_iterator p = pv.rbegin(); p != pv.rend(); ++p){
Source *s = Mixer::manager().findSource( p->first );
if (canSelect(s))
selection.push_back( s );
@@ -678,38 +679,37 @@ void TextureView::draw()
ImGui::EndPopup();
}
ImGui::SameLine(0, 60);
// store mask if changed, reset after applied
if (edit_source_->maskShader()->effect > 0)
edit_source_->storeMask();
edit_source_->maskShader()->effect = 0;
// menu for effects
ImGui::SameLine(0, 60);
if (ImGui::Button(ICON_FA_MAGIC ICON_FA_SORT_DOWN ))
ImGui::OpenPopup( "brush_menu_popup" );
if (ImGui::BeginPopup( "brush_menu_popup" ))
{
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT);
std::ostringstream oss;
oss << edit_source_->name();
int e = 0;
if (ImGui::Selectable( ICON_FA_BACKSPACE "\tClear")) {
edit_source_->maskShader()->effect = 1;
edit_source_->maskShader()->cursor = glm::vec4(100.0, 100.0, 0.f, 0.f);
edit_source_->touch();
// store action history
std::ostringstream oss;
oss << edit_source_->name() << ": Mask Paint Clear";
Action::manager().store(oss.str());
e = 1;
oss << ": Clear " << MASK_PAINT_ACTION_LABEL;
}
if (ImGui::Selectable( ICON_FA_ADJUST "\tInvert")) {
edit_source_->maskShader()->effect = 2;
edit_source_->maskShader()->cursor = glm::vec4(100.0, 100.0, 0.f, 0.f);
edit_source_->touch();
// store action history
std::ostringstream oss;
oss << edit_source_->name() << ": Mask Paint Invert";
Action::manager().store(oss.str());
e = 2;
oss << ": Invert " << MASK_PAINT_ACTION_LABEL;
}
if (ImGui::Selectable( ICON_FA_WAVE_SQUARE "\tEdge")) {
edit_source_->maskShader()->effect = 3;
e = 3;
oss << ": Edge " << MASK_PAINT_ACTION_LABEL;
}
if (e>0) {
edit_source_->maskShader()->effect = e;
edit_source_->maskShader()->cursor = glm::vec4(100.0, 100.0, 0.f, 0.f);
edit_source_->touch();
// store action history
std::ostringstream oss;
oss << edit_source_->name() << ": Mask Paint Edge";
Action::manager().store(oss.str());
}
ImGui::PopFont();
@@ -885,7 +885,6 @@ void TextureView::draw()
show_scale_ = false;
}
#define MASK_PAINT_ACTION_LABEL "Mask Paint Edit"
View::Cursor TextureView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick)
{
@@ -902,6 +901,8 @@ View::Cursor TextureView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pa
// work on the edited source
if ( edit_source_ != nullptr ) {
info << edit_source_->name() << ": ";
if ( pick.first == mask_cursor_circle_ ) {
// inform shader of a cursor action : coordinates and crop scaling
@@ -912,6 +913,8 @@ View::Cursor TextureView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pa
info << MASK_PAINT_ACTION_LABEL;
// cursor indication - no info, just cursor
ret.type = Cursor_Hand;
// store action in history
current_action_ = info.str();
}
else if ( pick.first == mask_cursor_crop_ ) {
@@ -947,11 +950,10 @@ View::Cursor TextureView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pa
info << " x " << edit_source_->maskShader()->size.y;
// cursor indication - no info, just cursor
ret.type = Cursor_Hand;
// store action in history
current_action_ = info.str();
}
// store action in history
current_action_ = edit_source_->name() + ": " + info.str();
}
return ret;
}
@@ -1191,9 +1193,9 @@ View::Cursor TextureView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pa
overlay_rotation_clock_->visible_ = false;
// rotation center to center of source (disregarding scale)
glm::mat4 T = glm::translate(glm::identity<glm::mat4>(), s->stored_status_->translation_);
source_from = glm::inverse(T) * glm::vec4( scene_from, 1.f );
source_to = glm::inverse(T) * glm::vec4( scene_to, 1.f );
glm::mat4 M = glm::translate(glm::identity<glm::mat4>(), s->stored_status_->translation_);
source_from = glm::inverse(M) * glm::vec4( scene_from, 1.f );
source_to = glm::inverse(M) * glm::vec4( scene_to, 1.f );
// compute rotation angle
float angle = glm::orientedAngle( glm::normalize(glm::vec2(source_from)), glm::normalize(glm::vec2(source_to)));
// apply rotation on Z axis

View File

@@ -13,7 +13,7 @@ struct includesTime: public std::unary_function<TimeInterval, bool>
return s.includes(_t);
}
includesTime(GstClockTime t) : _t(t) { }
explicit includesTime(GstClockTime t) : _t(t) { }
private:
GstClockTime _t;
@@ -26,6 +26,7 @@ Timeline::Timeline()
Timeline::~Timeline()
{
reset();
}
Timeline& Timeline::operator = (const Timeline& b)
@@ -142,7 +143,7 @@ bool Timeline::addGap(TimeInterval s)
return false;
}
void Timeline::setGaps(TimeIntervalSet g)
void Timeline::setGaps(const TimeIntervalSet &g)
{
gaps_array_need_update_ = true;
gaps_ = g;
@@ -176,8 +177,7 @@ TimeIntervalSet Timeline::sections() const
++it;
}
for (; it != gaps_.end(); ++it)
{
for (; it != gaps_.end(); ++it) {
sec.insert( TimeInterval(begin_sec, (*it).begin) );
begin_sec = (*it).end;
}
@@ -205,8 +205,8 @@ float Timeline::fadingAt(const GstClockTime t)
double true_index = (static_cast<double>(MAX_TIMELINE_ARRAY) * static_cast<double>(t)) / static_cast<double>(timing_.end);
double previous_index = floor(true_index);
float percent = static_cast<float>(true_index - previous_index);
size_t keyframe_index = CLAMP( static_cast<size_t>(previous_index), 0, MAX_TIMELINE_ARRAY-1);
size_t keyframe_next_index = CLAMP( keyframe_index+1, 0, MAX_TIMELINE_ARRAY-1);
size_t keyframe_index = MINI( static_cast<size_t>(previous_index), MAX_TIMELINE_ARRAY-1);
size_t keyframe_next_index = MINI( keyframe_index+1, MAX_TIMELINE_ARRAY-1);
float v = fadingArray_[keyframe_index];
v += percent * (fadingArray_[keyframe_next_index] - fadingArray_[keyframe_index]);

View File

@@ -111,7 +111,7 @@ public:
inline size_t numGaps() const { return gaps_.size(); }
float *gapsArray();
void clearGaps();
void setGaps(TimeIntervalSet g);
void setGaps(const TimeIntervalSet &g);
bool addGap(TimeInterval s);
bool addGap(GstClockTime begin, GstClockTime end);
bool removeGaptAt(GstClockTime t);

View File

@@ -22,6 +22,8 @@
#include "TransitionView.h"
#define POS_TARGET 0.4f
TransitionView::TransitionView() : View(TRANSITION), transition_source_(nullptr)
{
@@ -69,7 +71,7 @@ TransitionView::TransitionView() : View(TRANSITION), transition_source_(nullptr)
scene.bg()->attach(border);
scene.bg()->scale_ = glm::vec3(0.1f, 0.1f, 1.f);
scene.bg()->translation_ = glm::vec3(0.4f, 0.f, 0.0f);
scene.bg()->translation_ = glm::vec3(POS_TARGET, 0.f, 0.0f);
}
@@ -85,7 +87,7 @@ void TransitionView::update(float dt)
FrameBuffer *output = Mixer::manager().session()->frame();
if (output){
float aspect_ratio = output->aspectRatio();
for (NodeSet::iterator node = scene.bg()->begin(); node != scene.bg()->end(); node++) {
for (NodeSet::iterator node = scene.bg()->begin(); node != scene.bg()->end(); ++node) {
(*node)->scale_.x = aspect_ratio;
}
output_surface_->setTextureIndex( output->texture() );
@@ -136,7 +138,7 @@ void TransitionView::update(float dt)
// request update
transition_source_->touch();
if (d > 0.2f && Settings::application.transition.auto_open)
if (d > 0.2f)
Mixer::manager().setView(View::MIXING);
}
@@ -168,8 +170,11 @@ void TransitionView::draw()
scene.accept(dv2);
// display interface duration
glm::vec2 P = Rendering::manager().project(glm::vec3(-0.17f, -0.14f, 0.f), scene.root()->transform_, false);
ImGui::SetNextWindowPos(ImVec2(P.x, P.y), ImGuiCond_Always);
glm::vec2 pos_window = Rendering::manager().project(glm::vec3(-0.2f, -0.14f, 0.f), scene.root()->transform_, false);
glm::vec2 pos_play = Rendering::manager().project(glm::vec3(0.f, -0.14f, 0.f), scene.root()->transform_, false);
glm::vec2 pos_open = Rendering::manager().project(glm::vec3(POS_TARGET, -0.14f, 0.f), scene.root()->transform_, false);
ImGui::SetNextWindowPos(ImVec2(pos_window.x, pos_window.y), ImGuiCond_Always);
if (ImGui::Begin("##Transition", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground
| ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus))
@@ -183,22 +188,31 @@ void TransitionView::draw()
ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(0.10f, 0.10f, 0.10f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.00f, 0.00f, 0.00f, 0.00f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.27f, 0.27f, 0.27f, 0.55f)); // 7 colors
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
ImGui::SetNextItemWidth(180.f);
// Duration slider (adjusted width)
ImGui::SetNextItemWidth(pos_play.x - pos_window.x - 20.f);
ImGui::SliderFloat("##transitionduration", &Settings::application.transition.duration,
TRANSITION_MIN_DURATION, TRANSITION_MAX_DURATION, "%.1f s");
ImGui::SameLine();
if ( ImGui::Button(ICON_FA_STEP_FORWARD) )
// Play button just at the end of the timeline
ImGui::SetCursorScreenPos(ImVec2(pos_play.x, pos_play.y+2));
if ( ImGui::Button(ICON_FA_PLAY_CIRCLE) )
play(false);
// centered "Open" button on the target frame
ImGui::SetCursorScreenPos(ImVec2(pos_open.x -80.f, pos_open.y +2.f));
ImGui::SetNextItemWidth(160.f);
if ( ImGui::Button(ICON_FA_FILE_UPLOAD " Open") )
open();
ImGui::PopFont();
ImGui::PopStyleColor(7); // 7 colors
ImGui::End();
}
P = Rendering::manager().project(glm::vec3(-0.535f, -0.14f, 0.f), scene.root()->transform_, false);
ImGui::SetNextWindowPos(ImVec2(P.x, P.y), ImGuiCond_Always);
pos_window = Rendering::manager().project(glm::vec3(-0.535f, -0.14f, 0.f), scene.root()->transform_, false);
ImGui::SetNextWindowPos(ImVec2(pos_window.x, pos_window.y), ImGuiCond_Always);
if (ImGui::Begin("##TransitionType", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground
| ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus))
@@ -309,6 +323,14 @@ std::pair<Node *, glm::vec2> TransitionView::pick(glm::vec2 P)
}
void TransitionView::open()
{
// quick jump over to target (and open)
transition_source_->group(View::TRANSITION)->clearCallbacks();
MoveToCallback *anim = new MoveToCallback(glm::vec3(POS_TARGET, 0.0, 0.0), 180.f);
transition_source_->group(View::TRANSITION)->update_callbacks_.push_back(anim);
}
void TransitionView::play(bool open)
{
if (transition_source_ != nullptr) {
@@ -323,7 +345,7 @@ void TransitionView::play(bool open)
transition_source_->group(View::TRANSITION)->clearCallbacks();
// if want to open session after play, target movement till end position, otherwise stop at 0
float target_x = open ? 0.4f : 0.f;
float target_x = open ? POS_TARGET : 0.f;
// calculate how far to reach target
float time = CLAMP(- transition_source_->group(View::TRANSITION)->translation_.x, 0.f, 1.f);
@@ -332,7 +354,6 @@ void TransitionView::play(bool open)
// calculate remaining time on the total duration, in ms
time *= Settings::application.transition.duration * 1000.f;
// if remaining time is more than 50ms
if (time > 50.f) {
// start animation

View File

@@ -21,6 +21,7 @@ public:
void attach(SessionFileSource *ts);
Session *detach();
void play(bool open);
void open();
private:
Surface *output_surface_;

View File

@@ -40,7 +40,7 @@ void MoveToCallback::update(Node *n, float dt)
}
RotateToCallback::RotateToCallback(float target, float duration) : UpdateCallback(),
duration_(duration), progress_(0.f), initialized_(false), target_(target)
duration_(duration), progress_(0.f), initialized_(false), startingangle_(0.f), target_(target)
{
}

View File

@@ -20,9 +20,6 @@ using namespace std;
// Include glfw3.h after our OpenGL definitions
#include <GLFW/glfw3.h>
// multiplatform
#include <tinyfiledialogs.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/string_cast.hpp>
@@ -38,17 +35,17 @@ using namespace std;
#include "defines.h"
#include "Log.h"
#include "SystemToolkit.h"
#include "DialogToolkit.h"
#include "GlmToolkit.h"
#include "GstToolkit.h"
#include "ImGuiToolkit.h"
#include "ImGuiVisitor.h"
#include "RenderingManager.h"
#include "Connection.h"
#include "ActionManager.h"
#include "Resource.h"
#include "FileDialog.h"
#include "Settings.h"
#include "SessionCreator.h"
#include "ImGuiToolkit.h"
#include "ImGuiVisitor.h"
#include "GlmToolkit.h"
#include "GstToolkit.h"
#include "Mixer.h"
#include "Recorder.h"
#include "Streamer.h"
@@ -78,79 +75,12 @@ void ShowSandbox(bool* p_open);
const std::chrono::milliseconds timeout = std::chrono::milliseconds(4);
static std::atomic<bool> fileDialogPending_ = false;
static std::vector< std::future<std::string> > saveSessionFileDialogs;
static std::string saveSessionFileDialog(const std::string &path)
{
std::string filename = "";
char const * save_file_name;
char const * save_pattern[1] = { "*.mix" };
save_file_name = tinyfd_saveFileDialog( "Save a session file", path.c_str(), 1, save_pattern, "vimix session");
if (save_file_name) {
filename = std::string(save_file_name);
std::string extension = filename.substr(filename.find_last_of(".") + 1);
if (extension != "mix")
filename += ".mix";
}
return filename;
}
static std::vector< std::future<std::string> > openSessionFileDialogs;
static std::vector< std::future<std::string> > importSessionFileDialogs;
static std::string openSessionFileDialog(const std::string &path)
{
std::string filename = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
char const * open_pattern[18] = { "*.mix" };
char const * open_file_name;
open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 1, open_pattern, "vimix session", 0);
if (open_file_name)
filename = std::string(open_file_name);
return filename;
}
static std::vector< std::future<std::string> > fileImportFileDialogs;
static std::string ImportFileDialog(const std::string &path)
{
std::string filename = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
char const * open_pattern[18] = { "*.mix", "*.mp4", "*.mpg",
"*.avi", "*.mov", "*.mkv",
"*.webm", "*.mod", "*.wmv",
"*.mxf", "*.ogg", "*.flv",
"*.asf", "*.jpg", "*.png",
"*.gif", "*.tif", "*.svg" };
char const * open_file_name;
open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 18, open_pattern, "All supported formats", 0);
if (open_file_name)
filename = std::string(open_file_name);
return filename;
}
static std::vector< std::future<std::string> > recentFolderFileDialogs;
static std::vector< std::future<std::string> > recordFolderFileDialogs;
static std::string FolderDialog(const std::string &path)
{
std::string foldername = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
char const * open_folder_name;
open_folder_name = tinyfd_selectFolderDialog("Select a folder", startpath.c_str());
if (open_folder_name)
foldername = std::string(open_folder_name);
return foldername;
}
UserInterface::UserInterface()
@@ -238,7 +168,7 @@ bool UserInterface::Init()
void UserInterface::handleKeyboard()
{
ImGuiIO& io = ImGui::GetIO();
const ImGuiIO& io = ImGui::GetIO();
alt_modifier_active = io.KeyAlt;
shift_modifier_active = io.KeyShift;
bool ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
@@ -328,12 +258,12 @@ void UserInterface::handleKeyboard()
Action::manager().undo();
}
else if (ImGui::IsKeyPressed( GLFW_KEY_C )) {
std::string clipboard = Mixer::selection().xml();
std::string clipboard = Mixer::selection().clipboard();
if (!clipboard.empty())
ImGui::SetClipboardText(clipboard.c_str());
}
else if (ImGui::IsKeyPressed( GLFW_KEY_X )) {
std::string clipboard = Mixer::selection().xml();
std::string clipboard = Mixer::selection().clipboard();
if (!clipboard.empty()) {
ImGui::SetClipboardText(clipboard.c_str());
Mixer::manager().deleteSelection();
@@ -700,7 +630,7 @@ void UserInterface::selectSaveFilename()
{
// launch file dialog to select a session filename to save
if ( saveSessionFileDialogs.empty()) {
saveSessionFileDialogs.emplace_back( std::async(std::launch::async, saveSessionFileDialog, Settings::application.recentSessions.path) );
saveSessionFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::saveSessionFileDialog, Settings::application.recentSessions.path) );
fileDialogPending_ = true;
}
navigator.hidePannel();
@@ -710,7 +640,7 @@ void UserInterface::selectOpenFilename()
{
// launch file dialog to select a session filename to open
if ( openSessionFileDialogs.empty()) {
openSessionFileDialogs.emplace_back( std::async(std::launch::async, openSessionFileDialog, Settings::application.recentSessions.path) );
openSessionFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openSessionFileDialog, Settings::application.recentSessions.path) );
fileDialogPending_ = true;
}
navigator.hidePannel();
@@ -857,10 +787,10 @@ void UserInterface::showMenuEdit()
{
bool has_selection = !Mixer::selection().empty();
const char *clipboard = ImGui::GetClipboardText();
bool has_clipboard = (clipboard != nullptr && strlen(clipboard) > 0 && Mixer::manager().isClipboard(clipboard));
bool has_clipboard = (clipboard != nullptr && strlen(clipboard) > 0 && SessionLoader::isClipboard(clipboard));
if (ImGui::MenuItem( ICON_FA_CUT " Cut", CTRL_MOD "X", false, has_selection)) {
std::string copied_text = Mixer::selection().xml();
std::string copied_text = Mixer::selection().clipboard();
if (!copied_text.empty()) {
ImGui::SetClipboardText(copied_text.c_str());
Mixer::manager().deleteSelection();
@@ -868,7 +798,7 @@ void UserInterface::showMenuEdit()
navigator.hidePannel();
}
if (ImGui::MenuItem( ICON_FA_COPY " Copy", CTRL_MOD "C", false, has_selection)) {
std::string copied_text = Mixer::selection().xml();
std::string copied_text = Mixer::selection().clipboard();
if (!copied_text.empty())
ImGui::SetClipboardText(copied_text.c_str());
navigator.hidePannel();
@@ -896,17 +826,17 @@ void UserInterface::showMenuFile()
ImGui::Separator();
ImGui::MenuItem( ICON_FA_HISTORY " Open last on start", nullptr, &Settings::application.recentSessions.load_at_start);
ImGui::MenuItem( ICON_FA_LEVEL_UP_ALT " Open last on start", nullptr, &Settings::application.recentSessions.load_at_start);
if (ImGui::MenuItem( ICON_FA_FILE_UPLOAD " Open", CTRL_MOD "O"))
selectOpenFilename();
if (ImGui::MenuItem( ICON_FA_FILE_UPLOAD " Reload", CTRL_MOD "Shift+O"))
if (ImGui::MenuItem( ICON_FA_FILE_UPLOAD " Re-open", CTRL_MOD "Shift+O"))
Mixer::manager().load( Mixer::manager().session()->filename() );
if (ImGui::MenuItem( ICON_FA_FILE_EXPORT " Import")) {
// launch file dialog to open a session file
if ( importSessionFileDialogs.empty()) {
importSessionFileDialogs.emplace_back( std::async(std::launch::async, openSessionFileDialog, Settings::application.recentSessions.path) );
importSessionFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openSessionFileDialog, Settings::application.recentSessions.path) );
fileDialogPending_ = true;
}
navigator.hidePannel();
@@ -919,10 +849,10 @@ void UserInterface::showMenuFile()
navigator.hidePannel();
}
}
if (ImGui::MenuItem( ICON_FA_SAVE " Save as", CTRL_MOD "Shift+S"))
if (ImGui::MenuItem( ICON_FA_FILE_DOWNLOAD " Save as", CTRL_MOD "Shift+S"))
selectSaveFilename();
ImGui::MenuItem( ICON_FA_FILE_DOWNLOAD " Save on exit", nullptr, &Settings::application.recentSessions.save_on_exit);
ImGui::MenuItem( ICON_FA_LEVEL_DOWN_ALT " Save on exit", nullptr, &Settings::application.recentSessions.save_on_exit);
ImGui::Separator();
if (ImGui::MenuItem( ICON_FA_POWER_OFF " Quit", CTRL_MOD "Q"))
@@ -1011,14 +941,6 @@ void ToolBox::Render()
ImGui::EndMenuBar();
}
static char buf1[128] = "videotestsrc pattern=smpte";
ImGui::InputText("gstreamer pipeline", buf1, 128);
if (ImGui::Button("Create Generic Stream Source") )
{
Mixer::manager().addSource( Mixer::manager().createSourceStream(buf1) );
}
//
// display histogram of update time and plot framerate
//
@@ -1156,6 +1078,32 @@ void UserInterface::RenderHistory()
ImGui::End();
}
void SetNextWindowVisible(ImVec2 pos, ImVec2 size, float margin = 80.f)
{
bool need_update = false;
ImVec2 pos_target = pos;
const ImGuiIO& io = ImGui::GetIO();
if ( pos_target.y > io.DisplaySize.y - margin ){
pos_target.y -= margin;
need_update = true;
}
if ( pos_target.y + size.y < margin ){
pos_target.y += margin;
need_update = true;
}
if ( pos_target.x > io.DisplaySize.x - margin){
pos_target.x -= margin;
need_update = true;
}
if ( pos_target.x + size.x <margin ){
pos_target.x += margin;
need_update = true;
}
if (need_update)
ImGui::SetNextWindowPos(pos_target, ImGuiCond_Appearing);
}
void UserInterface::RenderPreview()
{
#if defined(LINUX)
@@ -1173,16 +1121,25 @@ void UserInterface::RenderPreview()
FrameBuffer *output = Mixer::manager().session()->frame();
if (output)
{
float ar = output->aspectRatio();
ImGui::SetNextWindowPos(ImVec2(1180, 20), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(400, 260), ImGuiCond_FirstUseEver);
// constraint position
static ImVec2 preview_window_pos = ImVec2(1180, 20);
static ImVec2 preview_window_size = ImVec2(400, 260);
SetNextWindowVisible(preview_window_pos, preview_window_size);
// constraint aspect ratio resizing
float ar = output->aspectRatio();
ImGui::SetNextWindowSizeConstraints(ImVec2(300, 200), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::AspectRatio, &ar);
if ( !ImGui::Begin("Preview", &Settings::application.widget.preview, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse ) )
{
ImGui::End();
return;
}
preview_window_pos = ImGui::GetWindowPos();
preview_window_size = ImGui::GetWindowSize();
FrameGrabber *rec = FrameGrabbing::manager().get(video_recorder_);
#if defined(LINUX)
@@ -1288,7 +1245,7 @@ void UserInterface::RenderPreview()
ImGui::Combo("Path", &selected_path, name_path, 4);
if (selected_path > 2) {
if (recordFolderFileDialogs.empty()) {
recordFolderFileDialogs.emplace_back( std::async(std::launch::async, FolderDialog, Settings::application.record.path) );
recordFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::FolderDialog, Settings::application.record.path) );
fileDialogPending_ = true;
}
}
@@ -1448,7 +1405,7 @@ int UserInterface::RenderViewNavigator(int *shift)
int target_index = ( (Settings::application.current_view -1)+ (*shift -1) )%4 + 1;
// prepare rendering of centered, fixed-size, semi-transparent window;
ImGuiIO& io = ImGui::GetIO();
const ImGuiIO& io = ImGui::GetIO();
ImVec2 window_pos = ImVec2(io.DisplaySize.x / 2.f, io.DisplaySize.y / 2.f);
ImVec2 window_pos_pivot = ImVec2(0.5f, 0.5f);
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
@@ -1582,35 +1539,64 @@ void MediaController::setMediaPlayer(MediaPlayer *mp)
void MediaController::followCurrentSource()
{
Source *s = Mixer::manager().currentSource();
if ( s != nullptr) {
MediaSource *ms = dynamic_cast<MediaSource *>(s);
if (ms) {
// update the internal mediaplayer if changed
if (mp_ != ms->mediaplayer()) {
mp_ = ms->mediaplayer();
media_playing_mode_ = mp_->isPlaying();
}
} else {
mp_ = nullptr;
media_playing_mode_ = false;
MediaSource *ms = nullptr;
if ( s != nullptr)
ms = dynamic_cast<MediaSource *>(s);
if ( ms != nullptr) {
// update the internal mediaplayer if changed
if (mp_ != ms->mediaplayer()) {
mp_ = ms->mediaplayer();
media_playing_mode_ = mp_->isPlaying();
}
}
else {
mp_ = nullptr;
media_playing_mode_ = false;
}
}
void MediaController::Render()
{
float toolbar_height = 2.5 * ImGui::GetFrameHeightWithSpacing();
ImVec2 MinWindowSize = ImVec2(350.f, 1.2f * ImGui::GetFrameHeightWithSpacing() + toolbar_height * (Settings::application.widget.media_player_view? 2.f : 1.f));
ImGui::SetNextWindowPos(ImVec2(1180, 400), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints(MinWindowSize, ImVec2(FLT_MAX, FLT_MAX));
// constraint position
static ImVec2 media_window_pos = ImVec2(1180, 20);
static ImVec2 media_window_size = ImVec2(400, 260);
SetNextWindowVisible(media_window_pos, media_window_size);
// estimate window size
const ImGuiContext& g = *GImGui;
const float _spacing = 6.0f;
const float _height = g.FontSize + g.Style.FramePadding.y * 2.0f ;
const float _timeline_height = (g.FontSize + g.Style.FramePadding.y) * 2.0f ; // double line for each timeline
const float _scrollbar_height = g.Style.ScrollbarSize;
// all together: 1 title bar + spacing + 1 toolbar + spacing + 2 timelines + scrollbar
const float _toobar_height = _height + 2.f * _timeline_height + _scrollbar_height + 2.f * _spacing;
// window: 1 title bar + spacing + toolbar
ImVec2 MinWindowSize = ImVec2(350.f, _height + _spacing + _toobar_height);
if (Settings::application.widget.media_player_view) {
// + min 2 lines for player if visible
MinWindowSize.y += 2.f * _height;
// free window height
ImGui::SetNextWindowSizeConstraints(MinWindowSize, ImVec2(FLT_MAX, FLT_MAX));
}
else {
// fixed window height
ImGui::SetNextWindowSizeConstraints(MinWindowSize, ImVec2(FLT_MAX, MinWindowSize.y));
}
if ( !ImGui::Begin(IMGUI_TITLE_MEDIAPLAYER, &Settings::application.widget.media_player, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse ))
{
ImGui::End();
return;
}
media_window_pos = ImGui::GetWindowPos();
media_window_size = ImGui::GetWindowSize();
// verify that mp is still registered
if ( std::find(MediaPlayer::begin(),MediaPlayer::end(), mp_ ) == MediaPlayer::end() ) {
@@ -1625,6 +1611,7 @@ void MediaController::Render()
if (ImGui::BeginMenu(IMGUI_TITLE_MEDIAPLAYER))
{
ImGui::MenuItem( ICON_FA_EYE " Preview", nullptr, &Settings::application.widget.media_player_view);
// ImGui::MenuItem( ICON_FA_QUESTION_CIRCLE " Help", nullptr, &media_player_help);
if ( ImGui::MenuItem( ICON_FA_TIMES " Close", CTRL_MOD "P") )
Settings::application.widget.media_player = false;
@@ -1682,19 +1669,15 @@ void MediaController::Render()
{
static float timeline_zoom = 1.f;
const float width = ImGui::GetContentRegionAvail().x;
const float timeline_height = 2.f * (ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y);
const float segments_height = timeline_height;
const float slider_zoom_width = segments_height / 2.f;
const float slider_zoom_width = _timeline_height / 2.f;
if (Settings::application.widget.media_player_view)
{
// set an image height to fill the vertical space, minus the height of control bar
float image_height = ImGui::GetContentRegionAvail().y;
if ( !mp_->isImage() ) {
// leave space for buttons and timelines
image_height -= ImGui::GetFrameHeight() + timeline_height + segments_height ;
// leave space for scrollbar & spacing
image_height -= ImGui::GetStyle().ScrollbarSize + 3.f * ImGui::GetStyle().ItemSpacing.y;
// leave space for buttons and timelines + spacing
image_height -= _toobar_height;
}
// display media
@@ -1714,8 +1697,14 @@ void MediaController::Render()
ImVec2(tooltip_pos.x + width + 10.f, tooltip_pos.y + tooltip_height), IMGUI_COLOR_OVERLAY);
ImGui::SetCursorScreenPos(tooltip_pos);
// filename
ImGui::Text(" %s", mp_->filename().c_str());
ImGui::Text(" %s", mp_->media().codec_name.c_str());
// decoding info
if (Settings::application.render.gpu_decoding && !mp_->hardwareDecoderName().empty() )
ImGui::Text(" %s (%s hardware decoder)", mp_->media().codec_name.c_str(), mp_->hardwareDecoderName().c_str());
else
ImGui::Text(" %s", mp_->media().codec_name.c_str());
// framerate
if ( mp_->frameRate() > 1.f )
ImGui::Text(" %d x %d px, %.2f / %.2f fps", mp_->width(), mp_->height(), mp_->updateFrameRate() , mp_->frameRate() );
else
@@ -1734,6 +1723,7 @@ void MediaController::Render()
ImGui::SetCursorPos(return_to_pos);
}
ImGui::Spacing();
// Control bar
if ( mp_->isEnabled() && !mp_->isImage()) {
@@ -1778,61 +1768,79 @@ void MediaController::Render()
mp_->setLoop( (MediaPlayer::LoopMode) current_loop );
// speed slider
ImGui::SameLine(0, MAX(spacing * 4.f, width - 400.f) );
ImGui::SameLine(0, MAX(spacing * 2.f, width - 450.f) );
float speed = static_cast<float>(mp_->playSpeed());
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 40.0);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - slider_zoom_width - 3.0);
if (ImGui::DragFloat( "##Speed", &speed, 0.01f, -10.f, 10.f, "Speed x %.1f", 2.f))
mp_->setPlaySpeed( static_cast<double>(speed) );
// if (ImGui::IsItemDeactivatedAfterEdit())
// Action::manager().store("Play Speed");
// TODO: avoid seek every time speed is set
// Timeline popup menu
ImGui::SameLine(0, spacing);
if (ImGuiToolkit::ButtonIcon(5, 8))
ImGui::SameLine(0, spacing * 0.5f);
bool l = true;
if (ImGuiToolkit::IconToggle(5,8,5,8, &l) )
ImGui::OpenPopup( "MenuTimeline" );
if (ImGui::BeginPopup( "MenuTimeline" )) {
if (ImGui::Selectable( "Reset Speed" )){
if (ImGui::BeginPopup( "MenuTimeline" ))
{
if (ImGui::MenuItem("Reset Speed" )){
speed = 1.f;
mp_->setPlaySpeed( static_cast<double>(speed) );
}
if (ImGui::Selectable( "Reset Timeline" )){
if (ImGui::MenuItem( "Reset Timeline" )){
timeline_zoom = 1.f;
mp_->timeline()->clearFading();
mp_->timeline()->clearGaps();
Action::manager().store("Timeline Reset");
}
ImGui::Separator();
ImGui::SetNextItemWidth(150);
int smoothcurve = 0;
if (ImGui::Combo("##SmoothCurve", &smoothcurve, "Smooth curve\0Just a little\0A bit more\0Quite a lot\0") ){
mp_->timeline()->smoothFading( 10 * (int) pow(4, smoothcurve-1) );
Action::manager().store("Timeline Smooth curve");
if (ImGui::BeginMenu("Smooth curve"))
{
const char* names[] = { "Just a little", "A bit more", "Quite a lot"};
for (int i = 0; i < IM_ARRAYSIZE(names); i++) {
if (ImGui::MenuItem(names[i])) {
mp_->timeline()->smoothFading( 10 * (int) pow(4, i) );
Action::manager().store("Timeline Smooth curve");
}
}
ImGui::EndMenu();
}
ImGui::SetNextItemWidth(150);
int autofade = 0;
if (ImGui::Combo("##Autofade", &autofade, "Auto fading\0 250 ms\0 500 ms\0 1 second\0 2 seconds\0") ){
mp_->timeline()->autoFading( 250 * (int ) pow(2, autofade-1) );
mp_->timeline()->smoothFading( 10 * autofade );
Action::manager().store("Timeline Auto fading");
if (ImGui::BeginMenu("Auto fading"))
{
const char* names[] = { "250 ms", "500 ms", "1 second", "2 seconds"};
for (int i = 0; i < IM_ARRAYSIZE(names); i++) {
if (ImGui::MenuItem(names[i])) {
mp_->timeline()->autoFading( 250 * (int ) pow(2, i) );
mp_->timeline()->smoothFading( 10 * (i + 1) );
Action::manager().store("Timeline Auto fading");
}
}
ImGui::EndMenu();
}
if (Settings::application.render.gpu_decoding && ImGui::BeginMenu("Hardware Decoding"))
{
bool hwdec = !mp_->softwareDecodingForced();
if (ImGui::MenuItem("Auto", "", &hwdec ))
mp_->setSoftwareDecodingForced(false);
hwdec = mp_->softwareDecodingForced();
if (ImGui::MenuItem("Disabled", "", &hwdec ))
mp_->setSoftwareDecodingForced(true);
ImGui::EndMenu();
}
ImGui::EndPopup();
}
ImGui::Spacing();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(3.f, 3.f));
guint64 current_t = mp_->position();
guint64 seek_t = current_t;
guint64 seek_t = mp_->position();
// scrolling sub-window
ImGui::BeginChild("##scrolling",
ImVec2(ImGui::GetContentRegionAvail().x - slider_zoom_width - 3.0,
timeline_height + segments_height + ImGui::GetStyle().ScrollbarSize ),
2.f * _timeline_height + _scrollbar_height ),
false, ImGuiWindowFlags_HorizontalScrollbar);
{
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(1.f, 1.f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.f);
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), segments_height -1);
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), _timeline_height -1);
size.x *= timeline_zoom;
bool released = false;
@@ -1856,7 +1864,7 @@ void MediaController::Render()
// zoom slider
ImGui::SameLine();
ImGui::VSliderFloat("##TimelineZoom", ImVec2(slider_zoom_width, timeline_height + segments_height), &timeline_zoom, 1.0, 5.f, "");
ImGui::VSliderFloat("##TimelineZoom", ImVec2(slider_zoom_width, 2.f * _timeline_height), &timeline_zoom, 1.0, 5.f, "");
ImGui::PopStyleVar();
// request seek (ASYNC)
@@ -1889,7 +1897,7 @@ void MediaController::Render()
ImGui::End();
}
void UserInterface::fillShaderEditor(std::string text)
void UserInterface::fillShaderEditor(const std::string &text)
{
static bool initialized = false;
if (!initialized) {
@@ -1923,6 +1931,7 @@ void UserInterface::fillShaderEditor(std::string text)
}
// init editor
editor.SetLanguageDefinition(lang);
initialized = true;
}
// remember text
@@ -2079,8 +2088,8 @@ void Navigator::Render()
std::string tooltip_ = "";
static uint timer_tooltip_ = 0;
ImGuiIO& io = ImGui::GetIO();
ImGuiStyle& style = ImGui::GetStyle();
const ImGuiIO& io = ImGui::GetIO();
const ImGuiStyle& style = ImGui::GetStyle();
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(COLOR_NAVIGATOR, 1.f));
@@ -2118,7 +2127,7 @@ void Navigator::Render()
// the list of INITIALS for sources
int index = 0;
SourceList::iterator iter;
for (iter = Mixer::manager().session()->begin(); iter != Mixer::manager().session()->end(); iter++, index++)
for (iter = Mixer::manager().session()->begin(); iter != Mixer::manager().session()->end(); ++iter, ++index)
{
Source *s = (*iter);
// draw an indicator for current source
@@ -2153,11 +2162,13 @@ void Navigator::Render()
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_SOURCE"))
{
if ( payload->DataSize == sizeof(int) ) {
bool status_current_index = selected_button[Mixer::manager().indexCurrentSource()];
// drop means move index and reorder
int payload_index = *(const int*)payload->Data;
int current_source_index = Mixer::manager().indexCurrentSource();
Mixer::manager().session()->move(payload_index, index);
if (payload_index == current_source_index)
applyButtonSelection(index);
Mixer::manager().moveIndex(payload_index, index);
// index of current source changed
selected_button[Mixer::manager().indexCurrentSource()] = status_current_index;
applyButtonSelection(Mixer::manager().indexCurrentSource());
}
}
ImGui::EndDragDropTarget();
@@ -2357,7 +2368,7 @@ SourcePreview::SourcePreview() : source_(nullptr), label_("")
}
void SourcePreview::setSource(Source *s, std::string label)
void SourcePreview::setSource(Source *s, const string &label)
{
if(source_)
delete source_;
@@ -2429,7 +2440,7 @@ void Navigator::RenderNewPannel()
// TITLE
ImGui::SetCursorPosY(10);
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
ImGui::Text("New");
ImGui::Text("Insert");
ImGui::PopFont();
// Edit menu
@@ -2464,7 +2475,7 @@ void Navigator::RenderNewPannel()
if ( ImGui::Button( ICON_FA_FILE_EXPORT " Open file", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) ) {
// launch async call to file dialog and get its future.
if (fileImportFileDialogs.empty()) {
fileImportFileDialogs.emplace_back( std::async(std::launch::async, ImportFileDialog, Settings::application.recentImport.path) );
fileImportFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::ImportFileDialog, Settings::application.recentImport.path) );
fileDialogPending_ = true;
}
}
@@ -2483,9 +2494,13 @@ void Navigator::RenderNewPannel()
fileImportFileDialogs.pop_back();
fileDialogPending_ = false;
// create a source with this file
std::string label = SystemToolkit::transliterate( open_filename );
label = label.substr( label.size() - MIN( 35, label.size()) );
new_source_preview_.setSource( Mixer::manager().createSourceFile(open_filename), label);
if (open_filename.empty()) {
Log::Notify("No file selected.");
} else {
std::string label = SystemToolkit::transliterate( open_filename );
label = label.substr( label.size() - MIN( 35, label.size()) );
new_source_preview_.setSource( Mixer::manager().createSourceFile(open_filename), label);
}
}
}
@@ -2494,7 +2509,7 @@ void Navigator::RenderNewPannel()
if (ImGui::BeginCombo("##RecentImport", IMGUI_LABEL_RECENT_FILES))
{
std::list<std::string> recent = Settings::application.recentImport.filenames;
for (std::list<std::string>::iterator path = recent.begin(); path != recent.end(); path++ )
for (std::list<std::string>::iterator path = recent.begin(); path != recent.end(); ++path )
{
std::string recentpath(*path);
if ( SystemToolkit::file_exists(recentpath)) {
@@ -2522,7 +2537,7 @@ void Navigator::RenderNewPannel()
new_source_preview_.setSource( Mixer::manager().createSourceRender(), label);
}
SourceList::iterator iter;
for (iter = Mixer::manager().session()->begin(); iter != Mixer::manager().session()->end(); iter++)
for (iter = Mixer::manager().session()->begin(); iter != Mixer::manager().session()->end(); ++iter)
{
label = std::string("Source ") + (*iter)->name();
if (ImGui::Selectable( label.c_str() )) {
@@ -2653,7 +2668,6 @@ void Navigator::RenderTransitionPannel()
ImGui::SetCursorPosY(width_);
ImGui::Text("Behavior");
ImGuiToolkit::ButtonSwitch( ICON_FA_RANDOM " Cross fading", &Settings::application.transition.cross_fade);
ImGuiToolkit::ButtonSwitch( ICON_FA_CROSSHAIRS " Open on target", &Settings::application.transition.auto_open);
ImGuiToolkit::ButtonSwitch( ICON_FA_CLOUD_SUN " Clear view", &Settings::application.transition.hide_windows);
// Transition options
@@ -2668,15 +2682,29 @@ void Navigator::RenderTransitionPannel()
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::Combo("Curve", &Settings::application.transition.profile, "Linear\0Quadratic\0");
ImGui::Spacing();
if ( ImGui::Button( ICON_FA_SIGN_IN_ALT " Play ", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ){
// specific transition actions
ImGui::Text(" ");
if ( ImGui::Button( ICON_FA_PLAY_CIRCLE " Play ", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ){
TransitionView *tv = static_cast<TransitionView *>(Mixer::manager().view(View::TRANSITION));
if (tv) tv->play(false);
}
ImGui::SameLine();
ImGui::Text("Animation");
if ( ImGui::Button( ICON_FA_FILE_UPLOAD " Open ", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ){
TransitionView *tv = static_cast<TransitionView *>(Mixer::manager().view(View::TRANSITION));
if (tv) tv->open();
}
ImGui::SameLine();
ImGui::Text("Session");
// General transition actions
ImGui::Text(" ");
if ( ImGui::Button( ICON_FA_PLAY_CIRCLE " Play & " ICON_FA_FILE_UPLOAD " Open ", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ){
TransitionView *tv = static_cast<TransitionView *>(Mixer::manager().view(View::TRANSITION));
if (tv) tv->play(true);
}
if ( ImGui::Button( ICON_FA_DOOR_OPEN " Exit", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
if ( ImGui::Button( ICON_FA_DOOR_OPEN " Exit", ImVec2(ImGui::GetContentRegionAvail().x, 0)) )
Mixer::manager().setView(View::MIXING);
ImGui::SameLine();
ImGuiToolkit::HelpMarker("Exit transition leaves the output 'as is',\nwith the newly openned session as a source\nif the transition is not finished.");
}
ImGui::End();
@@ -2746,7 +2774,7 @@ void Navigator::RenderMainPannel()
// Option 2 : add a folder
if (ImGui::Selectable( ICON_FA_FOLDER_PLUS " Add Folder") ){
if (recentFolderFileDialogs.empty()) {
recentFolderFileDialogs.emplace_back( std::async(std::launch::async, FolderDialog, Settings::application.recentFolders.path) );
recentFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::FolderDialog, Settings::application.recentFolders.path) );
fileDialogPending_ = true;
}
}
@@ -2778,8 +2806,8 @@ void Navigator::RenderMainPannel()
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.7);
bool reset = false;
if ( selection_session_mode == 1) {
const char *tooltip[2] = {"Discard folder", "Discard folder"};
if (ImGuiToolkit::IconToggle(12,14,11,14, &reset, tooltip)) {
const char *tooltip_[2] = {"Discard folder", "Discard folder"};
if (ImGuiToolkit::IconToggle(12,14,11,14, &reset, tooltip_)) {
Settings::application.recentFolders.filenames.remove(Settings::application.recentFolders.path);
if (Settings::application.recentFolders.filenames.empty()) {
Settings::application.recentFolders.path.assign(IMGUI_LABEL_RECENT_FILES);
@@ -2792,8 +2820,8 @@ void Navigator::RenderMainPannel()
}
}
else {
const char *tooltip[2] = {"Clear history", "Clear history"};
if (ImGuiToolkit::IconToggle(12,14,11,14, &reset, tooltip)) {
const char *tooltip__[2] = {"Clear history", "Clear history"};
if (ImGuiToolkit::IconToggle(12,14,11,14, &reset, tooltip__)) {
Settings::application.recentSessions.filenames.clear();
Settings::application.recentSessions.front_is_valid = false;
// reload the list next time
@@ -2806,12 +2834,13 @@ void Navigator::RenderMainPannel()
// fill the session list depending on the mode
static std::list<std::string> sessions_list;
// change session list if changed
if (selection_session_mode_changed) {
if (selection_session_mode_changed || Settings::application.recentSessions.changed) {
// selection MODE 0 ; RECENT sessions
if ( selection_session_mode == 0) {
// show list of recent sessions
sessions_list = Settings::application.recentSessions.filenames;
Settings::application.recentSessions.changed = false;
}
// selection MODE 1 : LIST FOLDER
else if ( selection_session_mode == 1) {
@@ -2831,7 +2860,7 @@ void Navigator::RenderMainPannel()
for(auto it = sessions_list.begin(); it != sessions_list.end(); it++) {
std::string sessionfilename(*it);
if (sessionfilename.empty())
break;
continue;
std::string shortname = SystemToolkit::filename(*it);
if (ImGui::Selectable( shortname.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick )) {
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) || file_selected == it) {
@@ -2857,7 +2886,7 @@ void Navigator::RenderMainPannel()
file_info.clear();
file_selected = sessions_list.end();
}
if (!file_info.empty()) {
else if (!file_info.empty()) {
ImGui::BeginTooltip();
ImGui::Text("%s", file_info.c_str());
ImGui::EndTooltip();
@@ -2957,42 +2986,44 @@ void Navigator::RenderMainPannel()
}
namespace ImGui
{
//namespace ImGui
//{
int hover(const char *label)
{
const ImGuiStyle& Style = GetStyle();
ImGuiWindow* Window = GetCurrentWindow();
if (Window->SkipItems)
return 0;
//int hover(const char *label)
//{
// const ImGuiStyle& Style = GetStyle();
// ImGuiWindow* Window = GetCurrentWindow();
// if (Window->SkipItems)
// return 0;
int hovered = IsItemActive() || IsItemHovered();
Dummy(ImVec2(0,3));
// int hovered = IsItemActive() || IsItemHovered();
// Dummy(ImVec2(0,3));
// prepare canvas
const float avail = GetContentRegionAvailWidth();
const float dim = ImMin(avail, 128.f);
ImVec2 Canvas(dim, dim);
// // prepare canvas
// const float avail = GetContentRegionAvailWidth();
// const float dim = ImMin(avail, 128.f);
// ImVec2 Canvas(dim, dim);
ImRect bb(Window->DC.CursorPos, Window->DC.CursorPos + Canvas);
const ImGuiID id = Window->GetID(label);
ItemSize(bb);
if (!ItemAdd(bb, id))
return 0;
// ImRect bb(Window->DC.CursorPos, Window->DC.CursorPos + Canvas);
// const ImGuiID id = Window->GetID(label);
// ItemSize(bb);
// if (!ItemAdd(bb, id))
// return 0;
hovered |= 0 != IsItemClicked();
// hovered |= 0 != IsItemClicked();
RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg, 1), true, Style.FrameRounding);
// RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg, 1), true, Style.FrameRounding);
return 1;
}
// return 1;
//}
}
//}
#define SEGMENT_ARRAY_MAX 1000
#define MAXSIZE 65535
void ShowSandbox(bool* p_open)
{
@@ -3006,6 +3037,33 @@ void ShowSandbox(bool* p_open)
ImGui::Text("Testing sandox");
static char buf1[1280] = "videotestsrc pattern=smpte";
ImGui::InputText("gstreamer pipeline", buf1, 1280);
if (ImGui::Button("Create Generic Stream Source") )
{
Mixer::manager().addSource(Mixer::manager().createSourceStream(buf1));
}
static char str[128] = "";
ImGui::InputText("Command", str, IM_ARRAYSIZE(str));
if ( ImGui::Button("Execute") )
SystemToolkit::execute(str);
if (ImGui::Button("Message test")) {
for(int i=0; i<10; i++) {
Log::Notify("Testing notification %d", i);
}
for(int i=0; i<10; i++) {
Log::Warning("Testing Warning %d", i);
}
}
// const guint64 duration = GST_SECOND * 6;
// const guint64 step = GST_MSECOND * 20;
// static guint64 t = 0;
@@ -3091,8 +3149,9 @@ void ShowSandbox(bool* p_open)
// ImGui::EndChild();
static char str0[128] = "àöäüèáû вторая строчка";
ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0));
ImGui::InputText("##inputtext", str0, IM_ARRAYSIZE(str0));
std::string tra = SystemToolkit::transliterate(std::string(str0));
ImGui::Text("Transliteration: '%s'", tra.c_str());
@@ -3120,7 +3179,7 @@ void UserInterface::RenderAbout(bool* p_open)
ImGui::Separator();
ImGui::Text("vimix performs graphical mixing and blending of\nseveral movie clips and computer generated graphics,\nwith image processing effects in real-time.");
ImGui::Text("\nvimix is licensed under the GNU GPL version 3.\nCopyright 2019-2020 Bruno Herbelin.");
ImGui::Text("\nvimix is licensed under the GNU GPL version 3.\nCopyright 2019-2021 Bruno Herbelin.");
ImGui::Spacing();
ImGuiToolkit::ButtonOpenUrl("https://brunoherbelin.github.io/vimix/", ImVec2(ImGui::GetContentRegionAvail().x, 0));
@@ -3129,10 +3188,13 @@ void UserInterface::RenderAbout(bool* p_open)
ImGui::Spacing();
ImGui::Text("\nvimix is built using the following libraries:");
// tinyfd_inputBox("tinyfd_query", NULL, NULL);
// ImGui::Text("- Tinyfiledialogs v%s mode '%s'", tinyfd_version, tinyfd_response);
ImGui::Columns(3, "abouts");
ImGui::Separator();
ImGui::Text("Dear ImGui");
ImGui::Text("- Dear ImGui");
ImGui::PushID("dearimguiabout");
if ( ImGui::Button("More info", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
show_imgui_about = true;
@@ -3140,7 +3202,7 @@ void UserInterface::RenderAbout(bool* p_open)
ImGui::NextColumn();
ImGui::Text("GStreamer");
ImGui::Text("- GStreamer");
ImGui::PushID("gstreamerabout");
if ( ImGui::Button("More info", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
show_gst_about = true;
@@ -3148,7 +3210,7 @@ void UserInterface::RenderAbout(bool* p_open)
ImGui::NextColumn();
ImGui::Text("OpenGL");
ImGui::Text("- OpenGL");
ImGui::PushID("openglabout");
if ( ImGui::Button("More info", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
show_opengl_about = true;

View File

@@ -22,7 +22,7 @@ class SourcePreview {
public:
SourcePreview();
void setSource(Source *s = nullptr, std::string label = "");
void setSource(Source *s = nullptr, const std::string &label = "");
Source *getSource();
void Render(float width, bool controlbutton = false);
@@ -157,7 +157,7 @@ public:
// TODO implement the shader editor
std::string currentTextEdit;
void fillShaderEditor(std::string text);
void fillShaderEditor(const std::string &text);
protected:

View File

@@ -215,7 +215,7 @@ void View::select(glm::vec2 A, glm::vec2 B)
// create a list of source matching the list of picked nodes
SourceList selection;
// loop over the nodes and add all sources found.
for(std::vector< std::pair<Node *, glm::vec2> >::const_reverse_iterator p = pv.rbegin(); p != pv.rend(); p++){
for(std::vector< std::pair<Node *, glm::vec2> >::const_reverse_iterator p = pv.rbegin(); p != pv.rend(); ++p){
Source *s = Mixer::manager().findSource( p->first );
if (canSelect(s))
selection.push_back( s );

147
cmake/modules/FindGTK.cmake Normal file
View File

@@ -0,0 +1,147 @@
# - Try to find GTK+ 3.x or 4.x
#
# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
# Copyright (C) 2013, 2015, 2020 Igalia S.L.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#[=======================================================================[.rst:
FindGTK
-------
Find GTK headers and libraries.
Optional Components
^^^^^^^^^^^^^^^^^^^
The ``COMPONENTS`` (or ``OPTIONAL_COMPONENTS``) keyword can be passed to
``find_package()``, the following GTK components can be searched for:
- ``unix-print``
Imported Targets
^^^^^^^^^^^^^^^^
``GTK::GTK``
The GTK library, if found.
``GTK::UnixPrint``
The GTK unix-print library, if found.
Result Variables
^^^^^^^^^^^^^^^^
This will define the following variables in your project:
``GTK_FOUND``
true if (the requested version of) GTK is available.
``GTK_UNIX_PRINT_FOUND``
true if the ``unix-print`` component is available.
``GTK_4``
whether GTK 4 was detected
``GTK_3``
whether GTK 3 was detected
``GTK_VERSION``
the version of GTK.
``GTK_SUPPORTS_BROADWAY``
true if the Broadway target is built into GTK.
``GTK_SUPPORTS_QUARTZ``
true if the Quartz target is built into GTK.
``GTK_SUPPORTS_WAYLAND``
true if the Wayland target is built into GTK.
``GTK_SUPPORTS_WIN32``
true if the Windows target is built into GTK.
``GTK_SUPPORTS_X11``
true if the X11 target is built into GTK.
#]=======================================================================]
if (GTK_FIND_VERSION VERSION_LESS 3.90)
set(GTK_PC_MODULE "gtk+-3.0")
set(GTK_PC_UNIX_PRINT_MODULE "gtk+-unix-print-3.0")
set(GTK_4 FALSE)
set(GTK_3 TRUE)
else ()
set(GTK_PC_MODULE "gtk4")
set(GTK_PC_UNIX_PRINT_MODULE "gtk4-unix-print")
set(GTK_4 TRUE)
set(GTK_3 FALSE)
endif ()
find_package(PkgConfig QUIET)
pkg_check_modules(GTK IMPORTED_TARGET ${GTK_PC_MODULE})
set(GTK_VERSION_OK TRUE)
if (GTK_VERSION)
if (GTK_FIND_VERSION_EXACT)
if (NOT("${GTK_FIND_VERSION}" VERSION_EQUAL "${GTK_VERSION}"))
set(GTK_VERSION_OK FALSE)
endif ()
else ()
if ("${GTK_VERSION}" VERSION_LESS "${GTK_FIND_VERSION}")
set(GTK_VERSION_OK FALSE)
endif ()
endif ()
endif ()
# Set all the GTK_SUPPORTS_<target> variables to FALSE initially.
foreach (gtk_target broadway quartz wayland win32 x11)
string(TOUPPER "GTK_SUPPORTS_${gtk_target}" gtk_target)
set(${gtk_target} FALSE)
endforeach ()
if (GTK_VERSION AND GTK_VERSION_OK)
# Fetch the "targets" variable and set GTK_SUPPORTS_<target>.
pkg_get_variable(GTK_TARGETS ${GTK_PC_MODULE} targets)
separate_arguments(GTK_TARGETS)
foreach (gtk_target ${GTK_TARGETS})
string(TOUPPER "GTK_SUPPORTS_${gtk_target}" gtk_target)
set(${gtk_target} TRUE)
endforeach ()
endif ()
if (TARGET PkgConfig::GTK AND NOT TARGET GTK::GTK)
add_library(GTK::GTK INTERFACE IMPORTED GLOBAL)
set_property(TARGET GTK::GTK PROPERTY
INTERFACE_LINK_LIBRARIES PkgConfig::GTK
)
endif ()
# Try to find additional components
foreach (gtk_component ${GTK_FIND_COMPONENTS})
if (NOT "${gtk_component}" STREQUAL unix-print)
message(FATAL_ERROR "Invalid component name: ${gtk_component}")
endif ()
pkg_check_modules(GTK_UNIX_PRINT IMPORTED_TARGET "${GTK_PC_UNIX_PRINT_MODULE}")
if (GTK_FIND_REQUIRED_unix-print AND NOT GTK_UNIX_PRINT_FOUND)
message(FATAL_ERROR "Component unix-print not found")
endif ()
if (TARGET PkgConfig::GTK_UNIX_PRINT AND NOT TARGET GTK::UnixPrint)
add_library(GTK::UnixPrint INTERFACE IMPORTED GLOBAL)
set_property(TARGET GTK::UnixPrint PROPERTY
INTERFACE_LINK_LIBRARIES PkgConfig::GTK_UNIX_PRINT)
endif ()
endforeach ()
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GTK DEFAULT_MSG GTK_VERSION GTK_VERSION_OK)

View File

@@ -98,5 +98,6 @@
#define COLOR_SLIDER_CIRCLE 0.11f, 0.11f, 0.11f
#define COLOR_STASH_CIRCLE 0.06f, 0.06f, 0.06f
#define USE_GST_APPSINK_CALLBACKS
#endif // VMIX_DEFINES_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

View File

@@ -9,28 +9,26 @@ monitor or a projector, but can be streamed or recorded live (no audio).
![screenshot](vimix_0.2_beta.jpg)
Check the [Graphical User Manual](https://github.com/brunoherbelin/vimix/wiki/User-manual) for more details or this selection of [videos by Jean Detheux](https://vimeo.com/showcase/7871359).
Check the [Graphical User Manual](https://github.com/brunoherbelin/vimix/wiki/User-manual) or [demo videos](https://vimeo.com/vimix) to discover vimix.
Watch this selection of [videos by Jean Detheux](https://vimeo.com/showcase/7871359) to see what vimix can do.
## Install vimix
[Quick installation guide](https://github.com/brunoherbelin/vimix/wiki/Quick-Installation-Guide)
### Linux
[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-white.svg)](https://snapcraft.io/vimix)
snap install vimix
### Mac OSX
Download package from [Github Release](https://github.com/brunoherbelin/vimix/releases)
*NB: You'll need to accept the exception in OSX security preference.*
Download package from [Github Releases](https://github.com/brunoherbelin/vimix/releases) page.
### Windows
*Please consider helping by compiling & providing packages for Windows.*
## About
vimix is free and open source (GPL3).
@@ -45,5 +43,4 @@ vimix welcomes contributions and support: check the [wiki](https://github.com/br
[comment]: # (webpage hosted at https://brunoherbelin.github.io/vimix/)

View File

@@ -350,7 +350,7 @@ class SocketReceiveMultiplexer::Implementation{
}
public:
Implementation()
Implementation(): break_(false)
{
if( pipe(breakPipe_) != 0 )
throw std::runtime_error( "creation of asynchronous break pipes failed\n" );
@@ -479,7 +479,7 @@ public:
if( FD_ISSET( breakPipe_[0], &tempfds ) ){
// clear pending data from the asynchronous break pipe
char c;
read( breakPipe_[0], &c, 1 );
size_t n = read( breakPipe_[0], &c, 1 );
}
if( break_ )
@@ -534,7 +534,8 @@ public:
break_ = true;
// Send a termination message to the asynchronous break pipe, so select() will return
write( breakPipe_[1], "!", 1 );
if (write( breakPipe_[1], "!", 1 ) < 1)
throw std::runtime_error("AsynchronousBreak failed\n");
}
};

View File

@@ -225,8 +225,8 @@ std::ostream& operator<<( std::ostream & os, const ReceivedBundle& b )
for( ReceivedBundle::const_iterator i = b.ElementsBegin();
i != b.ElementsEnd(); ++i ){
if( i->IsBundle() ){
ReceivedBundle b(*i);
os << b << "\n";
ReceivedBundle rb(*i);
os << rb << "\n";
}else{
ReceivedMessage m(*i);
for( int j=0; j < indent; ++j )

View File

@@ -1,27 +1,8 @@
#include <stdio.h>
#include <iostream>
// standalone image loader
#include "stb_image.h"
// Opengl
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_access.hpp>
#include <glm/gtc/matrix_transform.hpp>
// GStreamer
#include <gst/gst.h>
#include <gst/gstbin.h>
#include <gst/gl/gl.h>
#include "GstToolkit.h"
// imgui
#include "imgui.h"
#include "ImGuiToolkit.h"
#include "ImGuiVisitor.h"
// vmix
#include "Settings.h"
@@ -97,9 +78,6 @@ int main(int argc, char *argv[])
gst_debug_set_active(FALSE);
#endif
// test text editor
// UserInterface::manager().fillShaderEditor( Resource::getText("shaders/image.fs") );
// draw the scene
Rendering::manager().pushFrontDrawCallback(drawScene);

Binary file not shown.

Binary file not shown.

View File

@@ -5,5 +5,4 @@ Comment=Live video mixing.
Categories=AudioVideo;Video;Graphics;Utility;
Icon=${SNAP}/meta/gui/vimix.svg
Exec=vimix
TryExec=vimix
Terminal=false

View File

@@ -1,6 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
@@ -15,11 +13,11 @@
height="64"
id="svg3744"
sodipodi:version="0.32"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
version="1.0"
sodipodi:docname="v-mix.svg"
sodipodi:docname="vimix.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
inkscape:export-filename="/home/bhbn/Developments/v-mix/rsc/images/v-mix_256x256.png"
inkscape:export-filename="/home/bhbn/Developments/v-mix/rsc/images/vimix_256x256.png"
inkscape:export-xdpi="405.16815"
inkscape:export-ydpi="405.16815">
<sodipodi:namedview
@@ -29,9 +27,9 @@
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="24.671299"
inkscape:cx="36.638352"
inkscape:cy="31.887987"
inkscape:zoom="22.627417"
inkscape:cx="35.082716"
inkscape:cy="31.811389"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:document-units="px"
@@ -41,16 +39,17 @@
showborder="true"
objecttolerance="1"
guidetolerance="10000"
inkscape:window-width="2618"
inkscape:window-width="2890"
inkscape:window-height="1885"
inkscape:window-x="920"
inkscape:window-y="160"
inkscape:window-x="817"
inkscape:window-y="89"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:showpageshadow="false">
inkscape:showpageshadow="false"
inkscape:document-rotation="0">
<inkscape:grid
type="xygrid"
id="grid3754"
@@ -169,7 +168,7 @@
xlink:href="#linearGradient3237-580"
id="radialGradient855"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(4.0147937,-0.11912026,0.11328765,3.8182126,-0.22461479,-46.392657)"
gradientTransform="matrix(4.0147937,-0.11912026,0.11328765,3.8182126,-0.22443818,-46.542095)"
cx="8"
cy="8"
fx="8"
@@ -192,16 +191,42 @@
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(-0.8,48.8)">
transform="translate(-0.8,48.8)"
style="opacity:0.89674699">
<ellipse
style="fill:#0c0c0c;fill-opacity:0.477137;stroke:none;stroke-width:0.862179;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path854"
cx="32.799999"
cy="-16.91748"
rx="27.470198"
ry="27.375973" />
<path
id="path4090"
d="m 32.8,-48.060406 c -17.255744,0 -31.2604062,14.004662 -31.2604062,31.260406 0,17.25574444 14.0046622,31.260406 31.2604062,31.260406 17.255744,0 31.260406,-14.00466156 31.260406,-31.260406 0,-17.255744 -14.004662,-31.260406 -31.260406,-31.260406 z m 0,3.907551 c 15.098776,0 27.352855,12.254079 27.352855,27.352855 0,15.0987764 -12.254079,27.352856 -27.352855,27.352856 -15.098776,0 -27.3528555,-12.2540796 -27.3528555,-27.352856 0,-15.098776 12.2540795,-27.352855 27.3528555,-27.352855 z"
style="display:inline;overflow:visible;visibility:visible;opacity:0.2;fill:#848484;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.34172344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.5999999;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
d="m 32.8,-48.060406 c -17.255744,0 -31.2604061,14.004662 -31.2604061,31.260406 0,17.25574393 14.0046621,31.260406 31.2604061,31.260406 17.255744,0 31.260406,-14.00466207 31.260406,-31.260406 0,-17.255744 -14.004662,-31.260406 -31.260406,-31.260406 z m 0,3.907551 c 15.098776,0 27.352855,12.254079 27.352855,27.352855 0,15.0987763 -12.254079,27.352856 -27.352855,27.352856 -15.098776,0 -27.3528552,-12.2540797 -27.3528552,-27.352856 0,-15.098776 12.2540792,-27.352855 27.3528552,-27.352855 z"
style="display:inline;overflow:visible;visibility:visible;opacity:0.2;fill:#848484;fill-opacity:0.654019;fill-rule:nonzero;stroke:none;stroke-width:4.34172;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.6;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;enable-background:accumulate"
inkscape:connector-curvature="0"
inkscape:export-xdpi="405"
inkscape:export-ydpi="405" />
<g
id="g858">
<path
id="path4096"
style="display:inline;overflow:visible;visibility:visible;opacity:0.801;fill:url(#radialGradient855);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:stroke fill markers;enable-background:accumulate"
d="m 36.688849,-42.590063 c -0.303933,2.108537 -2.081796,3.675579 -4.183594,3.6875 -2.080492,0.0106 -3.863017,-1.50665 -4.21289,-3.585937 -12.672805,2.236616 -21.7888326,13.427755 -21.4160161,26.291015 0.1430268,4.836336 1.636139,9.5362214 4.3105471,13.5683594 L 33.725958,-20.359594 v -1.535156 c 0,-3.229159 2.616539,-5.847657 5.845703,-5.847657 1.906945,0 3.585217,0.927726 4.652344,2.339844 h 1.195313 c 3.875281,0 7.017578,2.093968 7.017578,4.677734 l -7.017578,1.167969 v 5.847656 c 0,6.3779194 -4.259861,11.7530414 -10.085938,13.45898538 l 3.015625,8.15429682 c 0.05432,0.149079 0.106204,0.243515 0.205076,0.378518 C 50.521949,5.6477954 58.834499,-4.9395226 58.733771,-17.193579 58.61364,-29.921919 49.273879,-40.681635 36.688849,-42.590063 Z m 2.769317,19.244586 c -0.764292,0 -1.38415,0.619286 -1.38415,1.384151 0,0.764288 0.619858,1.385691 1.38415,1.385691 0.764291,0 1.385692,-0.621403 1.385692,-1.385691 0,-0.764865 -0.621402,-1.384151 -1.385692,-1.384151 z M 31.800177,0.30446838 c -0.138877,0.0044 -0.274456,0.01953 -0.414062,0.01953 h -2.855469 l 2.800781,7.58007782 c 0.138751,0.380792 0.336318,0.7630677 0.493205,1.0479047 0.763561,0.0573 0.707955,0.03676 1.473592,0.02631 0.559417,-0.01186 1.118303,-0.04182 1.675781,-0.08984 z m -14.0625,0.01953 -2.873047,1.43554602 c 3.582287,3.433627 8.074429,5.76748 12.943359,6.7246095 L 24.788458,0.32399838 Z"
sodipodi:nodetypes="ccccccsscsccscccccssssscsccccccccccc" />
</g>
<path
style="display:inline;overflow:visible;visibility:visible;opacity:0.80100002;fill:url(#radialGradient855);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.09425879;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.64077669;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:stroke fill markers;enable-background:accumulate"
d="m 32.810524,-42.734828 -1.015246,0.02097 A 25.934385,25.934385 0 0 0 6.8756139,-16.048973 25.934385,25.934385 0 0 0 11.186212,-2.4795029 L 33.725083,-20.21064 v -1.535455 c 0,-3.229159 2.61689,-5.846052 5.846054,-5.846052 1.906945,0 3.585382,0.926722 4.652509,2.33884 h 1.19564 c 3.875281,0 7.016522,2.093915 7.016522,4.677681 l -7.016522,1.168373 v 5.848149 c 0,6.3779185 -4.259255,11.7523532 -10.085332,13.45829656 l 3.014273,8.15342884 c 0.05432,0.1490791 0.05251,0.2989217 0.02726,0.444695 A 25.934385,25.934385 0 0 0 58.732847,-17.043242 25.934385,25.934385 0 0 0 32.810524,-42.734828 Z m 6.760613,19.235127 c -0.968462,0 -1.753607,0.78442 -1.753607,1.753606 0,0.968457 0.785146,1.755704 1.753607,1.755704 0.96846,0 1.755703,-0.787247 1.755703,-1.755704 0,-0.969186 -0.787244,-1.753606 -1.755703,-1.753606 z M 31.799473,0.45296287 c -0.138877,0.004393 -0.273623,0.0209759 -0.413229,0.0209759 h -2.854855 l 2.800315,7.57868263 c 0.138751,0.3807921 -0.01302,0.7823496 -0.329324,1.0047577 a 25.934385,25.934385 0 0 0 2.294791,0.071319 25.934385,25.934385 0 0 0 1.675994,-0.090197 z M 17.737064,0.47393875 14.865429,1.9087078 A 25.934385,25.934385 0 0 0 27.807711,8.6336612 L 24.789245,0.47393875 Z"
id="path4096"
inkscape:connector-curvature="0" />
style="opacity:0.809083;fill:#888888;fill-opacity:1;stroke:#ffffff;stroke-width:0.5;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path859"
sodipodi:type="arc"
sodipodi:cx="32.47654"
sodipodi:cy="-43.181934"
sodipodi:rx="2.0615652"
sodipodi:ry="2.0901108"
sodipodi:start="0"
sodipodi:end="6.2616827"
sodipodi:arc-type="chord"
d="m 34.538105,-43.181934 a 2.0615652,2.0901108 0 0 1 -2.050483,2.09008 2.0615652,2.0901108 0 0 1 -2.072528,-2.067609 2.0615652,2.0901108 0 0 1 2.0282,-2.11231 2.0615652,2.0901108 0 0 1 2.094334,2.044899 z"
sodipodi:open="true" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 322 KiB

After

Width:  |  Height:  |  Size: 324 KiB

View File

@@ -17,7 +17,7 @@ confinement: strict # use 'strict' once you have the right plugs and slots
apps:
vimix:
extensions: [gnome-3-34]
extensions: [gnome-3-28]
command: bin/vimix
adapter: full
plugs:
@@ -37,15 +37,18 @@ apps:
GST_PLUGIN_SYSTEM_PATH : $SNAP/usr/lib/x86_64-linux-gnu/gstreamer-1.0
GST_PLUGIN_SCANNER: $SNAP/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner
parts:
vimix-binary:
plugin: cmake
source: .
configflags:
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
- -DCMAKE_INSTALL_PREFIX=/
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
build-packages:
- g++
- make
- git
- libpng-dev
- libxrandr-dev
- libglfw3-dev
@@ -53,7 +56,6 @@ parts:
- libgstreamer-plugins-base1.0-dev
- libicu-dev
stage-packages:
- zenity
- libpng16-16
- libglu1-mesa
- freeglut3
@@ -69,12 +71,6 @@ parts:
- gstreamer1.0-plugins-ugly
- libgpm2
- libslang2
stage-snaps:
- zenity-integration

View File

@@ -25,27 +25,27 @@ XMLElement *tinyxml2::XMLElementFromGLM(XMLDocument *doc, glm::ivec2 vector)
XMLElement *tinyxml2::XMLElementFromGLM(XMLDocument *doc, glm::vec2 vector)
{
XMLElement *newelement = doc->NewElement( "vec2" );
newelement->SetAttribute("x", vector.x);
newelement->SetAttribute("y", vector.y);
newelement->SetAttribute("x", (float) vector.x);
newelement->SetAttribute("y", (float) vector.y);
return newelement;
}
XMLElement *tinyxml2::XMLElementFromGLM(XMLDocument *doc, glm::vec3 vector)
{
XMLElement *newelement = doc->NewElement( "vec3" );
newelement->SetAttribute("x", vector.x);
newelement->SetAttribute("y", vector.y);
newelement->SetAttribute("z", vector.z);
newelement->SetAttribute("x", (float) vector.x);
newelement->SetAttribute("y", (float) vector.y);
newelement->SetAttribute("z", (float) vector.z);
return newelement;
}
XMLElement *tinyxml2::XMLElementFromGLM(XMLDocument *doc, glm::vec4 vector)
{
XMLElement *newelement = doc->NewElement( "vec4" );
newelement->SetAttribute("x", vector.x);
newelement->SetAttribute("y", vector.y);
newelement->SetAttribute("z", vector.z);
newelement->SetAttribute("w", vector.w);
newelement->SetAttribute("x", (float) vector.x);
newelement->SetAttribute("y", (float) vector.y);
newelement->SetAttribute("z", (float) vector.z);
newelement->SetAttribute("w", (float) vector.w);
return newelement;
}
@@ -129,7 +129,7 @@ XMLElement *tinyxml2::XMLElementEncodeArray(XMLDocument *doc, const void *array,
gchar *compressed_array = g_new(gchar, compressed_size);
// encoded string will hold the base64 encoding of the array
const gchar *encoded_array = nullptr;
gchar *encoded_array = nullptr;
// zlib compress ((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen));
if ( Z_OK == compress((Bytef *)compressed_array, &compressed_size,
@@ -153,6 +153,7 @@ XMLElement *tinyxml2::XMLElementEncodeArray(XMLDocument *doc, const void *array,
// free temporary array
g_free(compressed_array);
g_free(encoded_array);
return newelement;
}
@@ -237,12 +238,13 @@ bool tinyxml2::XMLSaveDoc(XMLDocument * const doc, std::string filename)
return !XMLResultError(eResult);
}
bool tinyxml2::XMLResultError(int result)
bool tinyxml2::XMLResultError(int result, bool verbose)
{
XMLError xmlresult = (XMLError) result;
if ( xmlresult != XML_SUCCESS)
{
Log::Info("XML error %i: %s", result, tinyxml2::XMLDocument::ErrorIDToName(xmlresult));
if (verbose)
Log::Info("XML error %i: %s", result, tinyxml2::XMLDocument::ErrorIDToName(xmlresult));
return true;
}
return false;

View File

@@ -29,7 +29,7 @@ XMLElement *XMLElementEncodeArray(XMLDocument *doc, const void *array, uint arra
bool XMLElementDecodeArray(XMLElement *elem, void *array, uint arraysize);
bool XMLSaveDoc(tinyxml2::XMLDocument * const doc, std::string filename);
bool XMLResultError(int result);
bool XMLResultError(int result, bool verbose = true);
}