Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d45ab1d20 | ||
|
|
ae1c3d85ab | ||
|
|
c22df2eb2a | ||
|
|
d3a130d9ba | ||
|
|
8a57b52fcc | ||
|
|
dbc9803f9e | ||
|
|
3e376eb166 | ||
|
|
12aca05aef | ||
|
|
ce1de27618 | ||
|
|
66d5148e3a | ||
|
|
d2b4a825eb | ||
|
|
f443720319 | ||
|
|
b4627a1613 | ||
|
|
ceea9c10d5 | ||
|
|
a143360497 | ||
|
|
163757cb69 | ||
|
|
4537b986ca | ||
|
|
aafac2a7a8 | ||
|
|
7344258b2f | ||
|
|
c59994b7e5 | ||
|
|
b089f59e2a | ||
|
|
649d2b7ef7 | ||
|
|
8bef575e8b | ||
|
|
559a036e6d | ||
|
|
0b845591f9 | ||
|
|
a8ef68ed59 | ||
|
|
7293b8b9dd | ||
|
|
8eef5465c9 | ||
|
|
c08cb95dc1 | ||
|
|
010166e7b0 | ||
|
|
4c7fb94616 | ||
|
|
46f486a367 | ||
|
|
ea195dcf11 | ||
|
|
e6979acded | ||
|
|
43b4fc81b9 | ||
|
|
d4ce6ebee6 | ||
|
|
1c9a5ece83 | ||
|
|
8a75664264 | ||
|
|
6687bdd258 | ||
|
|
e525ecad36 | ||
|
|
e8b5dc0649 | ||
|
|
3a0f96c1ec | ||
|
|
e4c06ba1bb | ||
|
|
bc4eadfd08 | ||
|
|
ee2ce3802f | ||
|
|
43d44634f7 | ||
|
|
41bd7fc958 | ||
|
|
1e458a642c | ||
|
|
b255e41091 | ||
|
|
bc22832ad6 | ||
|
|
f59ac505b7 | ||
|
|
e15b962221 | ||
|
|
28d4d4acc4 | ||
|
|
5f800f3723 | ||
|
|
2537ca03c8 | ||
|
|
1860402452 | ||
|
|
bec9385c68 | ||
|
|
798139e097 | ||
|
|
6683d76222 | ||
|
|
6d2d16b644 |
@@ -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;
|
||||
|
||||
110
CMakeLists.txt
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ private:
|
||||
void updateSelectionOverlay() override;
|
||||
|
||||
float aspect_ratio;
|
||||
Mesh *persp_layer_;
|
||||
Mesh *persp_left_, *persp_right_;
|
||||
Group *frame_;
|
||||
|
||||
|
||||
21
Log.cpp
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
188
MediaPlayer.cpp
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
9
Mesh.cpp
@@ -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
@@ -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;
|
||||
|
||||
80
Mixer.cpp
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
2
Mixer.h
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
28
README.md
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
15
Resource.cpp
@@ -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;
|
||||
|
||||
|
||||
22
Scene.cpp
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
uint size () const;
|
||||
|
||||
// extract
|
||||
std::string xml() const;
|
||||
std::string clipboard() const;
|
||||
SourceList getCopy() const;
|
||||
|
||||
protected:
|
||||
|
||||
14
Session.cpp
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() ){
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
21
Settings.cpp
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
10
Shader.cpp
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
34
Source.cpp
@@ -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)
|
||||
|
||||
4
Source.h
@@ -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;
|
||||
|
||||
@@ -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++)
|
||||
|
||||
12
SourceList.h
@@ -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
|
||||
|
||||
43
Stream.cpp
@@ -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)
|
||||
|
||||
1
Stream.h
@@ -169,6 +169,7 @@ protected:
|
||||
status = INVALID;
|
||||
position = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
void unmap();
|
||||
};
|
||||
Frame frame_[N_FRAME];
|
||||
guint write_index_;
|
||||
|
||||
36
Streamer.cpp
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
12
Timeline.cpp
@@ -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]);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +21,7 @@ public:
|
||||
void attach(SessionFileSource *ts);
|
||||
Session *detach();
|
||||
void play(bool open);
|
||||
void open();
|
||||
|
||||
private:
|
||||
Surface *output_surface_;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
2
View.cpp
@@ -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
@@ -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)
|
||||
@@ -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
|
||||
|
||||
BIN
docs/images/vimix osx dmg license.png
Normal file
|
After Width: | Height: | Size: 311 KiB |
BIN
docs/images/vimix osx dmg.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 43 KiB |
BIN
docs/images/vimix ubuntu snap.png
Normal file
|
After Width: | Height: | Size: 268 KiB |
@@ -9,28 +9,26 @@ monitor or a projector, but can be streamed or recorded live (no audio).
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||
[](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/)
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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 )
|
||||
|
||||
22
main.cpp
@@ -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);
|
||||
|
||||
|
||||
BIN
osx/vimix.icns
@@ -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
|
||||
|
||||
@@ -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 |
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||