mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-12 10:49:59 +01:00
Complete refactoring
This commit is contained in:
@@ -198,16 +198,16 @@ include_directories(
|
|||||||
set(VMIX_BINARY "vmix")
|
set(VMIX_BINARY "vmix")
|
||||||
set(VMIX_SRCS
|
set(VMIX_SRCS
|
||||||
main.cpp
|
main.cpp
|
||||||
ShaderManager.cpp
|
Log.cpp
|
||||||
RenderingManager.cpp
|
Shader.cpp
|
||||||
SettingsManager.cpp
|
Settings.cpp
|
||||||
ResourceManager.cpp
|
Resource.cpp
|
||||||
UserInterfaceManager.cpp
|
|
||||||
FileDialog.cpp
|
FileDialog.cpp
|
||||||
ImGuiToolkit.cpp
|
ImGuiToolkit.cpp
|
||||||
GstToolkit.cpp
|
GstToolkit.cpp
|
||||||
MediaPlayer.cpp
|
MediaPlayer.cpp
|
||||||
Log.cpp
|
RenderingManager.cpp
|
||||||
|
UserInterfaceManager.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(VMIX_RSC_FILES
|
set(VMIX_RSC_FILES
|
||||||
@@ -221,7 +221,7 @@ set(VMIX_RSC_FILES
|
|||||||
./rsc/fonts/Roboto-Italic.ttf
|
./rsc/fonts/Roboto-Italic.ttf
|
||||||
./rsc/fonts/fa-regular-400.ttf
|
./rsc/fonts/fa-regular-400.ttf
|
||||||
./rsc/fonts/fa-solid-900.ttf
|
./rsc/fonts/fa-solid-900.ttf
|
||||||
./rsc/images/glmixer_256x256.png
|
./rsc/images/v-mix_256x256.png
|
||||||
./rsc/images/icons.dds
|
./rsc/images/icons.dds
|
||||||
./rsc/images/seed_512.jpg
|
./rsc/images/seed_512.jpg
|
||||||
)
|
)
|
||||||
|
|||||||
370
ImGuiToolkit.cpp
370
ImGuiToolkit.cpp
@@ -2,17 +2,22 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include <windows.h>
|
||||||
|
# include <shellapi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
#endif
|
#endif
|
||||||
#include "imgui_internal.h"
|
#include "imgui_internal.h"
|
||||||
|
|
||||||
#define MILISECOND 1000000##L
|
#define MILISECOND 1000000L
|
||||||
#define SECOND 1000000000##L
|
#define SECOND 1000000000L
|
||||||
#define MINUTE 60000000000##L
|
#define MINUTE 60000000000L
|
||||||
|
|
||||||
#include "ResourceManager.h"
|
#include "Resource.h"
|
||||||
#include "ImGuiToolkit.h"
|
#include "ImGuiToolkit.h"
|
||||||
#include "GstToolkit.h"
|
#include "GstToolkit.h"
|
||||||
|
|
||||||
@@ -30,6 +35,22 @@ std::string ImGuiToolkit::DateTime(){
|
|||||||
return datetime;
|
return datetime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ImGuiToolkit::OpenWebpage( const char* url )
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
ShellExecuteA( nullptr, nullptr, url, nullptr, nullptr, 0 );
|
||||||
|
#elif defined __APPLE__
|
||||||
|
char buf[1024];
|
||||||
|
sprintf( buf, "open %s", url );
|
||||||
|
system( buf );
|
||||||
|
#else
|
||||||
|
char buf[1024];
|
||||||
|
sprintf( buf, "xdg-open %s", url );
|
||||||
|
system( buf );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void ImGuiToolkit::Icon(int i, int j)
|
void ImGuiToolkit::Icon(int i, int j)
|
||||||
{
|
{
|
||||||
// icons.dds is a 20 x 20 grid of icons
|
// icons.dds is a 20 x 20 grid of icons
|
||||||
@@ -68,8 +89,6 @@ void ImGuiToolkit::ShowIconsWindow(bool* p_open)
|
|||||||
|
|
||||||
ImGui::Begin("Icons", p_open);
|
ImGui::Begin("Icons", p_open);
|
||||||
|
|
||||||
// ImGui::Text(" 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19");
|
|
||||||
|
|
||||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||||
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(640, 640));
|
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(640, 640));
|
||||||
if (ImGui::IsItemHovered())
|
if (ImGui::IsItemHovered())
|
||||||
@@ -95,104 +114,135 @@ void ImGuiToolkit::ShowIconsWindow(bool* p_open)
|
|||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 begin, guint64 end, guint64 duration, guint64 step)
|
|
||||||
|
// Draws a timeline showing
|
||||||
|
// 1) a cursor at position *time in the range [0 duration]
|
||||||
|
// 2) a line of tick marks indicating time, every step if possible
|
||||||
|
// 3) a slider handle below the cursor: user can move the slider
|
||||||
|
// Behavior
|
||||||
|
// a) Returns TRUE if the left mouse button LMB is pressed over the timeline
|
||||||
|
// b) the value of *time is changed to the position of the slider handle from user input (LMB)
|
||||||
|
|
||||||
|
bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 duration, guint64 step)
|
||||||
{
|
{
|
||||||
static guint64 optimal_tick_marks[] = { 100 * MILISECOND, 500 * MILISECOND, 1 * SECOND, 2 * SECOND, 5 * SECOND, 10 * SECOND, 20 * SECOND, 1 * MINUTE, 2 * MINUTE, 5 * MINUTE, 10 * MINUTE, 60 * MINUTE };
|
static guint64 optimal_tick_marks[12] = { 100 * MILISECOND, 500 * MILISECOND, 1 * SECOND, 2 * SECOND, 5 * SECOND, 10 * SECOND, 20 * SECOND, 1 * MINUTE, 2 * MINUTE, 5 * MINUTE, 10 * MINUTE, 60 * MINUTE };
|
||||||
|
|
||||||
|
|
||||||
bool value_return = false;
|
|
||||||
|
|
||||||
// get window
|
// get window
|
||||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||||
if (window->SkipItems) {
|
if (window->SkipItems)
|
||||||
return value_return;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// get style
|
// get style & id
|
||||||
const ImGuiStyle& style = ImGui::GetStyle();
|
const ImGuiStyle& style = ImGui::GetStyle();
|
||||||
const float fontsize = ImGui::GetFontSize();
|
const float fontsize = ImGui::GetFontSize();
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *GImGui;
|
||||||
const ImGuiID id = window->GetID(label);
|
const ImGuiID id = window->GetID(label);
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIRST PREPARE ALL data structures
|
||||||
|
//
|
||||||
|
|
||||||
// widget bounding box
|
// widget bounding box
|
||||||
|
const float height = 2.f * (fontsize + style.FramePadding.y);
|
||||||
ImVec2 pos = window->DC.CursorPos;
|
ImVec2 pos = window->DC.CursorPos;
|
||||||
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), (fontsize + style.FramePadding.y)* 2.0f);
|
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), height);
|
||||||
ImRect bbox(pos, pos + size);
|
ImRect bbox(pos, pos + size);
|
||||||
ImGui::ItemSize(size, style.FramePadding.y);
|
ImGui::ItemSize(size, style.FramePadding.y);
|
||||||
if (!ImGui::ItemAdd(bbox, id))
|
if (!ImGui::ItemAdd(bbox, id))
|
||||||
return value_return;
|
return false;
|
||||||
|
|
||||||
// cursor size
|
// cursor size
|
||||||
const float cursor_scale = 1.f;
|
const float cursor_scale = 1.f;
|
||||||
const float cursor_width = window->DrawList->_Data->FontSize * cursor_scale;
|
const float cursor_width = 0.5f * fontsize * cursor_scale;
|
||||||
|
|
||||||
// user input
|
// TIMELINE is inside the bbox, in a slightly smaller bounding box
|
||||||
|
ImRect timeline_bbox(bbox);
|
||||||
|
timeline_bbox.Expand( ImVec2(-cursor_width, -style.FramePadding.y) );
|
||||||
|
|
||||||
|
// SLIDER is inside the timeline
|
||||||
|
ImRect slider_bbox( timeline_bbox.GetTL() + ImVec2(-cursor_width + 2.f, cursor_width + 4.f ), timeline_bbox.GetBR() + ImVec2( cursor_width - 2.f, 0.f ) );
|
||||||
|
|
||||||
|
// units conversion: from time to float (calculation made with higher precision first)
|
||||||
|
float time_ = static_cast<float> ( static_cast<double>(*time) / static_cast<double>(duration) );
|
||||||
|
float step_ = static_cast<float> ( static_cast<double>(step) / static_cast<double>(duration) );
|
||||||
|
|
||||||
|
//
|
||||||
|
// SECOND GET USER INPUT AND PERFORM CHANGES AND DECISIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
// read user input from system
|
||||||
|
bool left_mouse_press = false;
|
||||||
const bool hovered = ImGui::ItemHoverable(bbox, id);
|
const bool hovered = ImGui::ItemHoverable(bbox, id);
|
||||||
bool temp_input_is_active = ImGui::TempInputIsActive(id);
|
bool temp_input_is_active = ImGui::TempInputIsActive(id);
|
||||||
if (!temp_input_is_active)
|
if (!temp_input_is_active)
|
||||||
{
|
{
|
||||||
const bool focus_requested = ImGui::FocusableItemRegister(window, id);
|
const bool focus_requested = ImGui::FocusableItemRegister(window, id);
|
||||||
const bool clicked = (hovered && g.IO.MouseClicked[0]);
|
left_mouse_press = hovered && ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
||||||
if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id)
|
if (focus_requested || left_mouse_press || g.NavActivateId == id || g.NavInputId == id)
|
||||||
{
|
{
|
||||||
ImGui::SetActiveID(id, window);
|
ImGui::SetActiveID(id, window);
|
||||||
ImGui::SetFocusID(id, window);
|
ImGui::SetFocusID(id, window);
|
||||||
ImGui::FocusWindow(window);
|
ImGui::FocusWindow(window);
|
||||||
}
|
}
|
||||||
value_return = hovered && (clicked || g.IO.MouseDownDuration[0] > 0.f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render bounding box
|
// time Slider behavior
|
||||||
|
ImRect grab_slider_bb;
|
||||||
|
ImU32 grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrab);
|
||||||
|
float time_slider = time_ * 10.f; // x 10 precision on grab
|
||||||
|
float time_zero = 0.f;
|
||||||
|
float time_end = 10.f;
|
||||||
|
bool value_changed = ImGui::SliderBehavior(slider_bbox, id, ImGuiDataType_Float, &time_slider, &time_zero,
|
||||||
|
&time_end, "%.2f", 1.f, ImGuiSliderFlags_None, &grab_slider_bb);
|
||||||
|
if (value_changed){
|
||||||
|
// g_print("slider %f %ld \n", time_slider, static_cast<guint64> ( static_cast<double>(time_slider) * static_cast<double>(duration) ));
|
||||||
|
*time = static_cast<guint64> ( 0.1 * static_cast<double>(time_slider) * static_cast<double>(duration) );
|
||||||
|
grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// THIRD RENDER
|
||||||
|
//
|
||||||
|
|
||||||
|
// Render the bounding box
|
||||||
const ImU32 frame_col = ImGui::GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
|
const ImU32 frame_col = ImGui::GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
|
||||||
ImGui::RenderFrame(bbox.Min, bbox.Max, frame_col, true, style.FrameRounding);
|
ImGui::RenderFrame(bbox.Min, bbox.Max, frame_col, true, style.FrameRounding);
|
||||||
|
|
||||||
// draw inside a slightly smaller bounding box
|
|
||||||
ImRect timeline_bbox(bbox);
|
|
||||||
timeline_bbox.Expand(ImVec2(-cursor_width, 0.f));
|
|
||||||
|
|
||||||
// units conversion: from time to float (calculation made with higher precision first)
|
|
||||||
float time_ = static_cast<float> ( static_cast<double>(*time) / static_cast<double>(duration) );
|
|
||||||
float begin_ = static_cast<float> ( static_cast<double>(begin) / static_cast<double>(duration) );
|
|
||||||
float end_ = static_cast<float> ( static_cast<double>(end) / static_cast<double>(duration) );
|
|
||||||
float step_ = static_cast<float> ( static_cast<double>(step) / static_cast<double>(duration) );
|
|
||||||
|
|
||||||
// by default, put a tick mark at every frame step and a large mark every second
|
// by default, put a tick mark at every frame step and a large mark every second
|
||||||
guint64 tick_step = step;
|
guint64 tick_step = step;
|
||||||
guint64 large_tick_step = SECOND;
|
guint64 large_tick_step = SECOND;
|
||||||
|
|
||||||
// how many pixels to represent one frame step?
|
// how many pixels to represent one frame step?
|
||||||
float tick_step_pixels = timeline_bbox.GetWidth() * step_;
|
float tick_step_pixels = timeline_bbox.GetWidth() * step_;
|
||||||
// while there is less than 3 pixels between two tick marks (or at last optimal tick mark)
|
// while there is less than 3 pixels between two tick marks (or at last optimal tick mark)
|
||||||
for ( int i=0; i<10 && tick_step_pixels < 3.f; ++i )
|
for ( int i=0; i<10 && tick_step_pixels < 3.f; ++i )
|
||||||
{
|
{
|
||||||
|
// try to use the optimal tick marks pre-defined
|
||||||
tick_step = optimal_tick_marks[i];
|
tick_step = optimal_tick_marks[i];
|
||||||
large_tick_step = optimal_tick_marks[i+1];
|
large_tick_step = optimal_tick_marks[i+1];
|
||||||
tick_step_pixels = timeline_bbox.GetWidth() * static_cast<float> ( static_cast<double>(tick_step) / static_cast<double>(duration) );
|
tick_step_pixels = timeline_bbox.GetWidth() * static_cast<float> ( static_cast<double>(tick_step) / static_cast<double>(duration) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// static guint64 t = 0;
|
// render the tick marks along TIMELINE
|
||||||
// if (t != *time) {
|
ImU32 color = ImGui::GetColorU32( style.Colors[ImGuiCol_Text] );
|
||||||
// t = *time;
|
|
||||||
// ImGuiToolkit::Log("tic %ld %ld %ld %ld \n", duration, step, tick_step, large_tick_step);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// tick marks along timeline
|
|
||||||
ImU32 color = ImGui::GetColorU32( style.Colors[ImGuiCol_PlotLines] );
|
|
||||||
pos = timeline_bbox.GetTL();
|
pos = timeline_bbox.GetTL();
|
||||||
guint64 tick = 0;
|
guint64 tick = 0;
|
||||||
|
float tick_percent = 0.f;
|
||||||
while ( tick < duration)
|
while ( tick < duration)
|
||||||
{
|
{
|
||||||
// large tick mark every large tick
|
// large tick mark every large tick
|
||||||
float tick_length = !(tick%large_tick_step) ? fontsize : style.FramePadding.y;
|
float tick_length = !(tick%large_tick_step) ? fontsize : style.FramePadding.y;
|
||||||
|
|
||||||
// draw a tick mark each step
|
// draw a tick mark each step
|
||||||
window->DrawList->AddLine( pos, pos + ImVec2(0.f, tick_length), color);
|
window->DrawList->AddLine( pos, pos + ImVec2(0.f, tick_length), color);
|
||||||
|
|
||||||
// next tick
|
// next tick
|
||||||
tick += tick_step;
|
tick += tick_step;
|
||||||
float tick_percent = static_cast<float> ( static_cast<double>(tick) / static_cast<double>(duration) );
|
tick_percent = static_cast<float> ( static_cast<double>(tick) / static_cast<double>(duration) );
|
||||||
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
|
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
|
||||||
}
|
}
|
||||||
// end
|
|
||||||
|
// tick EOF
|
||||||
window->DrawList->AddLine( timeline_bbox.GetTR(), timeline_bbox.GetTR() + ImVec2(0.f, fontsize), color);
|
window->DrawList->AddLine( timeline_bbox.GetTR(), timeline_bbox.GetTR() + ImVec2(0.f, fontsize), color);
|
||||||
|
|
||||||
// render text : duration and current time
|
// render text : duration and current time
|
||||||
@@ -210,32 +260,222 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 begi
|
|||||||
if (overlay_size.x > 0.0f)
|
if (overlay_size.x > 0.0f)
|
||||||
ImGui::RenderTextClipped( bbox.GetBL() + overlay_size, bbox.Max, overlay_buf, NULL, &overlay_size);
|
ImGui::RenderTextClipped( bbox.GetBL() + overlay_size, bbox.Max, overlay_buf, NULL, &overlay_size);
|
||||||
|
|
||||||
|
// draw slider grab handle
|
||||||
// Slider behavior
|
if (grab_slider_bb.Max.x > grab_slider_bb.Min.x) {
|
||||||
ImRect grab_bb;
|
window->DrawList->AddRectFilled(grab_slider_bb.Min, grab_slider_bb.Max, grab_slider_color, style.GrabRounding);
|
||||||
ImRect slider_bbox( timeline_bbox.GetTL() + ImVec2(-cursor_width * 0.5f, cursor_width ), timeline_bbox.GetBR() + ImVec2( cursor_width * 0.5f, 0.f ) );
|
|
||||||
float time_slider = time_;
|
|
||||||
float time_zero = 0.f;
|
|
||||||
float time_end = 1.f;
|
|
||||||
bool value_changed = ImGui::SliderBehavior(slider_bbox, id, ImGuiDataType_Float, &time_slider, &time_zero,
|
|
||||||
&time_end, "%.2f", 1.f, ImGuiSliderFlags_None, &grab_bb);
|
|
||||||
if (value_changed){
|
|
||||||
// g_print("slider %f %ld \n", time_slider, static_cast<guint64> ( static_cast<double>(time_slider) * static_cast<double>(duration) ));
|
|
||||||
*time = static_cast<guint64> ( static_cast<double>(time_slider) * static_cast<double>(duration) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render grab
|
|
||||||
if (grab_bb.Max.x > grab_bb.Min.x)
|
|
||||||
window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max,
|
|
||||||
ImGui::GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab),
|
|
||||||
style.GrabRounding);
|
|
||||||
|
|
||||||
// last: draw the cursor
|
// draw the cursor
|
||||||
color = ImGui::GetColorU32(style.Colors[ImGuiCol_SliderGrab]);
|
color = ImGui::GetColorU32(style.Colors[ImGuiCol_SliderGrab]);
|
||||||
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width * 0.5f, 0.f);
|
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width, 2.f);
|
||||||
ImGui::RenderArrow(window->DrawList, pos, color, ImGuiDir_Up, cursor_scale);
|
ImGui::RenderArrow(window->DrawList, pos, color, ImGuiDir_Up, cursor_scale);
|
||||||
|
|
||||||
return value_return;
|
return left_mouse_press;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draws a timeline showing
|
||||||
|
// 1) a cursor at position *time in the range [0 duration]
|
||||||
|
// 2) a line of tick marks indicating time, every step if possible
|
||||||
|
// 3) a slider handle below the cursor: user can move the slider
|
||||||
|
// 4) different blocs for each time segment [first second] of the list of segments
|
||||||
|
// Behavior
|
||||||
|
// a) Returns TRUE if the left mouse button LMB is pressed over the timeline
|
||||||
|
// b) the value of *time is changed to the position of the slider handle from user input (LMB)
|
||||||
|
// c) the list segments is replaced with the list of new segments created by user (empty list otherwise)
|
||||||
|
|
||||||
|
bool ImGuiToolkit::TimelineSliderEdit(const char* label, guint64 *time, guint64 duration, guint64 step,
|
||||||
|
std::list<std::pair<guint64, guint64> >& segments)
|
||||||
|
{
|
||||||
|
static guint64 optimal_tick_marks[12] = { 100 * MILISECOND, 500 * MILISECOND, 1 * SECOND, 2 * SECOND, 5 * SECOND, 10 * SECOND, 20 * SECOND, 1 * MINUTE, 2 * MINUTE, 5 * MINUTE, 10 * MINUTE, 60 * MINUTE };
|
||||||
|
|
||||||
|
// get window
|
||||||
|
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||||
|
if (window->SkipItems)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// get style & id
|
||||||
|
const ImGuiStyle& style = ImGui::GetStyle();
|
||||||
|
const float fontsize = ImGui::GetFontSize();
|
||||||
|
ImGuiContext& g = *GImGui;
|
||||||
|
const ImGuiID id = window->GetID(label);
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIRST PREPARE ALL data structures
|
||||||
|
//
|
||||||
|
|
||||||
|
// widget bounding box
|
||||||
|
const float height = 2.f * (fontsize + style.FramePadding.y);
|
||||||
|
ImVec2 pos = window->DC.CursorPos;
|
||||||
|
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), height);
|
||||||
|
ImRect bbox(pos, pos + size);
|
||||||
|
ImGui::ItemSize(size, style.FramePadding.y);
|
||||||
|
if (!ImGui::ItemAdd(bbox, id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// cursor size
|
||||||
|
const float cursor_scale = 1.f;
|
||||||
|
const float cursor_width = 0.5f * fontsize * cursor_scale;
|
||||||
|
|
||||||
|
// TIMELINE is inside the bbox, in a slightly smaller bounding box
|
||||||
|
ImRect timeline_bbox(bbox);
|
||||||
|
timeline_bbox.Expand( ImVec2(-cursor_width, -style.FramePadding.y) );
|
||||||
|
|
||||||
|
// SLIDER is inside the timeline
|
||||||
|
ImRect slider_bbox( timeline_bbox.GetTL() + ImVec2(-cursor_width + 2.f, cursor_width + 4.f ), timeline_bbox.GetBR() + ImVec2( cursor_width - 2.f, 0.f ) );
|
||||||
|
|
||||||
|
// units conversion: from time to float (calculation made with higher precision first)
|
||||||
|
float time_ = static_cast<float> ( static_cast<double>(*time) / static_cast<double>(duration) );
|
||||||
|
float step_ = static_cast<float> ( static_cast<double>(step) / static_cast<double>(duration) );
|
||||||
|
|
||||||
|
//
|
||||||
|
// SECOND GET USER INPUT AND PERFORM CHANGES AND DECISIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
// read user input from system
|
||||||
|
bool left_mouse_press = false;
|
||||||
|
bool right_mouse_press = false;
|
||||||
|
const bool hovered = ImGui::ItemHoverable(bbox, id);
|
||||||
|
bool temp_input_is_active = ImGui::TempInputIsActive(id);
|
||||||
|
if (!temp_input_is_active)
|
||||||
|
{
|
||||||
|
const bool focus_requested = ImGui::FocusableItemRegister(window, id);
|
||||||
|
left_mouse_press = hovered && ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
||||||
|
right_mouse_press = hovered && ImGui::IsMouseDown(ImGuiMouseButton_Right);
|
||||||
|
if (focus_requested || left_mouse_press || right_mouse_press || g.NavActivateId == id || g.NavInputId == id)
|
||||||
|
{
|
||||||
|
ImGui::SetActiveID(id, window);
|
||||||
|
ImGui::SetFocusID(id, window);
|
||||||
|
ImGui::FocusWindow(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// active slider if inside bb
|
||||||
|
ImRect grab_slider_bb;
|
||||||
|
ImU32 grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrab);
|
||||||
|
ImGuiID slider_id = window->GetID("werwerwsdvcsdgfdghdfsgagewrgsvdfhfdghkjfghsdgsdtgewfszdvgfkjfg"); // FIXME: cleaner way to create a valid but useless id that is never active
|
||||||
|
if ( slider_bbox.Contains(g.IO.MousePos) ) {
|
||||||
|
slider_id = id; // active id
|
||||||
|
grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
// time Slider behavior
|
||||||
|
float time_slider = time_ * 10.f; // x 10 precision on grab
|
||||||
|
float time_zero = 0.f;
|
||||||
|
float time_end = 10.f;
|
||||||
|
bool value_changed = ImGui::SliderBehavior(slider_bbox, slider_id, ImGuiDataType_Float, &time_slider, &time_zero,
|
||||||
|
&time_end, "%.2f", 1.f, ImGuiSliderFlags_None, &grab_slider_bb);
|
||||||
|
if (value_changed){
|
||||||
|
// g_print("slider %f %ld \n", time_slider, static_cast<guint64> ( static_cast<double>(time_slider) * static_cast<double>(duration) ));
|
||||||
|
*time = static_cast<guint64> ( 0.1 * static_cast<double>(time_slider) * static_cast<double>(duration) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// segments behavior
|
||||||
|
float time_segment_begin = 0.f;
|
||||||
|
float time_segment_end = 0.f;
|
||||||
|
if (right_mouse_press) {
|
||||||
|
time_segment_begin = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// THIRD RENDER
|
||||||
|
//
|
||||||
|
|
||||||
|
// Render the bounding box
|
||||||
|
const ImU32 frame_col = ImGui::GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
|
||||||
|
ImGui::RenderFrame(bbox.Min, bbox.Max, frame_col, true, style.FrameRounding);
|
||||||
|
|
||||||
|
// by default, put a tick mark at every frame step and a large mark every second
|
||||||
|
guint64 tick_step = step;
|
||||||
|
guint64 large_tick_step = SECOND;
|
||||||
|
|
||||||
|
// how many pixels to represent one frame step?
|
||||||
|
float tick_step_pixels = timeline_bbox.GetWidth() * step_;
|
||||||
|
// while there is less than 3 pixels between two tick marks (or at last optimal tick mark)
|
||||||
|
for ( int i=0; i<10 && tick_step_pixels < 3.f; ++i )
|
||||||
|
{
|
||||||
|
// try to use the optimal tick marks pre-defined
|
||||||
|
tick_step = optimal_tick_marks[i];
|
||||||
|
large_tick_step = optimal_tick_marks[i+1];
|
||||||
|
tick_step_pixels = timeline_bbox.GetWidth() * static_cast<float> ( static_cast<double>(tick_step) / static_cast<double>(duration) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// render the tick marks along TIMELINE
|
||||||
|
ImU32 color_in = ImGui::GetColorU32( style.Colors[ImGuiCol_Text] );
|
||||||
|
ImU32 color_out = ImGui::GetColorU32( style.Colors[ImGuiCol_TextDisabled] );
|
||||||
|
ImU32 color = color_in;
|
||||||
|
pos = timeline_bbox.GetTL();
|
||||||
|
guint64 tick = 0;
|
||||||
|
float tick_percent = 0.f;
|
||||||
|
std::list< std::pair<guint64, guint64> >::iterator it = segments.begin();
|
||||||
|
while ( tick < duration)
|
||||||
|
{
|
||||||
|
// large tick mark every large tick
|
||||||
|
float tick_length = !(tick%large_tick_step) ? fontsize : style.FramePadding.y;
|
||||||
|
|
||||||
|
// different colors for inside and outside [begin end] of segments
|
||||||
|
if ( it != segments.end() ) {
|
||||||
|
|
||||||
|
if ( tick < it->first )
|
||||||
|
color = color_out;
|
||||||
|
else if ( tick > it->second ){
|
||||||
|
color = color_out;
|
||||||
|
it ++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
color = color_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw a tick mark each step
|
||||||
|
window->DrawList->AddLine( pos, pos + ImVec2(0.f, tick_length), color);
|
||||||
|
|
||||||
|
// next tick
|
||||||
|
tick += tick_step;
|
||||||
|
tick_percent = static_cast<float> ( static_cast<double>(tick) / static_cast<double>(duration) );
|
||||||
|
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop over segments and EMPTY the list
|
||||||
|
// ticks for segments begin & end
|
||||||
|
for (std::list< std::pair<guint64, guint64> >::iterator s = segments.begin(); s != segments.end(); s++){
|
||||||
|
tick_percent = static_cast<float> ( static_cast<double>(s->first) / static_cast<double>(duration) );
|
||||||
|
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
|
||||||
|
window->DrawList->AddLine( pos, pos + ImVec2(0.f, timeline_bbox.GetHeight()), color_in);
|
||||||
|
tick_percent = static_cast<float> ( static_cast<double>(s->second) / static_cast<double>(duration) );
|
||||||
|
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
|
||||||
|
window->DrawList->AddLine( pos, pos + ImVec2(0.f, timeline_bbox.GetHeight()), color_in);
|
||||||
|
}
|
||||||
|
segments.clear();
|
||||||
|
|
||||||
|
// tick EOF
|
||||||
|
window->DrawList->AddLine( timeline_bbox.GetTR(), timeline_bbox.GetTR() + ImVec2(0.f, fontsize), color_in);
|
||||||
|
|
||||||
|
// render text : duration and current time
|
||||||
|
char overlay_buf[24];
|
||||||
|
ImVec2 overlay_size = ImVec2(0.f, 0.f);
|
||||||
|
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%s", GstToolkit::to_string(duration).c_str());
|
||||||
|
overlay_size = ImGui::CalcTextSize(overlay_buf, NULL);
|
||||||
|
overlay_size += ImVec2(3.f, 3.f);
|
||||||
|
if (overlay_size.x > 0.0f)
|
||||||
|
ImGui::RenderTextClipped( bbox.GetBR() - overlay_size, bbox.Max, overlay_buf, NULL, &overlay_size);
|
||||||
|
|
||||||
|
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%s", GstToolkit::to_string(*time).c_str());
|
||||||
|
overlay_size = ImGui::CalcTextSize(overlay_buf, NULL);
|
||||||
|
overlay_size = ImVec2(3.f, -3.f - overlay_size.y);
|
||||||
|
if (overlay_size.x > 0.0f)
|
||||||
|
ImGui::RenderTextClipped( bbox.GetBL() + overlay_size, bbox.Max, overlay_buf, NULL, &overlay_size);
|
||||||
|
|
||||||
|
|
||||||
|
// draw slider grab handle
|
||||||
|
if (grab_slider_bb.Max.x > grab_slider_bb.Min.x) {
|
||||||
|
window->DrawList->AddRectFilled(grab_slider_bb.Min, grab_slider_bb.Max, grab_slider_color, style.GrabRounding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw the cursor
|
||||||
|
color = ImGui::GetColorU32(style.Colors[ImGuiCol_SliderGrab]);
|
||||||
|
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width, 2.f);
|
||||||
|
ImGui::RenderArrow(window->DrawList, pos, color, ImGuiDir_Up, cursor_scale);
|
||||||
|
|
||||||
|
return left_mouse_press;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <utility>
|
||||||
#include "rsc/fonts/IconsFontAwesome5.h"
|
#include "rsc/fonts/IconsFontAwesome5.h"
|
||||||
|
|
||||||
namespace ImGuiToolkit
|
namespace ImGuiToolkit
|
||||||
@@ -13,8 +15,9 @@ namespace ImGuiToolkit
|
|||||||
void ShowIconsWindow(bool* p_open);
|
void ShowIconsWindow(bool* p_open);
|
||||||
void ToggleButton( const char* label, bool& toggle );
|
void ToggleButton( const char* label, bool& toggle );
|
||||||
void Bar(float value, float in, float out, float min, float max, const char* title, bool expand);
|
void Bar(float value, float in, float out, float min, float max, const char* title, bool expand);
|
||||||
bool TimelineSlider(const char* label, guint64 *time,
|
bool TimelineSlider(const char* label, guint64 *time, guint64 duration, guint64 step);
|
||||||
guint64 begin, guint64 end, guint64 duration, guint64 step);
|
bool TimelineSliderEdit(const char* label, guint64 *time, guint64 duration, guint64 step,
|
||||||
|
std::list<std::pair<guint64, guint64> >& segments);
|
||||||
|
|
||||||
// fonts from ressources 'fonts/'
|
// fonts from ressources 'fonts/'
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -35,6 +38,7 @@ namespace ImGuiToolkit
|
|||||||
void SetAccentColor(accent_color color);
|
void SetAccentColor(accent_color color);
|
||||||
|
|
||||||
std::string DateTime();
|
std::string DateTime();
|
||||||
};
|
void OpenWebpage( const char* url );
|
||||||
|
}
|
||||||
|
|
||||||
#endif // __IMGUI_TOOLKIT_H_
|
#endif // __IMGUI_TOOLKIT_H_
|
||||||
|
|||||||
77
Log.cpp
77
Log.cpp
@@ -7,6 +7,14 @@
|
|||||||
#include "imgui_internal.h"
|
#include "imgui_internal.h"
|
||||||
|
|
||||||
#include "ImGuiToolkit.h"
|
#include "ImGuiToolkit.h"
|
||||||
|
#include "defines.h"
|
||||||
|
|
||||||
|
// multiplatform
|
||||||
|
#include <tinyfiledialogs.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
struct AppLog
|
struct AppLog
|
||||||
{
|
{
|
||||||
@@ -119,7 +127,7 @@ struct AppLog
|
|||||||
|
|
||||||
static AppLog logs;
|
static AppLog logs;
|
||||||
|
|
||||||
void Log::Info(const char* fmt, ...)
|
void Log::Info(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
@@ -129,7 +137,72 @@ void Log::Info(const char* fmt, ...)
|
|||||||
|
|
||||||
void Log::ShowLogWindow(bool* p_open)
|
void Log::ShowLogWindow(bool* p_open)
|
||||||
{
|
{
|
||||||
ImGui::SetNextWindowSize(ImVec2(500, 600), ImGuiCond_FirstUseEver);
|
ImGui::SetNextWindowSize(ImVec2(700, 600), ImGuiCond_FirstUseEver);
|
||||||
logs.Draw( ICON_FA_LIST_UL " Logs", p_open);
|
logs.Draw( ICON_FA_LIST_UL " Logs", p_open);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static list<string> warnings;
|
||||||
|
|
||||||
|
void Log::Warning(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
ImGuiTextBuffer buf;
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
buf.appendfv(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
warnings.push_back(buf.c_str());
|
||||||
|
Log::Info("Warning - %s\n", buf.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Log::Render()
|
||||||
|
{
|
||||||
|
if (warnings.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
float width = io.DisplaySize.x * 0.4f;
|
||||||
|
|
||||||
|
ImGui::OpenPopup("Warning");
|
||||||
|
if (ImGui::BeginPopupModal("Warning", NULL, ImGuiWindowFlags_AlwaysAutoResize))
|
||||||
|
{
|
||||||
|
ImGuiToolkit::Icon(9, 4);
|
||||||
|
ImGui::SameLine(0, 10);
|
||||||
|
ImGui::SetNextItemWidth(width);
|
||||||
|
ImGui::TextColored(ImVec4(1.0f,0.6f,0.0f,1.0f), "%ld error(s) occured.\n\n", warnings.size());
|
||||||
|
ImGui::Dummy(ImVec2(width, 0));
|
||||||
|
|
||||||
|
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + width);
|
||||||
|
for (list<string>::iterator it=warnings.begin(); it != warnings.end(); ++it) {
|
||||||
|
ImGui::Text("%s \n", (*it).c_str());
|
||||||
|
ImGui::Separator();
|
||||||
|
}
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
|
||||||
|
ImGui::Dummy(ImVec2(width * 0.8f, 0)); ImGui::SameLine(); // right align
|
||||||
|
if (ImGui::Button(" Ok ", ImVec2(width * 0.2f, 0))) {
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
// messages have been seen
|
||||||
|
warnings.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Log::Error(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
ImGuiTextBuffer buf;
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
buf.appendfv(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
tinyfd_messageBox( APP_TITLE, buf.c_str(), "ok", "error", 0);
|
||||||
|
Log::Info("Error - %s\n", buf.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
11
Log.h
11
Log.h
@@ -3,10 +3,15 @@
|
|||||||
|
|
||||||
namespace Log
|
namespace Log
|
||||||
{
|
{
|
||||||
// log info
|
// log
|
||||||
void Info(const char* fmt, ...);
|
void Info(const char* fmt, ...);
|
||||||
|
void Warning(const char* fmt, ...);
|
||||||
|
void Error(const char* fmt, ...);
|
||||||
|
|
||||||
// Draw logs
|
// Draw logs
|
||||||
void ShowLogWindow(bool* p_open = nullptr);
|
void ShowLogWindow(bool* p_open = nullptr);
|
||||||
};
|
|
||||||
|
|
||||||
#endif // __LOG_H_
|
void Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __LOG_H_
|
||||||
|
|||||||
104
MediaPlayer.cpp
104
MediaPlayer.cpp
@@ -1,10 +1,11 @@
|
|||||||
#include "MediaPlayer.h"
|
#include "MediaPlayer.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
// vmix
|
// vmix
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "RenderingManager.h"
|
#include "RenderingManager.h"
|
||||||
#include "ResourceManager.h"
|
#include "Resource.h"
|
||||||
#include "UserInterfaceManager.h"
|
#include "UserInterfaceManager.h"
|
||||||
#include "GstToolkit.h"
|
#include "GstToolkit.h"
|
||||||
|
|
||||||
@@ -61,8 +62,10 @@ MediaPlayer::MediaPlayer(string name) : id(name)
|
|||||||
loop = LoopMode::LOOP_REWIND;
|
loop = LoopMode::LOOP_REWIND;
|
||||||
need_loop = false;
|
need_loop = false;
|
||||||
v_frame_is_full = false;
|
v_frame_is_full = false;
|
||||||
|
current_segment = segments.begin();
|
||||||
|
|
||||||
textureindex = 0;
|
textureindex = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaPlayer::~MediaPlayer()
|
MediaPlayer::~MediaPlayer()
|
||||||
@@ -96,7 +99,7 @@ void MediaPlayer::Open(string uri)
|
|||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
discoverer = gst_discoverer_new (5 * GST_SECOND, &err);
|
discoverer = gst_discoverer_new (5 * GST_SECOND, &err);
|
||||||
if (!discoverer) {
|
if (!discoverer) {
|
||||||
UserInterface::Warning("Error creating discoverer instance: %s\n", err->message);
|
Log::Warning("Error creating discoverer instance: %s\n", err->message);
|
||||||
g_clear_error (&err);
|
g_clear_error (&err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -110,7 +113,7 @@ void MediaPlayer::Open(string uri)
|
|||||||
gst_discoverer_start(discoverer);
|
gst_discoverer_start(discoverer);
|
||||||
// Add the request to process asynchronously the URI
|
// Add the request to process asynchronously the URI
|
||||||
if (!gst_discoverer_discover_uri_async (discoverer, uri.c_str())) {
|
if (!gst_discoverer_discover_uri_async (discoverer, uri.c_str())) {
|
||||||
UserInterface::Warning("Failed to start discovering URI '%s'\n", uri.c_str());
|
Log::Warning("Failed to start discovering URI '%s'\n", uri.c_str());
|
||||||
g_object_unref (discoverer);
|
g_object_unref (discoverer);
|
||||||
discoverer = nullptr;
|
discoverer = nullptr;
|
||||||
}
|
}
|
||||||
@@ -129,7 +132,7 @@ void MediaPlayer::execute_open()
|
|||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
pipeline = gst_parse_launch (description.c_str(), &error);
|
pipeline = gst_parse_launch (description.c_str(), &error);
|
||||||
if (error != NULL) {
|
if (error != NULL) {
|
||||||
UserInterface::Warning("Could not construct pipeline %s:\n%s\n", description.c_str(), error->message);
|
Log::Warning("Could not construct pipeline %s:\n%s\n", description.c_str(), error->message);
|
||||||
g_clear_error (&error);
|
g_clear_error (&error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -139,7 +142,7 @@ void MediaPlayer::execute_open()
|
|||||||
string capstring = "video/x-raw,format=RGB,width="+ std::to_string(width) + ",height=" + std::to_string(height);
|
string capstring = "video/x-raw,format=RGB,width="+ std::to_string(width) + ",height=" + std::to_string(height);
|
||||||
GstCaps *caps = gst_caps_from_string(capstring.c_str());
|
GstCaps *caps = gst_caps_from_string(capstring.c_str());
|
||||||
if (!gst_video_info_from_caps (&v_frame_video_info, caps)) {
|
if (!gst_video_info_from_caps (&v_frame_video_info, caps)) {
|
||||||
UserInterface::Warning("%s: Could not configure MediaPlayer video frame info\n", gst_element_get_name(pipeline));
|
Log::Warning("%s: Could not configure MediaPlayer video frame info\n", gst_element_get_name(pipeline));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +167,7 @@ void MediaPlayer::execute_open()
|
|||||||
gst_object_unref (sink);
|
gst_object_unref (sink);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
UserInterface::Warning("%s: Could not configure MediaPlayer sink\n", gst_element_get_name(pipeline));
|
Log::Warning("%s: Could not configure MediaPlayer sink\n", gst_element_get_name(pipeline));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gst_caps_unref (caps);
|
gst_caps_unref (caps);
|
||||||
@@ -175,7 +178,7 @@ void MediaPlayer::execute_open()
|
|||||||
// set to desired state (PLAY or PAUSE)
|
// set to desired state (PLAY or PAUSE)
|
||||||
GstStateChangeReturn ret = gst_element_set_state (pipeline, desired_state);
|
GstStateChangeReturn ret = gst_element_set_state (pipeline, desired_state);
|
||||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
if (ret == GST_STATE_CHANGE_FAILURE) {
|
||||||
UserInterface::Warning("%s: Failed to open media %s \n%s\n", gst_element_get_name(pipeline), uri.c_str(), discoverer_message.str().c_str());
|
Log::Warning("%s: Failed to open media %s \n%s\n", gst_element_get_name(pipeline), uri.c_str(), discoverer_message.str().c_str());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// all good
|
// all good
|
||||||
@@ -290,7 +293,7 @@ void MediaPlayer::Play(bool on)
|
|||||||
// all ready, apply state change immediately
|
// all ready, apply state change immediately
|
||||||
GstStateChangeReturn ret = gst_element_set_state (pipeline, desired_state);
|
GstStateChangeReturn ret = gst_element_set_state (pipeline, desired_state);
|
||||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
if (ret == GST_STATE_CHANGE_FAILURE) {
|
||||||
UserInterface::Warning("Failed to start up Media %s\n", gst_element_get_name(pipeline));
|
Log::Warning("Failed to start up Media %s\n", gst_element_get_name(pipeline));
|
||||||
}
|
}
|
||||||
#ifdef MEDIA_PLAYER_DEBUG
|
#ifdef MEDIA_PLAYER_DEBUG
|
||||||
else if (on)
|
else if (on)
|
||||||
@@ -394,6 +397,47 @@ void MediaPlayer::FastForward()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool MediaPlayer::addPlaySegment(GstClockTime begin, GstClockTime end)
|
||||||
|
{
|
||||||
|
return addPlaySegment( MediaSegment(begin, end) );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MediaPlayer::addPlaySegment(MediaSegment s)
|
||||||
|
{
|
||||||
|
if ( s.is_valid() )
|
||||||
|
return segments.insert(s).second;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MediaPlayer::removeAllPlaySegmentOverlap(MediaSegment s)
|
||||||
|
{
|
||||||
|
bool ret = removePlaySegmentAt(s.begin);
|
||||||
|
return removePlaySegmentAt(s.end) || ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MediaPlayer::removePlaySegmentAt(GstClockTime t)
|
||||||
|
{
|
||||||
|
MediaSegmentSet::const_iterator s = std::find_if(segments.begin(), segments.end(), containsTime(t));
|
||||||
|
|
||||||
|
if ( s != segments.end() ) {
|
||||||
|
segments.erase(s);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list< std::pair<guint64, guint64> > MediaPlayer::getPlaySegments() const
|
||||||
|
{
|
||||||
|
std::list< std::pair<guint64, guint64> > ret;
|
||||||
|
for (MediaSegmentSet::iterator it = segments.begin(); it != segments.end(); it++)
|
||||||
|
ret.push_back( std::make_pair( it->begin, it->end ) );
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void MediaPlayer::Update()
|
void MediaPlayer::Update()
|
||||||
{
|
{
|
||||||
// discard
|
// discard
|
||||||
@@ -408,6 +452,7 @@ void MediaPlayer::Update()
|
|||||||
|
|
||||||
// apply texture
|
// apply texture
|
||||||
if (v_frame_is_full) {
|
if (v_frame_is_full) {
|
||||||
|
// first occurence; create texture
|
||||||
if (textureindex==0) {
|
if (textureindex==0) {
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glGenTextures(1, &textureindex);
|
glGenTextures(1, &textureindex);
|
||||||
@@ -417,7 +462,7 @@ void MediaPlayer::Update()
|
|||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height,
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height,
|
||||||
0, GL_RGB, GL_UNSIGNED_BYTE, v_frame.data[0]);
|
0, GL_RGB, GL_UNSIGNED_BYTE, v_frame.data[0]);
|
||||||
}
|
}
|
||||||
else
|
else // bind texture
|
||||||
{
|
{
|
||||||
glBindTexture(GL_TEXTURE_2D, textureindex);
|
glBindTexture(GL_TEXTURE_2D, textureindex);
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height,
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height,
|
||||||
@@ -430,20 +475,34 @@ void MediaPlayer::Update()
|
|||||||
|
|
||||||
// manage loop mode
|
// manage loop mode
|
||||||
if (need_loop && !isimage) {
|
if (need_loop && !isimage) {
|
||||||
if (loop == LOOP_NONE)
|
execute_loop_command();
|
||||||
Play(false);
|
|
||||||
else
|
|
||||||
execute_loop_command();
|
|
||||||
|
|
||||||
need_loop = false;
|
need_loop = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all other updates below are only for playing mode
|
||||||
|
if (desired_state != GST_STATE_PLAYING)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// test segments
|
||||||
|
if ( segments.begin() != segments.end()) {
|
||||||
|
|
||||||
|
if ( current_segment == segments.end() )
|
||||||
|
current_segment = segments.begin();
|
||||||
|
|
||||||
|
if ( Position() > current_segment->end) {
|
||||||
|
g_print("switch to next segment ");
|
||||||
|
current_segment++;
|
||||||
|
if ( current_segment == segments.end() )
|
||||||
|
current_segment = segments.begin();
|
||||||
|
SeekTo(current_segment->begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaPlayer::execute_loop_command()
|
void MediaPlayer::execute_loop_command()
|
||||||
{
|
{
|
||||||
if ( pipeline == nullptr) return;
|
|
||||||
|
|
||||||
if (loop==LOOP_REWIND) {
|
if (loop==LOOP_REWIND) {
|
||||||
Rewind();
|
Rewind();
|
||||||
}
|
}
|
||||||
@@ -451,7 +510,9 @@ void MediaPlayer::execute_loop_command()
|
|||||||
rate *= - 1.f;
|
rate *= - 1.f;
|
||||||
execute_seek_command();
|
execute_seek_command();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Play(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaPlayer::execute_seek_command(GstClockTime target)
|
void MediaPlayer::execute_seek_command(GstClockTime target)
|
||||||
@@ -481,7 +542,7 @@ void MediaPlayer::execute_seek_command(GstClockTime target)
|
|||||||
int seek_flags = GST_SEEK_FLAG_FLUSH;
|
int seek_flags = GST_SEEK_FLAG_FLUSH;
|
||||||
// seek with trick mode if fast speed
|
// seek with trick mode if fast speed
|
||||||
if ( ABS(rate) > 2.0 )
|
if ( ABS(rate) > 2.0 )
|
||||||
seek_flags |= GST_SEEK_FLAG_TRICKMODE | GST_SEEK_FLAG_TRICKMODE_NO_AUDIO;
|
seek_flags |= GST_SEEK_FLAG_TRICKMODE;
|
||||||
|
|
||||||
// create seek event depending on direction
|
// create seek event depending on direction
|
||||||
if (rate > 0) {
|
if (rate > 0) {
|
||||||
@@ -492,7 +553,7 @@ void MediaPlayer::execute_seek_command(GstClockTime target)
|
|||||||
GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, seek_pos);
|
GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, seek_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the event
|
// Send the event (ASYNC)
|
||||||
if (seek_event && !gst_element_send_event(pipeline, seek_event) )
|
if (seek_event && !gst_element_send_event(pipeline, seek_event) )
|
||||||
Log::Info("Seek failed in Media %s\n", gst_element_get_name(pipeline));
|
Log::Info("Seek failed in Media %s\n", gst_element_get_name(pipeline));
|
||||||
#ifdef MEDIA_PLAYER_DEBUG
|
#ifdef MEDIA_PLAYER_DEBUG
|
||||||
@@ -532,6 +593,7 @@ double MediaPlayer::UpdateFrameRate() const
|
|||||||
return timecount.framerate();
|
return timecount.framerate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// CALLBACKS
|
// CALLBACKS
|
||||||
|
|
||||||
bool MediaPlayer::fill_v_frame(GstBuffer *buf)
|
bool MediaPlayer::fill_v_frame(GstBuffer *buf)
|
||||||
@@ -690,7 +752,7 @@ TimeCounter::TimeCounter() {
|
|||||||
current_time = gst_util_get_timestamp ();
|
current_time = gst_util_get_timestamp ();
|
||||||
last_time = gst_util_get_timestamp();
|
last_time = gst_util_get_timestamp();
|
||||||
nbFrames = 0;
|
nbFrames = 0;
|
||||||
fps = 0.f;
|
fps = 1.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeCounter::tic ()
|
void TimeCounter::tic ()
|
||||||
@@ -700,7 +762,7 @@ void TimeCounter::tic ()
|
|||||||
if ((current_time - last_time) >= GST_SECOND)
|
if ((current_time - last_time) >= GST_SECOND)
|
||||||
{
|
{
|
||||||
last_time = current_time;
|
last_time = current_time;
|
||||||
fps = 0.2f * fps + 0.8f * static_cast<float>(nbFrames);
|
fps = 0.1f * fps + 0.9f * static_cast<float>(nbFrames);
|
||||||
nbFrames = 0;
|
nbFrames = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <set>
|
||||||
|
#include <list>
|
||||||
|
#include <utility>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
@@ -26,18 +29,71 @@ public:
|
|||||||
int framecount() const;
|
int framecount() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MediaSegment
|
||||||
|
{
|
||||||
|
GstClockTime begin;
|
||||||
|
GstClockTime end;
|
||||||
|
|
||||||
|
MediaSegment()
|
||||||
|
{
|
||||||
|
begin = GST_CLOCK_TIME_NONE;
|
||||||
|
end = GST_CLOCK_TIME_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaSegment(GstClockTime b, GstClockTime e)
|
||||||
|
{
|
||||||
|
if ( b < e ) {
|
||||||
|
begin = b;
|
||||||
|
end = e;
|
||||||
|
} else {
|
||||||
|
begin = GST_CLOCK_TIME_NONE;
|
||||||
|
end = GST_CLOCK_TIME_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline bool is_valid() const
|
||||||
|
{
|
||||||
|
return begin != GST_CLOCK_TIME_NONE && end != GST_CLOCK_TIME_NONE && begin < end;
|
||||||
|
}
|
||||||
|
inline bool operator < (const MediaSegment b) const
|
||||||
|
{
|
||||||
|
return (this->is_valid() && b.is_valid() && this->end < b.begin);
|
||||||
|
}
|
||||||
|
inline bool operator == (const MediaSegment b) const
|
||||||
|
{
|
||||||
|
return (this->begin == b.begin && this->end == b.end);
|
||||||
|
}
|
||||||
|
inline bool operator != (const MediaSegment b) const
|
||||||
|
{
|
||||||
|
return (this->begin != b.begin || this->end != b.end);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct containsTime: public std::unary_function<MediaSegment, bool>
|
||||||
|
{
|
||||||
|
inline bool operator()(const MediaSegment s) const
|
||||||
|
{
|
||||||
|
return ( s.is_valid() && _t > s.begin && _t < s.end );
|
||||||
|
}
|
||||||
|
|
||||||
|
containsTime(GstClockTime t) : _t(t) { }
|
||||||
|
|
||||||
|
private:
|
||||||
|
GstClockTime _t;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::set<MediaSegment> MediaSegmentSet;
|
||||||
|
|
||||||
class MediaPlayer {
|
class MediaPlayer {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor of a GStreamer Media
|
* Constructor of a GStreamer Media
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
MediaPlayer(string name);
|
MediaPlayer(string name);
|
||||||
/**
|
/**
|
||||||
* Destructor.
|
* Destructor.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
~MediaPlayer();
|
~MediaPlayer();
|
||||||
/**
|
/**
|
||||||
@@ -141,8 +197,16 @@ public:
|
|||||||
guint Height() const;
|
guint Height() const;
|
||||||
float AspectRatio() const;
|
float AspectRatio() const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
bool addPlaySegment(GstClockTime begin, GstClockTime end);
|
||||||
|
bool addPlaySegment(MediaSegment s);
|
||||||
|
bool removePlaySegmentAt(GstClockTime t);
|
||||||
|
bool removeAllPlaySegmentOverlap(MediaSegment s);
|
||||||
|
std::list< std::pair<guint64, guint64> > getPlaySegments() const;
|
||||||
|
|
||||||
string id;
|
string id;
|
||||||
string uri;
|
string uri;
|
||||||
guint textureindex;
|
guint textureindex;
|
||||||
@@ -165,13 +229,16 @@ private:
|
|||||||
std::atomic<bool> v_frame_is_full;
|
std::atomic<bool> v_frame_is_full;
|
||||||
std::atomic<bool> need_loop;
|
std::atomic<bool> need_loop;
|
||||||
|
|
||||||
|
MediaSegmentSet segments;
|
||||||
|
MediaSegmentSet::iterator current_segment;
|
||||||
|
|
||||||
bool ready;
|
bool ready;
|
||||||
bool seekable;
|
bool seekable;
|
||||||
bool isimage;
|
bool isimage;
|
||||||
|
|
||||||
void execute_open();
|
void execute_open();
|
||||||
void execute_loop_command();
|
void execute_loop_command();
|
||||||
void execute_seek_command(GstClockTime target = GST_CLOCK_TIME_NONE);
|
void execute_seek_command(GstClockTime target = GST_CLOCK_TIME_NONE);
|
||||||
bool fill_v_frame(GstBuffer *buf);
|
bool fill_v_frame(GstBuffer *buf);
|
||||||
|
|
||||||
static GstFlowReturn callback_pull_sample_video (GstElement *bin, MediaPlayer *m);
|
static GstFlowReturn callback_pull_sample_video (GstElement *bin, MediaPlayer *m);
|
||||||
|
|||||||
@@ -42,51 +42,40 @@
|
|||||||
// vmix
|
// vmix
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "ResourceManager.h"
|
#include "Resource.h"
|
||||||
#include "SettingsManager.h"
|
#include "Settings.h"
|
||||||
#include "UserInterfaceManager.h"
|
#include "UserInterfaceManager.h"
|
||||||
#include "RenderingManager.h"
|
#include "RenderingManager.h"
|
||||||
|
|
||||||
// Class statics
|
|
||||||
GLFWwindow* Rendering::window = nullptr;
|
|
||||||
std::string Rendering::glsl_version;
|
|
||||||
int Rendering::render_width = 0;
|
|
||||||
int Rendering::render_height = 0;
|
|
||||||
std::list<Rendering::RenderingCallback> Rendering::drawCallbacks;
|
|
||||||
Screenshot Rendering::window_screenshot;
|
|
||||||
bool Rendering::request_screenshot = false;
|
|
||||||
|
|
||||||
// local statics
|
// local statics
|
||||||
static GstGLContext *global_gl_context = NULL;
|
static GstGLContext *global_gl_context = NULL;
|
||||||
static GstGLDisplay *global_display = NULL;
|
static GstGLDisplay *global_display = NULL;
|
||||||
static guintptr global_window_handle = 0;
|
static guintptr global_window_handle = 0;
|
||||||
|
|
||||||
|
|
||||||
static void glfw_error_callback(int error, const char* description)
|
static void glfw_error_callback(int error, const char* description)
|
||||||
{
|
{
|
||||||
std::string msg = "Glfw Error: " + std::string(description);
|
Log::Error("Glfw Error %d: %s", error, description);
|
||||||
UserInterface::Error(msg, APP_TITLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WindowRefreshCallback( GLFWwindow* window )
|
static void WindowRefreshCallback( GLFWwindow* window )
|
||||||
{
|
{
|
||||||
Rendering::Draw();
|
Rendering::manager().Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::mat4 Rendering::Projection() {
|
Rendering::Rendering()
|
||||||
glm::mat4 projection = glm::ortho(-5.0, 5.0, -5.0, 5.0);
|
{
|
||||||
glm::mat4 scale = glm::scale(glm::mat4(1.0f), glm::vec3(1.f, AspectRatio(), 1.f));
|
window = nullptr;
|
||||||
|
render_width = 0;
|
||||||
return projection * scale;
|
render_height = 0;
|
||||||
|
request_screenshot = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Rendering::Init()
|
bool Rendering::Init()
|
||||||
{
|
{
|
||||||
// Setup window
|
// Setup window
|
||||||
glfwSetErrorCallback(glfw_error_callback);
|
glfwSetErrorCallback(glfw_error_callback);
|
||||||
if (!glfwInit()){
|
if (!glfwInit()){
|
||||||
UserInterface::Error("Failed to Initialize GLFW.");
|
Log::Error("Failed to Initialize GLFW.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,19 +88,19 @@ bool Rendering::Init()
|
|||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
WindowSettings winset = Settings::application.windows.front();
|
Settings::Window winset = Settings::application.windows.front();
|
||||||
|
|
||||||
// Create window with graphics context
|
// Create window with graphics context
|
||||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||||
window = glfwCreateWindow(winset.w, winset.h, winset.name.c_str(), NULL, NULL);
|
window = glfwCreateWindow(winset.w, winset.h, winset.name.c_str(), NULL, NULL);
|
||||||
if (window == NULL){
|
if (window == NULL){
|
||||||
UserInterface::Error("Failed to Create GLFW Window.");
|
Log::Error("Failed to Create GLFW Window.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add application icon
|
// Add application icon
|
||||||
size_t fpsize = 0;
|
size_t fpsize = 0;
|
||||||
const char *fp = Resource::getData("images/glmixer_256x256.png", &fpsize);
|
const char *fp = Resource::getData("images/v-mix_256x256.png", &fpsize);
|
||||||
if (fp != nullptr) {
|
if (fp != nullptr) {
|
||||||
GLFWimage icon;
|
GLFWimage icon;
|
||||||
icon.pixels = stbi_load_from_memory( (const stbi_uc*)fp, fpsize, &icon.width, &icon.height, nullptr, 4 );
|
icon.pixels = stbi_load_from_memory( (const stbi_uc*)fp, fpsize, &icon.width, &icon.height, nullptr, 4 );
|
||||||
@@ -127,7 +116,7 @@ bool Rendering::Init()
|
|||||||
// Initialize OpenGL loader
|
// Initialize OpenGL loader
|
||||||
bool err = gladLoadGLLoader((GLADloadproc) glfwGetProcAddress) == 0;
|
bool err = gladLoadGLLoader((GLADloadproc) glfwGetProcAddress) == 0;
|
||||||
if (err) {
|
if (err) {
|
||||||
UserInterface::Error("Failed to initialize OpenGL loader.");
|
Log::Error("Failed to initialize GLAD OpenGL loader.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,50 +135,19 @@ bool Rendering::Init()
|
|||||||
gst_init (NULL, NULL);
|
gst_init (NULL, NULL);
|
||||||
|
|
||||||
#if GST_GL_HAVE_PLATFORM_WGL
|
#if GST_GL_HAVE_PLATFORM_WGL
|
||||||
context =
|
global_gl_context = gst_gl_context_new_wrapped (display, (guintptr) wglGetCurrentContext (),
|
||||||
gst_gl_context_new_wrapped (display, (guintptr) wglGetCurrentContext (),
|
GST_GL_PLATFORM_WGL, GST_GL_API_OPENGL);
|
||||||
GST_GL_PLATFORM_WGL, GST_GL_API_OPENGL);
|
|
||||||
#elif GST_GL_HAVE_PLATFORM_CGL
|
#elif GST_GL_HAVE_PLATFORM_CGL
|
||||||
|
|
||||||
// GstGLDisplay* display = gst_gl_display_new();
|
|
||||||
// guintptr context = gst_gl_context_get_current_gl_context(GST_GL_HAVE_PLATFORM_CGL);
|
|
||||||
// GstGLContext* gstcontext = gst_gl_context_new_wrapped(display, context, GST_GL_HAVE_PLATFORM_CGL, GST_GL_API_OPENGL);
|
|
||||||
// GstGLDisplay *gst_display;
|
|
||||||
|
|
||||||
|
// global_display = GST_GL_DISPLAY ( glfwGetCocoaMonitor(window) );
|
||||||
|
// global_display = GST_GL_DISPLAY (gst_gl_display_cocoa_new ());
|
||||||
|
|
||||||
// GstGLDisplay *gst_display;
|
// global_gl_context = gst_gl_context_new_wrapped (global_display,
|
||||||
|
// (guintptr) 0,
|
||||||
|
// GST_GL_PLATFORM_CGL, GST_GL_API_OPENGL);
|
||||||
|
|
||||||
//global_display = GST_GL_DISPLAY ( glfwGetCocoaMonitor(window) );
|
|
||||||
|
|
||||||
//GstGLDisplayCocoa *tmp = gst_gl_display_cocoa_new ();
|
|
||||||
|
|
||||||
global_display = GST_GL_DISPLAY (gst_gl_display_cocoa_new ());
|
|
||||||
|
|
||||||
//glfwMakeContextCurrent(window);
|
|
||||||
|
|
||||||
global_gl_context = gst_gl_context_new_wrapped (global_display,
|
|
||||||
(guintptr) 0,
|
|
||||||
GST_GL_PLATFORM_CGL, GST_GL_API_OPENGL);
|
|
||||||
|
|
||||||
// id toto = glfwGetNSGLContext(window);
|
|
||||||
|
|
||||||
// g_warning("OpenGL Contexts %ld %ld", (long int) glfwGetNSGLContext(window), (long int) CocoaToolkit::get_current_nsopengl_context() );
|
|
||||||
|
|
||||||
// global_gl_context = gst_gl_context_new_wrapped (global_display,
|
|
||||||
// (guintptr) glfwGetNSGLContext(window),
|
|
||||||
// GST_GL_PLATFORM_CGL, GST_GL_API_OPENGL);
|
|
||||||
|
|
||||||
// guintptr context;
|
|
||||||
// context = gst_gl_context_get_current_gl_context (GST_GL_PLATFORM_CGL);
|
|
||||||
|
|
||||||
// gst_display = gst_gl_display_new ();
|
|
||||||
|
|
||||||
// global_gl_context = gst_gl_context_new_wrapped (GST_GL_DISPLAY (gst_display),
|
|
||||||
// context, GST_GL_PLATFORM_CGL, gst_gl_context_get_current_gl_api (GST_GL_PLATFORM_CGL, NULL, NULL));
|
|
||||||
|
|
||||||
// global_display = GST_GL_DISPLAY ( gst_gl_context_get_display(global_gl_context) );
|
|
||||||
|
|
||||||
// global_window_handle = (guintptr) glfwGetCocoaWindow(window);
|
|
||||||
|
|
||||||
#elif GST_GL_HAVE_PLATFORM_GLX
|
#elif GST_GL_HAVE_PLATFORM_GLX
|
||||||
|
|
||||||
@@ -237,9 +195,9 @@ void Rendering::AddDrawCallback(RenderingCallback function)
|
|||||||
|
|
||||||
void Rendering::Draw()
|
void Rendering::Draw()
|
||||||
{
|
{
|
||||||
if (Rendering::Begin() )
|
if ( Begin() )
|
||||||
{
|
{
|
||||||
UserInterface::NewFrame();
|
UserInterface::manager().NewFrame();
|
||||||
|
|
||||||
std::list<Rendering::RenderingCallback>::iterator iter;
|
std::list<Rendering::RenderingCallback>::iterator iter;
|
||||||
for (iter=drawCallbacks.begin(); iter != drawCallbacks.end(); iter++)
|
for (iter=drawCallbacks.begin(); iter != drawCallbacks.end(); iter++)
|
||||||
@@ -247,8 +205,8 @@ void Rendering::Draw()
|
|||||||
(*iter)();
|
(*iter)();
|
||||||
}
|
}
|
||||||
|
|
||||||
UserInterface::Render();
|
UserInterface::manager().Render();
|
||||||
Rendering::End();
|
End();
|
||||||
}
|
}
|
||||||
|
|
||||||
// no g_main_loop_run(loop) : update global GMainContext
|
// no g_main_loop_run(loop) : update global GMainContext
|
||||||
@@ -324,6 +282,14 @@ void Rendering::Close()
|
|||||||
glfwSetWindowShouldClose(window, true);
|
glfwSetWindowShouldClose(window, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
glm::mat4 Rendering::Projection() {
|
||||||
|
glm::mat4 projection = glm::ortho(-5.0, 5.0, -5.0, 5.0);
|
||||||
|
glm::mat4 scale = glm::scale(glm::mat4(1.0f), glm::vec3(1.f, AspectRatio(), 1.f));
|
||||||
|
|
||||||
|
return projection * scale;
|
||||||
|
}
|
||||||
|
|
||||||
void Rendering::ToggleFullscreen()
|
void Rendering::ToggleFullscreen()
|
||||||
{
|
{
|
||||||
// if in fullscreen mode
|
// if in fullscreen mode
|
||||||
@@ -362,52 +328,6 @@ float Rendering::AspectRatio()
|
|||||||
return static_cast<float>(render_width) / static_cast<float>(render_height);
|
return static_cast<float>(render_width) / static_cast<float>(render_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static GstBusSyncReply
|
|
||||||
bus_sync_handler (GstBus * bus, GstMessage * msg, gpointer user_data)
|
|
||||||
{
|
|
||||||
if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_NEED_CONTEXT) {
|
|
||||||
const gchar* contextType;
|
|
||||||
gst_message_parse_context_type(msg, &contextType);
|
|
||||||
|
|
||||||
if (!g_strcmp0(contextType, GST_GL_DISPLAY_CONTEXT_TYPE)) {
|
|
||||||
GstContext *displayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
|
|
||||||
gst_context_set_gl_display(displayContext, global_display);
|
|
||||||
gst_element_set_context(GST_ELEMENT(msg->src), displayContext);
|
|
||||||
gst_context_unref (displayContext);
|
|
||||||
|
|
||||||
g_info ("Managed %s\n", contextType);
|
|
||||||
}
|
|
||||||
if (!g_strcmp0(contextType, "gst.gl.app_context")) {
|
|
||||||
GstContext *appContext = gst_context_new("gst.gl.app_context", TRUE);
|
|
||||||
GstStructure* structure = gst_context_writable_structure(appContext);
|
|
||||||
gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, global_gl_context, nullptr);
|
|
||||||
gst_element_set_context(GST_ELEMENT(msg->src), appContext);
|
|
||||||
gst_context_unref (appContext);
|
|
||||||
|
|
||||||
g_info ("Managed %s\n", contextType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_message_unref (msg);
|
|
||||||
|
|
||||||
return GST_BUS_DROP;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rendering::LinkPipeline( GstPipeline *pipeline )
|
|
||||||
{
|
|
||||||
// capture bus signals to force a unique opengl context for all GST elements
|
|
||||||
GstBus* m_bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
|
||||||
gst_bus_set_sync_handler (m_bus, (GstBusSyncHandler) bus_sync_handler, pipeline, NULL);
|
|
||||||
gst_object_unref (m_bus);
|
|
||||||
|
|
||||||
|
|
||||||
// GstBus* m_bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
|
||||||
// gst_bus_enable_sync_message_emission (m_bus);
|
|
||||||
// g_signal_connect (m_bus, "sync-message", G_CALLBACK (bus_sync_handler), pipeline);
|
|
||||||
// gst_object_unref (m_bus);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rendering::FileDropped(GLFWwindow* window, int path_count, const char* paths[])
|
void Rendering::FileDropped(GLFWwindow* window, int path_count, const char* paths[])
|
||||||
{
|
{
|
||||||
for (int i = 0; i < path_count; ++i) {
|
for (int i = 0; i < path_count; ++i) {
|
||||||
@@ -502,3 +422,57 @@ void Screenshot::FlipVertical()
|
|||||||
}
|
}
|
||||||
delete[] line_tmp;
|
delete[] line_tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Discarded because not working under OSX - kept in case it would become useful
|
||||||
|
//
|
||||||
|
// Linking pipeline to the rendering instance ensures the opengl contexts
|
||||||
|
// created by gstreamer inside plugins (e.g. glsinkbin) is the same
|
||||||
|
//
|
||||||
|
|
||||||
|
static GstBusSyncReply
|
||||||
|
bus_sync_handler (GstBus * bus, GstMessage * msg, gpointer user_data)
|
||||||
|
{
|
||||||
|
if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_NEED_CONTEXT) {
|
||||||
|
const gchar* contextType;
|
||||||
|
gst_message_parse_context_type(msg, &contextType);
|
||||||
|
|
||||||
|
if (!g_strcmp0(contextType, GST_GL_DISPLAY_CONTEXT_TYPE)) {
|
||||||
|
GstContext *displayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
|
||||||
|
gst_context_set_gl_display(displayContext, global_display);
|
||||||
|
gst_element_set_context(GST_ELEMENT(msg->src), displayContext);
|
||||||
|
gst_context_unref (displayContext);
|
||||||
|
|
||||||
|
g_info ("Managed %s\n", contextType);
|
||||||
|
}
|
||||||
|
if (!g_strcmp0(contextType, "gst.gl.app_context")) {
|
||||||
|
GstContext *appContext = gst_context_new("gst.gl.app_context", TRUE);
|
||||||
|
GstStructure* structure = gst_context_writable_structure(appContext);
|
||||||
|
gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, global_gl_context, nullptr);
|
||||||
|
gst_element_set_context(GST_ELEMENT(msg->src), appContext);
|
||||||
|
gst_context_unref (appContext);
|
||||||
|
|
||||||
|
g_info ("Managed %s\n", contextType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_message_unref (msg);
|
||||||
|
|
||||||
|
return GST_BUS_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rendering::LinkPipeline( GstPipeline *pipeline )
|
||||||
|
{
|
||||||
|
// capture bus signals to force a unique opengl context for all GST elements
|
||||||
|
GstBus* m_bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
||||||
|
gst_bus_set_sync_handler (m_bus, (GstBusSyncHandler) bus_sync_handler, pipeline, NULL);
|
||||||
|
gst_object_unref (m_bus);
|
||||||
|
|
||||||
|
|
||||||
|
// GstBus* m_bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
||||||
|
// gst_bus_enable_sync_message_emission (m_bus);
|
||||||
|
// g_signal_connect (m_bus, "sync-message", G_CALLBACK (bus_sync_handler), pipeline);
|
||||||
|
// gst_object_unref (m_bus);
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,56 +34,70 @@ class Rendering
|
|||||||
friend class UserInterface;
|
friend class UserInterface;
|
||||||
|
|
||||||
// GLFW integration in OS window management
|
// GLFW integration in OS window management
|
||||||
static class GLFWwindow* window;
|
class GLFWwindow* window;
|
||||||
static Screenshot window_screenshot;
|
Screenshot window_screenshot;
|
||||||
static std::string glsl_version;
|
std::string glsl_version;
|
||||||
static int render_width, render_height;
|
int render_width, render_height;
|
||||||
static bool request_screenshot;
|
bool request_screenshot;
|
||||||
|
|
||||||
|
// Private Constructor
|
||||||
|
Rendering();
|
||||||
|
Rendering(Rendering const& copy); // Not Implemented
|
||||||
|
Rendering& operator=(Rendering const& copy); // Not Implemented
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Initialization OpenGL and GLFW window creation
|
static Rendering& manager()
|
||||||
static bool Init();
|
{
|
||||||
// true if active rendering window
|
// The only instance
|
||||||
static bool isActive();
|
static Rendering _instance;
|
||||||
// request close of the UI (Quit the program)
|
return _instance;
|
||||||
static void Close();
|
}
|
||||||
// Post-loop termination
|
|
||||||
static void Terminate();
|
|
||||||
|
|
||||||
|
// Initialization OpenGL and GLFW window creation
|
||||||
|
bool Init();
|
||||||
|
// true if active rendering window
|
||||||
|
bool isActive();
|
||||||
// draw one frame
|
// draw one frame
|
||||||
static void Draw();
|
void Draw();
|
||||||
|
// request close of the UI (Quit the program)
|
||||||
|
void Close();
|
||||||
|
// Post-loop termination
|
||||||
|
void Terminate();
|
||||||
|
|
||||||
// add function to call during Draw
|
// add function to call during Draw
|
||||||
typedef void (* RenderingCallback)(void);
|
typedef void (* RenderingCallback)(void);
|
||||||
static void AddDrawCallback(RenderingCallback function);
|
void AddDrawCallback(RenderingCallback function);
|
||||||
|
|
||||||
// request screenshot
|
// request screenshot
|
||||||
static void RequestScreenshot();
|
void RequestScreenshot();
|
||||||
// get Screenshot
|
// get Screenshot
|
||||||
static Screenshot *CurrentScreenshot();
|
Screenshot *CurrentScreenshot();
|
||||||
|
|
||||||
// request fullscreen
|
// request fullscreen
|
||||||
static void ToggleFullscreen();
|
void ToggleFullscreen();
|
||||||
// get width of rendering area
|
// get width of rendering area
|
||||||
static float Width() { return render_width; }
|
float Width() { return render_width; }
|
||||||
// get height of rendering area
|
// get height of rendering area
|
||||||
static float Height() { return render_height; }
|
float Height() { return render_height; }
|
||||||
// get aspect ratio of rendering area
|
// get aspect ratio of rendering area
|
||||||
static float AspectRatio();
|
float AspectRatio();
|
||||||
// get projection matrix (for sharers)
|
|
||||||
static glm::mat4 Projection();
|
|
||||||
|
|
||||||
// for opengl pipile in gstreamer
|
// get projection matrix (for sharers) => Views
|
||||||
static void LinkPipeline( GstPipeline *pipeline );
|
glm::mat4 Projection();
|
||||||
|
|
||||||
|
// for opengl pipeline in gstreamer
|
||||||
|
void LinkPipeline( GstPipeline *pipeline );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// loop update to begin new frame
|
// loop update to begin new frame
|
||||||
static bool Begin();
|
bool Begin();
|
||||||
// loop update end frame
|
// loop update end frame
|
||||||
static void End();
|
void End();
|
||||||
|
|
||||||
// list of functions to call at each Draw
|
// list of functions to call at each Draw
|
||||||
static std::list<RenderingCallback> drawCallbacks;
|
std::list<RenderingCallback> drawCallbacks;
|
||||||
|
|
||||||
// file drop callback
|
// file drop callback
|
||||||
static void FileDropped(GLFWwindow* window, int path_count, const char* paths[]);
|
static void FileDropped(GLFWwindow* window, int path_count, const char* paths[]);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "ResourceManager.h"
|
#include "Resource.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
CMRC_DECLARE(vmix);
|
CMRC_DECLARE(vmix);
|
||||||
|
|
||||||
|
|
||||||
std::map<std::string, unsigned int> Resource::textureIndex;
|
std::map<std::string, unsigned int> textureIndex;
|
||||||
|
|
||||||
const char *Resource::getData(const std::string& path, size_t* out_file_size){
|
const char *Resource::getData(const std::string& path, size_t* out_file_size){
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ unsigned int Resource::getTextureImage(const std::string& path)
|
|||||||
return textureID;
|
return textureID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Resource::listFiles()
|
std::string Resource::listDirectory()
|
||||||
{
|
{
|
||||||
// enter icons directory
|
// enter icons directory
|
||||||
auto fs = cmrc::vmix::get_filesystem();
|
auto fs = cmrc::vmix::get_filesystem();
|
||||||
@@ -210,16 +210,19 @@ void Resource::listFiles()
|
|||||||
cmrc::directory_iterator it = fs.iterate_directory("");
|
cmrc::directory_iterator it = fs.iterate_directory("");
|
||||||
cmrc::directory_iterator itend = it.end();
|
cmrc::directory_iterator itend = it.end();
|
||||||
|
|
||||||
|
std::string ls;
|
||||||
|
|
||||||
// loop over all icons
|
// loop over all icons
|
||||||
while(it != itend){
|
while(it != itend){
|
||||||
|
|
||||||
cmrc::directory_entry file = *it;
|
cmrc::directory_entry file = *it;
|
||||||
|
|
||||||
if (file.is_file()) {
|
if (file.is_file()) {
|
||||||
std::cerr << "Found file " << file.filename() << std::endl;
|
ls += file.filename() + ", ";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ls;
|
||||||
}
|
}
|
||||||
30
Resource.h
Normal file
30
Resource.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef __RSC_MANAGER_H_
|
||||||
|
#define __RSC_MANAGER_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace Resource
|
||||||
|
{
|
||||||
|
|
||||||
|
// Support all text files
|
||||||
|
// return file tyext content as one string
|
||||||
|
std::string getText(const std::string& path);
|
||||||
|
|
||||||
|
// Support DDS files, DXT1, DXT5 and DXT5
|
||||||
|
// Returns the OpenGL generated Texture index
|
||||||
|
unsigned int getTextureDDS(const std::string& path);
|
||||||
|
|
||||||
|
// Support PNG, JPEG, TGA, BMP, PSD, GIF, HDR, PIC, PNM
|
||||||
|
// Returns the OpenGL generated Texture index
|
||||||
|
unsigned int getTextureImage(const std::string& path);
|
||||||
|
|
||||||
|
// Generic access to pointer to data
|
||||||
|
const char *getData(const std::string& path, size_t* out_file_size);
|
||||||
|
|
||||||
|
// list files in resource directory
|
||||||
|
std::string listDirectory();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __RSC_MANAGER_H_ */
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#ifndef vlmixer_rsc_manager
|
|
||||||
#define vlmixer_rsc_manager
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
class Resource
|
|
||||||
{
|
|
||||||
|
|
||||||
static std::map<std::string, unsigned int> textureIndex;
|
|
||||||
|
|
||||||
static void listFiles();
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// Support all text files
|
|
||||||
// return file tyext content as one string
|
|
||||||
static std::string getText(const std::string& path);
|
|
||||||
|
|
||||||
// Support DDS files, DXT1, DXT5 and DXT5
|
|
||||||
// Returns the OpenGL generated Texture index
|
|
||||||
static unsigned int getTextureDDS(const std::string& path);
|
|
||||||
|
|
||||||
// Support PNG, JPEG, TGA, BMP, PSD, GIF, HDR, PIC, PNM
|
|
||||||
// Returns the OpenGL generated Texture index
|
|
||||||
static unsigned int getTextureImage(const std::string& path);
|
|
||||||
|
|
||||||
// Generic access to pointer to data
|
|
||||||
static const char *getData(const std::string& path, size_t* out_file_size);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* vlmixer_rsc_manager */
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "SettingsManager.h"
|
#include "Settings.h"
|
||||||
#include "defines.h"
|
#include "Log.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@@ -7,20 +7,12 @@
|
|||||||
using namespace tinyxml2;
|
using namespace tinyxml2;
|
||||||
|
|
||||||
#ifndef XMLCheckResult
|
#ifndef XMLCheckResult
|
||||||
#define XMLCheckResult(a_eResult) if (a_eResult != XML_SUCCESS) { printf("Settings warning: %i\n", a_eResult); return; }
|
#define XMLCheckResult(a_eResult) if (a_eResult != XML_SUCCESS) { Log::Warning("XML error %i\n", a_eResult); return; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AppSettings Settings::application(APP_NAME);
|
|
||||||
|
|
||||||
AppSettings::AppSettings(const string& name) {
|
Settings::Application Settings::application;
|
||||||
|
string filename = string("./") + APP_NAME + ".xml";
|
||||||
this->name = name;
|
|
||||||
this->filename = "./" + name + ".xml";
|
|
||||||
this->windows.clear();
|
|
||||||
this->windows.push_back(WindowSettings(15,15,1280,720, APP_TITLE));
|
|
||||||
this->scale = 1.0;
|
|
||||||
this->color = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Settings::Save()
|
void Settings::Save()
|
||||||
{
|
{
|
||||||
@@ -41,10 +33,10 @@ void Settings::Save()
|
|||||||
{
|
{
|
||||||
XMLElement *windowsNode = xmlDoc.NewElement( "Windows" );
|
XMLElement *windowsNode = xmlDoc.NewElement( "Windows" );
|
||||||
|
|
||||||
list<WindowSettings>::iterator iter;
|
list<Settings::Window>::iterator iter;
|
||||||
for (iter=application.windows.begin(); iter != application.windows.end(); iter++)
|
for (iter=application.windows.begin(); iter != application.windows.end(); iter++)
|
||||||
{
|
{
|
||||||
const WindowSettings& w=*iter;
|
const Settings::Window& w=*iter;
|
||||||
|
|
||||||
XMLElement *window = xmlDoc.NewElement( "Window" );
|
XMLElement *window = xmlDoc.NewElement( "Window" );
|
||||||
window->SetAttribute("name", w.name.c_str());
|
window->SetAttribute("name", w.name.c_str());
|
||||||
@@ -64,14 +56,14 @@ void Settings::Save()
|
|||||||
applicationNode->SetAttribute("color", application.color);
|
applicationNode->SetAttribute("color", application.color);
|
||||||
pRoot->InsertEndChild(applicationNode);
|
pRoot->InsertEndChild(applicationNode);
|
||||||
|
|
||||||
XMLError eResult = xmlDoc.SaveFile(application.filename.c_str());
|
XMLError eResult = xmlDoc.SaveFile(filename.c_str());
|
||||||
XMLCheckResult(eResult);
|
XMLCheckResult(eResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::Load()
|
void Settings::Load()
|
||||||
{
|
{
|
||||||
XMLDocument xmlDoc;
|
XMLDocument xmlDoc;
|
||||||
XMLError eResult = xmlDoc.LoadFile(application.filename.c_str());
|
XMLError eResult = xmlDoc.LoadFile(filename.c_str());
|
||||||
|
|
||||||
// do not warn if non existing file
|
// do not warn if non existing file
|
||||||
if (eResult == XML_ERROR_FILE_NOT_FOUND)
|
if (eResult == XML_ERROR_FILE_NOT_FOUND)
|
||||||
@@ -96,7 +88,7 @@ void Settings::Load()
|
|||||||
XMLElement* pWindowNode = pElement->FirstChildElement("Window");
|
XMLElement* pWindowNode = pElement->FirstChildElement("Window");
|
||||||
for( ; pWindowNode ; pWindowNode=pWindowNode->NextSiblingElement())
|
for( ; pWindowNode ; pWindowNode=pWindowNode->NextSiblingElement())
|
||||||
{
|
{
|
||||||
WindowSettings w;
|
Settings::Window w;
|
||||||
const char *pName=pWindowNode->Attribute("name");
|
const char *pName=pWindowNode->Attribute("name");
|
||||||
if (pName) w.name=pName;
|
if (pName) w.name=pName;
|
||||||
|
|
||||||
@@ -123,8 +115,8 @@ void Settings::Check()
|
|||||||
Settings::Save();
|
Settings::Save();
|
||||||
|
|
||||||
XMLDocument xmlDoc;
|
XMLDocument xmlDoc;
|
||||||
XMLError eResult = xmlDoc.LoadFile(application.filename.c_str());
|
XMLError eResult = xmlDoc.LoadFile(filename.c_str());
|
||||||
XMLCheckResult(eResult);
|
XMLCheckResult(eResult);
|
||||||
|
|
||||||
xmlDoc.Print();
|
xmlDoc.Print();
|
||||||
}
|
}
|
||||||
47
Settings.h
Normal file
47
Settings.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#ifndef __SETTINGS_H_
|
||||||
|
#define __SETTINGS_H_
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace Settings {
|
||||||
|
|
||||||
|
struct Window
|
||||||
|
{
|
||||||
|
string name;
|
||||||
|
int x,y,w,h;
|
||||||
|
bool fullscreen;
|
||||||
|
|
||||||
|
Window() : name(APP_TITLE), x(15), y(15), w(1280), h(720), fullscreen(false) { }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Application
|
||||||
|
{
|
||||||
|
string name;
|
||||||
|
float scale;
|
||||||
|
int color;
|
||||||
|
list<Window> windows;
|
||||||
|
|
||||||
|
Application() : name(APP_NAME), scale(1.f), color(0){
|
||||||
|
windows.push_back(Window());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// minimal implementation of settings
|
||||||
|
// Can be accessed r&w anywhere
|
||||||
|
extern Application application;
|
||||||
|
|
||||||
|
// Save and Load store settings in XML file
|
||||||
|
void Save();
|
||||||
|
void Load();
|
||||||
|
void Check();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __SETTINGS_H_ */
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
#ifndef vlmixer_app_settings
|
|
||||||
#define vlmixer_app_settings
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <list>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
class WindowSettings
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
int x,y,w,h;
|
|
||||||
string name;
|
|
||||||
bool fullscreen;
|
|
||||||
|
|
||||||
WindowSettings() : x(0), y(0), w(100), h(100), name("Untitled"), fullscreen(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowSettings(int x, int y, int w, int h, const string& name)
|
|
||||||
{
|
|
||||||
this->x=x;
|
|
||||||
this->y=y;
|
|
||||||
this->w=w;
|
|
||||||
this->h=h;
|
|
||||||
this->name=name;
|
|
||||||
this->fullscreen=false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class AppSettings
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
string name;
|
|
||||||
string filename;
|
|
||||||
list<WindowSettings> windows;
|
|
||||||
float scale;
|
|
||||||
int color;
|
|
||||||
|
|
||||||
AppSettings(const string& name);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Settings
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static AppSettings application;
|
|
||||||
|
|
||||||
static void Save();
|
|
||||||
static void Load();
|
|
||||||
static void Check();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* vlmixer_app_settings */
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "ShaderManager.h"
|
#include "Shader.h"
|
||||||
#include "ResourceManager.h"
|
#include "Resource.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -25,27 +25,17 @@
|
|||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#include <stb_image_write.h>
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
# include <windows.h>
|
|
||||||
# include <shellapi.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "TextEditor.h"
|
#include "TextEditor.h"
|
||||||
#include "UserInterfaceManager.h"
|
#include "UserInterfaceManager.h"
|
||||||
#include "RenderingManager.h"
|
#include "RenderingManager.h"
|
||||||
#include "ResourceManager.h"
|
#include "Resource.h"
|
||||||
#include "SettingsManager.h"
|
#include "Settings.h"
|
||||||
#include "FileDialog.h"
|
#include "FileDialog.h"
|
||||||
#include "ImGuiToolkit.h"
|
#include "ImGuiToolkit.h"
|
||||||
#include "GstToolkit.h"
|
#include "GstToolkit.h"
|
||||||
|
|
||||||
MessageBox UserInterface::warnings;
|
|
||||||
MainWindow UserInterface::mainwindow;
|
|
||||||
std::string UserInterface::currentFileDialog = "";
|
|
||||||
std::string UserInterface::currentTextEdit = "";
|
|
||||||
|
|
||||||
static std::thread loadThread;
|
static std::thread loadThread;
|
||||||
static bool loadThreadDone = false;
|
static bool loadThreadDone = false;
|
||||||
static TextEditor editor;
|
static TextEditor editor;
|
||||||
@@ -74,25 +64,16 @@ static void NativeOpenFile(std::string ext)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void OpenWebpage( const char* url )
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
ShellExecuteA( nullptr, nullptr, url, nullptr, nullptr, 0 );
|
|
||||||
#elif defined __APPLE__
|
|
||||||
char buf[1024];
|
|
||||||
sprintf( buf, "open %s", url );
|
|
||||||
system( buf );
|
|
||||||
#else
|
|
||||||
char buf[1024];
|
|
||||||
sprintf( buf, "xdg-open %s", url );
|
|
||||||
system( buf );
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
UserInterface::UserInterface()
|
||||||
|
{
|
||||||
|
currentFileDialog = "";
|
||||||
|
currentTextEdit = "";
|
||||||
|
}
|
||||||
|
|
||||||
bool UserInterface::Init()
|
bool UserInterface::Init()
|
||||||
{
|
{
|
||||||
if (Rendering::window == nullptr)
|
if (Rendering::manager().window == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Setup Dear ImGui context
|
// Setup Dear ImGui context
|
||||||
@@ -103,8 +84,8 @@ bool UserInterface::Init()
|
|||||||
io.MouseDrawCursor = true;
|
io.MouseDrawCursor = true;
|
||||||
|
|
||||||
// Setup Platform/Renderer bindings
|
// Setup Platform/Renderer bindings
|
||||||
ImGui_ImplGlfw_InitForOpenGL(Rendering::window, true);
|
ImGui_ImplGlfw_InitForOpenGL(Rendering::manager().window, true);
|
||||||
ImGui_ImplOpenGL3_Init(Rendering::glsl_version.c_str());
|
ImGui_ImplOpenGL3_Init(Rendering::manager().glsl_version.c_str());
|
||||||
|
|
||||||
// Setup Dear ImGui style
|
// Setup Dear ImGui style
|
||||||
ImGuiToolkit::SetAccentColor(static_cast<ImGuiToolkit::accent_color>(Settings::application.color));
|
ImGuiToolkit::SetAccentColor(static_cast<ImGuiToolkit::accent_color>(Settings::application.color));
|
||||||
@@ -148,7 +129,7 @@ void UserInterface::handleKeyboard()
|
|||||||
if ( io.KeyCtrl ) {
|
if ( io.KeyCtrl ) {
|
||||||
|
|
||||||
if (ImGui::IsKeyPressed( GLFW_KEY_Q ))
|
if (ImGui::IsKeyPressed( GLFW_KEY_Q ))
|
||||||
Rendering::Close();
|
Rendering::manager().Close();
|
||||||
else if (ImGui::IsKeyPressed( GLFW_KEY_O ))
|
else if (ImGui::IsKeyPressed( GLFW_KEY_O ))
|
||||||
UserInterface::OpenFileMedia();
|
UserInterface::OpenFileMedia();
|
||||||
else if (ImGui::IsKeyPressed( GLFW_KEY_S ))
|
else if (ImGui::IsKeyPressed( GLFW_KEY_S ))
|
||||||
@@ -162,7 +143,7 @@ void UserInterface::handleKeyboard()
|
|||||||
|
|
||||||
// Application F-Keys
|
// Application F-Keys
|
||||||
if (ImGui::IsKeyPressed( GLFW_KEY_F12 ))
|
if (ImGui::IsKeyPressed( GLFW_KEY_F12 ))
|
||||||
Rendering::ToggleFullscreen();
|
Rendering::manager().ToggleFullscreen();
|
||||||
else if (ImGui::IsKeyPressed( GLFW_KEY_F11 ))
|
else if (ImGui::IsKeyPressed( GLFW_KEY_F11 ))
|
||||||
mainwindow.StartScreenshot();
|
mainwindow.StartScreenshot();
|
||||||
|
|
||||||
@@ -205,17 +186,17 @@ void UserInterface::NewFrame()
|
|||||||
ImGui_ImplGlfw_NewFrame();
|
ImGui_ImplGlfw_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
// Main window
|
|
||||||
mainwindow.Render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UserInterface::Render()
|
void UserInterface::Render()
|
||||||
{
|
{
|
||||||
ImVec2 geometry(static_cast<float>(Rendering::render_width), static_cast<float>(Rendering::render_height));
|
ImVec2 geometry(static_cast<float>(Rendering::manager().Width()), static_cast<float>(Rendering::manager().Height()));
|
||||||
|
|
||||||
|
// Main window
|
||||||
|
mainwindow.Render();
|
||||||
|
|
||||||
// warning modal dialog
|
// warning modal dialog
|
||||||
warnings.Render( geometry.x * 0.4f ); // 40% width
|
Log::Render();
|
||||||
|
|
||||||
// file modal dialog
|
// file modal dialog
|
||||||
geometry.x *= 0.4f;
|
geometry.x *= 0.4f;
|
||||||
@@ -242,62 +223,6 @@ void UserInterface::Terminate()
|
|||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserInterface::Error(const string& text, const string& title)
|
|
||||||
{
|
|
||||||
// Show NON Graphical dialog (using multiplatform tiny file dialog )
|
|
||||||
string t = title.empty() ? APP_TITLE : title;
|
|
||||||
tinyfd_messageBox( t.c_str(), text.c_str(), "ok", "error", 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void UserInterface::Warning(const char* fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
|
|
||||||
ImGuiTextBuffer buf;
|
|
||||||
buf.appendfv(fmt, args);
|
|
||||||
warnings.messages.push_back(buf.c_str());
|
|
||||||
Log::Info(buf.c_str());
|
|
||||||
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageBox::MessageBox()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void MessageBox::Render(float width)
|
|
||||||
{
|
|
||||||
if (messages.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
ImGui::OpenPopup("Warning");
|
|
||||||
if (ImGui::BeginPopupModal("Warning", NULL, ImGuiWindowFlags_AlwaysAutoResize))
|
|
||||||
{
|
|
||||||
ImGuiToolkit::Icon(9, 4);
|
|
||||||
ImGui::SameLine(0, 10);
|
|
||||||
ImGui::TextColored(ImVec4(1.0f,0.6f,0.0f,1.0f), "%ld error(s) occured.\n\n", messages.size());
|
|
||||||
|
|
||||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + width);
|
|
||||||
for (list<string>::iterator it=messages.begin(); it != messages.end(); ++it) {
|
|
||||||
ImGui::Text("%s\n", (*it).c_str());
|
|
||||||
ImGui::Separator();
|
|
||||||
}
|
|
||||||
ImGui::PopTextWrapPos();
|
|
||||||
|
|
||||||
ImGui::Dummy(ImVec2(width * 0.8f, 0)); ImGui::SameLine(); // right align
|
|
||||||
if (ImGui::Button("Ok", ImVec2(width * 0.2f, 0))) {
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
// messages have been seen
|
|
||||||
messages.clear();
|
|
||||||
}
|
|
||||||
ImGui::SetItemDefaultFocus();
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// template info panel
|
// template info panel
|
||||||
inline void InfosPane(std::string vFilter, bool *vCantContinue)
|
inline void InfosPane(std::string vFilter, bool *vCantContinue)
|
||||||
// if vCantContinue is false, the user cant validate the dialog
|
// if vCantContinue is false, the user cant validate the dialog
|
||||||
@@ -491,7 +416,8 @@ void MainWindow::ShowStats(bool* p_open)
|
|||||||
ImGui::Text("Mouse (%.1f,%.1f)", io.MousePos.x, io.MousePos.y);
|
ImGui::Text("Mouse (%.1f,%.1f)", io.MousePos.x, io.MousePos.y);
|
||||||
else
|
else
|
||||||
ImGui::Text("Mouse <invalid>");
|
ImGui::Text("Mouse <invalid>");
|
||||||
|
|
||||||
|
ImGui::Text("Window (%.1f,%.1f)", io.DisplaySize.x, io.DisplaySize.y);
|
||||||
ImGui::Text("Rendering %.1f FPS", io.Framerate);
|
ImGui::Text("Rendering %.1f FPS", io.Framerate);
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|
||||||
@@ -540,7 +466,7 @@ static void ShowAboutOpengl(bool* p_open)
|
|||||||
ImGui::Text("OpenGL is the premier environment for developing portable, \ninteractive 2D and 3D graphics applications.");
|
ImGui::Text("OpenGL is the premier environment for developing portable, \ninteractive 2D and 3D graphics applications.");
|
||||||
|
|
||||||
if ( ImGui::Button( ICON_FA_EXTERNAL_LINK_ALT " https://www.opengl.org") )
|
if ( ImGui::Button( ICON_FA_EXTERNAL_LINK_ALT " https://www.opengl.org") )
|
||||||
OpenWebpage("https://www.opengl.org");
|
ImGuiToolkit::OpenWebpage("https://www.opengl.org");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
// static std::string allextensions( glGetString(GL_EXTENSIONS) );
|
// static std::string allextensions( glGetString(GL_EXTENSIONS) );
|
||||||
@@ -606,7 +532,7 @@ static void ShowAboutGStreamer(bool* p_open)
|
|||||||
ImGui::Text("GStreamer is licensed under the LGPL License.");
|
ImGui::Text("GStreamer is licensed under the LGPL License.");
|
||||||
|
|
||||||
if ( ImGui::Button( ICON_FA_EXTERNAL_LINK_ALT " https://gstreamer.freedesktop.org") )
|
if ( ImGui::Button( ICON_FA_EXTERNAL_LINK_ALT " https://gstreamer.freedesktop.org") )
|
||||||
OpenWebpage("https://gstreamer.freedesktop.org/");
|
ImGuiToolkit::OpenWebpage("https://gstreamer.freedesktop.org/");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
static bool show_config_info = false;
|
static bool show_config_info = false;
|
||||||
@@ -694,10 +620,10 @@ void MainWindow::Render()
|
|||||||
|
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem( ICON_FA_FILE_UPLOAD " Open", "Ctrl+O")) {
|
if (ImGui::MenuItem( ICON_FA_FILE_UPLOAD " Open", "Ctrl+O")) {
|
||||||
UserInterface::OpenFileMedia();
|
UserInterface::manager().OpenFileMedia();
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem( ICON_FA_POWER_OFF " Quit", "Ctrl+Q")) {
|
if (ImGui::MenuItem( ICON_FA_POWER_OFF " Quit", "Ctrl+Q")) {
|
||||||
Rendering::Close();
|
Rendering::manager().Close();
|
||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
@@ -784,17 +710,17 @@ void MainWindow::Render()
|
|||||||
screenshot_step = 2;
|
screenshot_step = 2;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
Rendering::RequestScreenshot();
|
Rendering::manager().RequestScreenshot();
|
||||||
screenshot_step = 3;
|
screenshot_step = 3;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
if ( Rendering::CurrentScreenshot()->IsFull() ){
|
if ( Rendering::manager().CurrentScreenshot()->IsFull() ){
|
||||||
std::time_t t = std::time(0); // get time now
|
std::time_t t = std::time(0); // get time now
|
||||||
std::tm* now = std::localtime(&t);
|
std::tm* now = std::localtime(&t);
|
||||||
std::string filename = ImGuiToolkit::DateTime() + "_vmixcapture.png";
|
std::string filename = ImGuiToolkit::DateTime() + "_vmixcapture.png";
|
||||||
Rendering::CurrentScreenshot()->SaveFile( filename.c_str() );
|
Rendering::manager().CurrentScreenshot()->SaveFile( filename.c_str() );
|
||||||
Rendering::CurrentScreenshot()->Clear();
|
Rendering::manager().CurrentScreenshot()->Clear();
|
||||||
}
|
}
|
||||||
screenshot_step = 4;
|
screenshot_step = 4;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,6 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
class MessageBox
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
list<string> messages;
|
|
||||||
|
|
||||||
MessageBox();
|
|
||||||
void Append(const string& text);
|
|
||||||
void Render(float width);
|
|
||||||
};
|
|
||||||
|
|
||||||
class MainWindow
|
class MainWindow
|
||||||
{
|
{
|
||||||
bool show_overlay_stats;
|
bool show_overlay_stats;
|
||||||
@@ -41,39 +31,48 @@ public:
|
|||||||
|
|
||||||
class UserInterface
|
class UserInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
// own implementation of ImGui
|
// own implementation of ImGui
|
||||||
static unsigned int textureicons;
|
unsigned int textureicons;
|
||||||
static MainWindow mainwindow;
|
MainWindow mainwindow;
|
||||||
static MessageBox warnings;
|
std::string currentFileDialog;
|
||||||
static std::string currentFileDialog;
|
std::string currentTextEdit;
|
||||||
static std::string currentTextEdit;
|
|
||||||
|
|
||||||
static void handleKeyboard();
|
void handleKeyboard();
|
||||||
static void handleMouse();
|
void handleMouse();
|
||||||
|
|
||||||
|
// Private Constructor
|
||||||
|
UserInterface();
|
||||||
|
UserInterface(UserInterface const& copy); // Not Implemented
|
||||||
|
UserInterface& operator=(UserInterface const& copy); // Not Implemented
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
static UserInterface& manager()
|
||||||
|
{
|
||||||
|
// The only instance
|
||||||
|
static UserInterface _instance;
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
// pre-loop initialization
|
// pre-loop initialization
|
||||||
static bool Init();
|
bool Init();
|
||||||
// loop update start new frame
|
// loop update start new frame
|
||||||
static void NewFrame();
|
void NewFrame();
|
||||||
// loop update rendering
|
// loop update rendering
|
||||||
static void Render();
|
void Render();
|
||||||
// Post-loop termination
|
// Post-loop termination
|
||||||
static void Terminate();
|
void Terminate();
|
||||||
// Boxer integration in OS of Error messages
|
|
||||||
static void Error(const string& text, const string& title = std::string());
|
|
||||||
// ImGui modal dialog for Warning messages
|
|
||||||
static void Warning(const char* fmt, ...);
|
|
||||||
// Open an Open File dialog for TEXT file
|
// Open an Open File dialog for TEXT file
|
||||||
static void OpenFileText();
|
void OpenFileText();
|
||||||
// Open an Open File dialog for IMAGE file
|
// Open an Open File dialog for IMAGE file
|
||||||
static void OpenFileImage();
|
void OpenFileImage();
|
||||||
// Open an Open File dialog for MEDIA file
|
// Open an Open File dialog for MEDIA file
|
||||||
static void OpenFileMedia();
|
void OpenFileMedia();
|
||||||
|
|
||||||
static void OpenTextEditor(std::string text);
|
//
|
||||||
|
void OpenTextEditor(std::string text);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* #define __UI_MANAGER_H_ */
|
#endif /* #define __UI_MANAGER_H_ */
|
||||||
|
|||||||
83
main.cpp
83
main.cpp
@@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
// vmix
|
// vmix
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "ShaderManager.h"
|
#include "Shader.h"
|
||||||
#include "SettingsManager.h"
|
#include "Settings.h"
|
||||||
#include "ResourceManager.h"
|
#include "Resource.h"
|
||||||
#include "RenderingManager.h"
|
#include "RenderingManager.h"
|
||||||
#include "UserInterfaceManager.h"
|
#include "UserInterfaceManager.h"
|
||||||
|
|
||||||
@@ -106,12 +106,10 @@ void drawMediaBackgound()
|
|||||||
if ( !testmedia2.isOpen() )
|
if ( !testmedia2.isOpen() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
// rendering TRIANGLE geometries
|
// rendering TRIANGLE geometries
|
||||||
rendering_shader.use();
|
rendering_shader.use();
|
||||||
rendering_shader.setUniform("render_projection", Rendering::Projection());
|
rendering_shader.setUniform("render_projection", Rendering::manager().Projection());
|
||||||
|
|
||||||
|
|
||||||
testmedia2.Update();
|
testmedia2.Update();
|
||||||
testmedia2.Bind();
|
testmedia2.Bind();
|
||||||
|
|
||||||
@@ -201,13 +199,28 @@ void drawMediaBackgound()
|
|||||||
|
|
||||||
void drawMediaPlayer()
|
void drawMediaPlayer()
|
||||||
{
|
{
|
||||||
|
static GstClockTime begin = GST_CLOCK_TIME_NONE;
|
||||||
|
static GstClockTime end = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
if ( !testmedia.isOpen() )
|
if ( !testmedia.isOpen() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
testmedia.Update();
|
testmedia.Update();
|
||||||
|
|
||||||
|
if ( begin == GST_CLOCK_TIME_NONE) {
|
||||||
|
|
||||||
|
begin = testmedia.Duration() / 5;
|
||||||
|
end = testmedia.Duration() / 3;
|
||||||
|
// if ( testmedia.addPlaySegment(begin, end) )
|
||||||
|
// std::cerr << " - first segment " << std::endl;
|
||||||
|
|
||||||
|
// begin *= 2;
|
||||||
|
// end *= 2;
|
||||||
|
// if ( testmedia.addPlaySegment(begin, end) )
|
||||||
|
// std::cerr << " - second segment " << std::endl;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Begin("Media Player");
|
ImGui::Begin("Media Player");
|
||||||
float width = ImGui::GetContentRegionAvail().x;
|
float width = ImGui::GetContentRegionAvail().x;
|
||||||
float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
|
float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
|
||||||
@@ -275,12 +288,26 @@ void drawMediaPlayer()
|
|||||||
|
|
||||||
guint64 current_t = testmedia.Position();
|
guint64 current_t = testmedia.Position();
|
||||||
guint64 seek_t = current_t;
|
guint64 seek_t = current_t;
|
||||||
guint64 begin = testmedia.Duration() / 5;
|
|
||||||
guint64 end = 4 * testmedia.Duration() / 5;
|
|
||||||
|
|
||||||
bool slider_pressed = ImGuiToolkit::TimelineSlider( "timeline", &seek_t, begin, end,
|
bool slider_pressed = ImGuiToolkit::TimelineSlider( "simpletimeline", &seek_t,
|
||||||
testmedia.Duration(), testmedia.FrameDuration());
|
testmedia.Duration(), testmedia.FrameDuration());
|
||||||
|
|
||||||
|
// std::list<std::pair<guint64, guint64> > segments = testmedia.getPlaySegments();
|
||||||
|
// bool slider_pressed = ImGuiToolkit::TimelineSliderEdit( "timeline", &seek_t, testmedia.Duration(),
|
||||||
|
// testmedia.FrameDuration(), segments);
|
||||||
|
|
||||||
|
// if (!segments.empty()) {
|
||||||
|
// // segments have been modified
|
||||||
|
// g_print("Segments modified \n");
|
||||||
|
|
||||||
|
// for (std::list< std::pair<guint64, guint64> >::iterator s = segments.begin(); s != segments.end(); s++){
|
||||||
|
// MediaSegment newsegment(s->first, s->second);
|
||||||
|
// testmedia.removeAllPlaySegmentOverlap(newsegment);
|
||||||
|
// if (!testmedia.addPlaySegment(newsegment))
|
||||||
|
// g_print("new Segment could not be added \n");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// if the seek target time is different from the current position time
|
// if the seek target time is different from the current position time
|
||||||
// (i.e. the difference is less than one frame)
|
// (i.e. the difference is less than one frame)
|
||||||
if ( ABS_DIFF (current_t, seek_t) > testmedia.FrameDuration() ) {
|
if ( ABS_DIFF (current_t, seek_t) > testmedia.FrameDuration() ) {
|
||||||
@@ -300,7 +327,6 @@ void drawMediaPlayer()
|
|||||||
testmedia.Play( media_play );
|
testmedia.Play( media_play );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// display info
|
// display info
|
||||||
ImGui::Text("Dimension %d x %d", testmedia.Width(), testmedia.Height());
|
ImGui::Text("Dimension %d x %d", testmedia.Width(), testmedia.Height());
|
||||||
ImGui::Text("Framerate %.2f / %.2f", testmedia.UpdateFrameRate() , testmedia.FrameRate() );
|
ImGui::Text("Framerate %.2f / %.2f", testmedia.UpdateFrameRate() , testmedia.FrameRate() );
|
||||||
@@ -320,46 +346,48 @@ int main(int, char**)
|
|||||||
///
|
///
|
||||||
/// RENDERING INIT
|
/// RENDERING INIT
|
||||||
///
|
///
|
||||||
if ( !Rendering::Init() )
|
if ( !Rendering::manager().Init() )
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// UI INIT
|
/// UI INIT
|
||||||
///
|
///
|
||||||
if ( !UserInterface::Init() )
|
if ( !UserInterface::manager().Init() )
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// GStreamer
|
/// GStreamer
|
||||||
///
|
///
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
gst_debug_set_active(TRUE);
|
|
||||||
gst_debug_set_default_threshold (GST_LEVEL_WARNING);
|
gst_debug_set_default_threshold (GST_LEVEL_WARNING);
|
||||||
|
gst_debug_set_active(TRUE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 1
|
// 1
|
||||||
// testmedia.Open("file:///home/bhbn/Images/2014-10-18_16-46-47.jpg");
|
// testmedia.Open("file:///home/bhbn/Images/2014-10-18_16-46-47.jpg");
|
||||||
// testmedia.Open("file:///home/bhbn/Videos/balls.gif");
|
// testmedia.Open("file:///home/bhbn/Videos/balls.gif");
|
||||||
// testmedia.Open("file:///home/bhbn/Videos/SIGGRAPH92_1.avi");
|
// testmedia.Open("file:///home/bhbn/Videos/SIGGRAPH92_1.avi");
|
||||||
// testmedia.Open("file:///home/bhbn/Videos/fish.mp4");
|
testmedia.Open("file:///home/bhbn/Videos/fish.mp4");
|
||||||
// testmedia.Open("file:///home/bhbn/Videos/iss.mov");
|
// testmedia.Open("file:///home/bhbn/Videos/jean/Solitude1080p.mov");
|
||||||
testmedia.Open("file:///home/bhbn/Videos/TearsOfSteel_720p_h265.mkv");
|
// testmedia.Open("file:///home/bhbn/Videos/TearsOfSteel_720p_h265.mkv");
|
||||||
// testmedia.Open("file:///home/bhbn/Videos/TestFormats/_h264GoldenLamps.mkv");
|
// testmedia.Open("file:///home/bhbn/Videos/TestFormats/_h264GoldenLamps.mkv");
|
||||||
// testmedia.Open("file:///home/bhbn/Videos/TestEncoding/vpxvp9high.webm");
|
// testmedia.Open("file:///home/bhbn/Videos/TestEncoding/vpxvp9high.webm");
|
||||||
|
|
||||||
|
// testmedia.Open("file:///home/bhbn/Videos/iss.mov");
|
||||||
testmedia.Play(false);
|
testmedia.Play(false);
|
||||||
|
|
||||||
// Add draw callbacks to the Rendering
|
// Add draw callbacks to the Rendering
|
||||||
Rendering::AddDrawCallback(drawMediaPlayer);
|
Rendering::manager().AddDrawCallback(drawMediaPlayer);
|
||||||
|
|
||||||
// 2
|
// 2
|
||||||
testmedia2.Open("file:///home/bhbn/Videos/iss.mov");
|
testmedia2.Open("file:///home/bhbn/Videos/iss.mov");
|
||||||
// testmedia2.Open("file:///home/bhbn/Images/svg/drawing.svg");
|
// testmedia2.Open("file:///home/bhbn/Images/svg/drawing.svg");
|
||||||
// testmedia2.Open("file:///home/bhbn/Videos/Upgrade.2018.720p.AMZN.WEB-DL.DDP5.1.H.264-NTG.m4v");
|
// testmedia2.Open("file:///home/bhbn/Videos/Upgrade.2018.720p.AMZN.WEB-DL.DDP5.1.H.264-NTG.m4v");
|
||||||
testmedia2.Play(true);
|
testmedia2.Play(true);
|
||||||
// create our geometries
|
// create our geometries
|
||||||
create_square(vbo, vao, ebo);
|
create_square(vbo, vao, ebo);
|
||||||
// Add draw callbacks to the Rendering
|
// Add draw callbacks to the Rendering
|
||||||
Rendering::AddDrawCallback(drawMediaBackgound);
|
Rendering::manager().AddDrawCallback(drawMediaBackgound);
|
||||||
|
|
||||||
// // load an image
|
// // load an image
|
||||||
// textureimagepng = loadPNG("/home/bhbn/Videos/iss_snap.png", &texturear);
|
// textureimagepng = loadPNG("/home/bhbn/Videos/iss_snap.png", &texturear);
|
||||||
@@ -367,20 +395,21 @@ int main(int, char**)
|
|||||||
// init shader
|
// init shader
|
||||||
rendering_shader.load("shaders/texture-shader.vs", "shaders/texture-shader.fs");
|
rendering_shader.load("shaders/texture-shader.vs", "shaders/texture-shader.fs");
|
||||||
|
|
||||||
UserInterface::OpenTextEditor( Resource::getText("shaders/texture-shader.vs") );
|
UserInterface::manager().OpenTextEditor( Resource::getText("shaders/texture-shader.vs") );
|
||||||
|
|
||||||
// Main loop
|
///
|
||||||
while ( Rendering::isActive() )
|
/// Main LOOP
|
||||||
|
///
|
||||||
|
while ( Rendering::manager().isActive() )
|
||||||
{
|
{
|
||||||
|
Rendering::manager().Draw();
|
||||||
Rendering::Draw();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
testmedia.Close();
|
testmedia.Close();
|
||||||
testmedia2.Close();
|
testmedia2.Close();
|
||||||
|
|
||||||
UserInterface::Terminate();
|
UserInterface::manager().Terminate();
|
||||||
Rendering::Terminate();
|
Rendering::manager().Terminate();
|
||||||
|
|
||||||
Settings::Save();
|
Settings::Save();
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.6 KiB |
@@ -15,13 +15,13 @@
|
|||||||
height="64"
|
height="64"
|
||||||
id="svg3744"
|
id="svg3744"
|
||||||
sodipodi:version="0.32"
|
sodipodi:version="0.32"
|
||||||
inkscape:version="0.91 r13725"
|
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||||
version="1.0"
|
version="1.0"
|
||||||
sodipodi:docname="glmixer.svg"
|
sodipodi:docname="v-mix.svg"
|
||||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||||
inkscape:export-filename="/home/bh/Developments/GraphicLiveMixer/icons/glmixer.png"
|
inkscape:export-filename="/home/bhbn/Developments/v-mix/rsc/images/v-mix_256x256.png"
|
||||||
inkscape:export-xdpi="45"
|
inkscape:export-xdpi="405.16815"
|
||||||
inkscape:export-ydpi="45">
|
inkscape:export-ydpi="405.16815">
|
||||||
<sodipodi:namedview
|
<sodipodi:namedview
|
||||||
id="base"
|
id="base"
|
||||||
pagecolor="#ffffff"
|
pagecolor="#ffffff"
|
||||||
@@ -29,9 +29,9 @@
|
|||||||
borderopacity="1.0"
|
borderopacity="1.0"
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="5.8802804"
|
inkscape:zoom="16.631945"
|
||||||
inkscape:cx="-5.1163318"
|
inkscape:cx="40.007713"
|
||||||
inkscape:cy="38.373326"
|
inkscape:cy="45.667662"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
@@ -41,11 +41,11 @@
|
|||||||
showborder="true"
|
showborder="true"
|
||||||
objecttolerance="1"
|
objecttolerance="1"
|
||||||
guidetolerance="10000"
|
guidetolerance="10000"
|
||||||
inkscape:window-width="2048"
|
inkscape:window-width="2283"
|
||||||
inkscape:window-height="1126"
|
inkscape:window-height="1885"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="1302"
|
||||||
inkscape:window-y="26"
|
inkscape:window-y="105"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="0"
|
||||||
fit-margin-top="0"
|
fit-margin-top="0"
|
||||||
fit-margin-left="0"
|
fit-margin-left="0"
|
||||||
fit-margin-right="0"
|
fit-margin-right="0"
|
||||||
@@ -80,7 +80,6 @@
|
|||||||
width="260" />
|
width="260" />
|
||||||
</pattern>
|
</pattern>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient5756"
|
id="linearGradient5756"
|
||||||
osb:paint="gradient">
|
osb:paint="gradient">
|
||||||
<stop
|
<stop
|
||||||
@@ -125,18 +124,6 @@
|
|||||||
height="260"
|
height="260"
|
||||||
width="260" />
|
width="260" />
|
||||||
</pattern>
|
</pattern>
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient4165">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4167" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4169" />
|
|
||||||
</linearGradient>
|
|
||||||
<inkscape:perspective
|
<inkscape:perspective
|
||||||
sodipodi:type="inkscape:persp3d"
|
sodipodi:type="inkscape:persp3d"
|
||||||
inkscape:vp_x="0 : 8 : 1"
|
inkscape:vp_x="0 : 8 : 1"
|
||||||
@@ -175,27 +162,8 @@
|
|||||||
fx="8"
|
fx="8"
|
||||||
fy="8"
|
fy="8"
|
||||||
r="6.75"
|
r="6.75"
|
||||||
gradientUnits="userSpaceOnUse" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4165"
|
|
||||||
id="linearGradient4171"
|
|
||||||
x1="-4.0976267"
|
|
||||||
y1="-1.8129851"
|
|
||||||
x2="8.3987904"
|
|
||||||
y2="7.1147871"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
gradientTransform="matrix(0.73981462,0,0,1.055413,1.3566253,0.16867927)" />
|
gradientTransform="matrix(3.7398771,0.00904054,-0.00860863,3.5612078,2.9498521,-45.361987)" />
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4165"
|
|
||||||
id="linearGradient5990"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(0.72647568,0,0,1.0777103,6.2654335,4.8979284)"
|
|
||||||
x1="16.436571"
|
|
||||||
y1="12.486804"
|
|
||||||
x2="4.1052761"
|
|
||||||
y2="3.7362654" />
|
|
||||||
</defs>
|
</defs>
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata3749">
|
id="metadata3749">
|
||||||
@@ -205,7 +173,7 @@
|
|||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title></dc:title>
|
<dc:title />
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
@@ -214,40 +182,21 @@
|
|||||||
inkscape:label="Layer 1"
|
inkscape:label="Layer 1"
|
||||||
inkscape:groupmode="layer"
|
inkscape:groupmode="layer"
|
||||||
transform="translate(-0.8,48.8)">
|
transform="translate(-0.8,48.8)">
|
||||||
<g
|
<path
|
||||||
id="g6034"
|
id="path4090"
|
||||||
transform="matrix(4.2122429,0,0,4.2122429,-0.8979432,-50.497943)">
|
d="m 32.8,-47.128149 c -16.741138,0 -30.3281489,13.587011 -30.3281489,30.328149 0,16.74113838 13.5870109,30.328149 30.3281489,30.328149 16.741138,0 30.328149,-13.58701062 30.328149,-30.328149 0,-16.741138 -13.587011,-30.328149 -30.328149,-30.328149 z m 0,3.791019 c 14.648496,0 26.53713,11.888634 26.53713,26.53713 C 59.33713,-2.1515039 47.448496,9.7371305 32.8,9.7371305 18.151504,9.7371305 6.2628697,-2.1515039 6.2628697,-16.8 6.2628697,-31.448496 18.151504,-43.33713 32.8,-43.33713 Z"
|
||||||
<path
|
style="display:inline;overflow:visible;visibility:visible;opacity:0.2;fill:#848484;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.21224308;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"
|
inkscape:connector-curvature="0" />
|
||||||
style="display:inline;overflow:visible;visibility:visible;opacity:0.2;fill:#848484;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;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"
|
<path
|
||||||
d="M 8,0.8 C 4.0256,0.8 0.8,4.0256 0.8,8 c 0,3.9744 3.2256,7.2 7.2,7.2 3.9744,0 7.2,-3.2256 7.2,-7.2 C 15.2,4.0256 11.9744,0.8 8,0.8 Z m 0,0.9 c 3.4776,0 6.3,2.8224 6.3,6.3 0,3.4776 -2.8224,6.3 -6.3,6.3 C 4.5224,14.3 1.7,11.4776 1.7,8 1.7,4.5224 4.5224,1.7 8,1.7 Z"
|
id="path4096"
|
||||||
id="path4090" />
|
style="display:inline;overflow:visible;visibility:visible;opacity:0.80100002;fill:url(#radialGradient4161);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:4.54984188;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;enable-background:accumulate"
|
||||||
<circle
|
sodipodi:type="arc"
|
||||||
r="6.3000002"
|
sodipodi:cx="32.799999"
|
||||||
cy="8"
|
sodipodi:cy="-16.799999"
|
||||||
cx="8"
|
sodipodi:rx="24.147949"
|
||||||
style="display:inline;overflow:visible;visibility:visible;opacity:0.5;fill:url(#radialGradient4161);fill-opacity:1;fill-rule:nonzero;stroke:#b536b5;stroke-width:0.40000001;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;enable-background:accumulate"
|
sodipodi:ry="24.147949"
|
||||||
id="path4096" />
|
sodipodi:start="4.7127834"
|
||||||
<g
|
sodipodi:end="4.7119507"
|
||||||
transform="matrix(0,-1,1,0,0.00722897,15.959713)"
|
d="M 32.809524,-40.947947 A 24.147949,24.147949 0 0 1 56.947948,-16.795502 24.147949,24.147949 0 0 1 32.800529,7.34795 24.147949,24.147949 0 0 1 8.6520507,-16.794443 24.147949,24.147949 0 0 1 32.789416,-40.947946 l 0.01058,24.147947 z" />
|
||||||
id="g6011">
|
|
||||||
<rect
|
|
||||||
style="fill:url(#linearGradient4171);fill-opacity:1;stroke:#f0ff2a;stroke-width:0.17672691;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="rect4163"
|
|
||||||
width="6.7404575"
|
|
||||||
height="6.4997482"
|
|
||||||
x="2.2274544"
|
|
||||||
y="2.2708783"
|
|
||||||
ry="0.64272904" />
|
|
||||||
<rect
|
|
||||||
ry="0.65630776"
|
|
||||||
y="7.0445399"
|
|
||||||
x="7.1205611"
|
|
||||||
height="6.6370654"
|
|
||||||
width="6.618926"
|
|
||||||
id="rect5988"
|
|
||||||
style="fill:url(#linearGradient5990);fill-opacity:1;stroke:#f0ff2a;stroke-width:0.17696671;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 322 KiB After Width: | Height: | Size: 321 KiB |
BIN
rsc/images/v-mix_256x256.png
Normal file
BIN
rsc/images/v-mix_256x256.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Reference in New Issue
Block a user