Initial commit

This commit is contained in:
BHBN
2020-03-06 21:16:51 +01:00
committed by GitHub
parent 67e9c29a01
commit db75874e87
26 changed files with 5314 additions and 0 deletions

550
ImGuiToolkit.cpp Normal file
View File

@@ -0,0 +1,550 @@
#include <map>
#include <ctime>
#include "imgui.h"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include "imgui_internal.h"
#define MILISECOND 1000000##L
#define SECOND 1000000000##L
#define MINUTE 60000000000##L
#include "ResourceManager.h"
#include "ImGuiToolkit.h"
#include "GstToolkit.h"
static unsigned int textureicons = 0;
static std::map <ImGuiToolkit::font_style, ImFont*>fontmap;
std::string ImGuiToolkit::DateTime(){
std::time_t t = std::time(0); // get time now
std::tm* now = std::localtime(&t);
std::string datetime = std::to_string(now->tm_year + 1900) +
std::to_string(now->tm_mon + 1) + std::to_string(now->tm_mday) +
std::to_string(now->tm_hour) + std::to_string(now->tm_min) +
std::to_string(now->tm_sec);
return datetime;
}
void ImGuiToolkit::Icon(int i, int j)
{
// icons.dds is a 20 x 20 grid of icons
if (textureicons == 0)
textureicons = Resource::getTextureDDS("images/icons.dds");
ImVec2 uv0( static_cast<float>(i) * 0.05, static_cast<float>(j) * 0.05 );
ImVec2 uv1( uv0.x + 0.05, uv0.y + 0.05 );
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(24, 24), uv0, uv1);
}
bool ImGuiToolkit::ButtonIcon(int i, int j)
{
// icons.dds is a 20 x 20 grid of icons
if (textureicons == 0)
textureicons = Resource::getTextureDDS("images/icons.dds");
ImVec2 uv0( static_cast<float>(i) * 0.05, static_cast<float>(j) * 0.05 );
ImVec2 uv1( uv0.x + 0.05, uv0.y + 0.05 );
ImGui::PushID( i*20 + j);
bool ret = ImGui::ImageButton((void*)(intptr_t)textureicons, ImVec2(24,24), uv0, uv1, 3);
ImGui::PopID();
return ret;
}
void ImGuiToolkit::ShowIconsWindow(bool* p_open)
{
if (textureicons == 0)
textureicons = Resource::getTextureDDS("images/icons.dds");
ImGuiIO& io = ImGui::GetIO();
float my_tex_w = 640.0;
float my_tex_h = 640.0;
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();
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(640, 640));
if (ImGui::IsItemHovered())
{
float zoom = 4.0f;
float region_sz = 32.0f; // 64 x 64 icons
float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
if (region_x < 0.0f) region_x = 0.0f; else if (region_x > my_tex_w - region_sz) region_x = my_tex_w - region_sz;
float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;
if (region_y < 0.0f) region_y = 0.0f; else if (region_y > my_tex_h - region_sz) region_y = my_tex_h - region_sz;
ImGui::BeginTooltip();
int i = (int) ( (region_x + region_sz * 0.5f) / region_sz);
int j = (int) ( (region_y + region_sz * 0.5f)/ region_sz);
ImGuiToolkit::Icon(i, j);
ImGui::SameLine();
ImGui::Text(" Icon (%d, %d)", i, j);
ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), ImVec4(1.0f, 1.0f, 1.0f, 0.5f));
ImGui::EndTooltip();
}
ImGui::End();
}
bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 begin, guint64 end, 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 };
bool value_changed = false;
// get window
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return value_changed;
// get style
const ImGuiStyle& style = ImGui::GetStyle();
const float fontsize = ImGui::GetFontSize();
ImGuiContext& g = *GImGui;
const ImGuiID id = window->GetID(label);
// widget bounding box
ImVec2 pos = window->DC.CursorPos;
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), (fontsize + style.FramePadding.y)* 2.0f);
ImRect bbox(pos, pos + size);
ImGui::ItemSize(size, style.FramePadding.y);
if (!ImGui::ItemAdd(bbox, id))
return value_changed;
// cursor size
const float cursor_scale = 1.f;
const float cursor_width = window->DrawList->_Data->FontSize * cursor_scale;
// user input
const bool hovered = ImGui::ItemHoverable(bbox, id);
bool temp_input_is_active = ImGui::TempInputTextIsActive(id);
if (!temp_input_is_active)
{
const bool focus_requested = ImGui::FocusableItemRegister(window, id);
const bool clicked = (hovered && g.IO.MouseClicked[0]);
if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id)
{
ImGui::SetActiveID(id, window);
ImGui::SetFocusID(id, window);
ImGui::FocusWindow(window);
}
}
// Render 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);
// 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
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 )
{
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) );
}
// static guint64 t = 0;
// if (t != *time) {
// 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();
guint64 tick = 0;
while ( tick < duration)
{
// large tick mark every large tick
float tick_length = !(tick%large_tick_step) ? fontsize : style.FramePadding.y;
// draw a tick mark each step
window->DrawList->AddLine( pos, pos + ImVec2(0.f, tick_length), color);
// next tick
tick += tick_step;
float tick_percent = static_cast<float> ( static_cast<double>(tick) / static_cast<double>(duration) );
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
}
// end
window->DrawList->AddLine( timeline_bbox.GetTR(), timeline_bbox.GetTR() + ImVec2(0.f, fontsize), color);
// 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);
// Slider behavior
ImRect grab_bb;
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;
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){
//ImGuiToolkit::Log("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
color = ImGui::GetColorU32(style.Colors[ImGuiCol_SliderGrab]);
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width * 0.5f, 0.f);
ImGui::RenderArrow(window->DrawList, pos, color, ImGuiDir_Up, cursor_scale);
return value_changed;
}
void ImGuiToolkit::Bar(float value, float in, float out, float min, float max, const char* title, bool expand)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
ImVec2 size_arg = expand ? ImVec2(-FLT_MIN, 0.0f) : ImVec2(0.0f, 0.0f);
ImVec2 pos = window->DC.CursorPos;
ImVec2 size = ImGui::CalcItemSize(size_arg, ImGui::CalcItemWidth(), (g.FontSize + style.FramePadding.y)* 2.0f);
ImRect bb(pos, pos + size);
ImGui::ItemSize(size, style.FramePadding.y);
if (!ImGui::ItemAdd(bb, 0))
return;
// calculations
float range_in = in / (max - min) + min;
float range_out = out / (max - min) + min;
float slider = value / (max - min) + min;
slider = ImSaturate(slider);
// Render
ImGui::RenderFrame(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
// tics
const ImU32 col_base = ImGui::GetColorU32( ImGuiCol_PlotLines );
ImVec2 pos0 = ImLerp(bb.Min, bb.Max, 0.1);
ImVec2 pos1 = ImLerp(bb.Min, bb.Max, 0.2);
float step = (bb.Max.x - bb.Min.x) / 100.f;
int i = 0;
for (float tic = bb.Min.x; tic < bb.Max.x; tic += step, ++i) {
float tic_lengh = i % 10 ? style.FramePadding.y : g.FontSize;
window->DrawList->AddLine( ImVec2(tic, bb.Min.y), ImVec2(tic, bb.Min.y + tic_lengh), col_base);
}
bb.Min.y += g.FontSize;
bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, slider), bb.Max.y);
ImGui::RenderRectFilledRangeH(window->DrawList, bb, ImGui::GetColorU32(ImGuiCol_CheckMark), range_in, range_out, style.FrameRounding);
char overlay_buf[32];
ImVec2 overlay_size = ImVec2(0.f, 0.f);
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f", in);
overlay_size = ImGui::CalcTextSize(overlay_buf, NULL);
if (overlay_size.x > 0.0f)
ImGui::RenderTextClipped(ImVec2( ImClamp(fill_br.x + style.ItemSpacing.x,bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y),
bb.Max, title, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb);
// Draw handle to move cursor (seek position)
// ImRect grab_bb;
// grab_bb.Min.x =
// window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, ImGui::GetColorU32(ImGuiCol_SliderGrabActive), style.GrabRounding);
// Draw cursor (actual position)
ImU32 color = ImGui::GetColorU32(1 ? ImGuiCol_Text : ImGuiCol_TextDisabled);
ImGui::RenderArrow(window->DrawList, pos0, color, ImGuiDir_Up);
// window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, ImGui::GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
// Default displaying the fraction as percentage string, but user can override it
// char overlay_buf[32];
// if (!title)
// {
// ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f", fraction*100+0.01f);
// title = overlay_buf;
// }
// ImVec2 overlay_size = ImGui::CalcTextSize(title, NULL);
// if (overlay_size.x > 0.0f)
// ImGui::RenderTextClipped(ImVec2( ImClamp(fill_br.x + style.ItemSpacing.x,bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y),
// bb.Max, title, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb);
}
void ImGuiToolkit::SetFont(ImGuiToolkit::font_style style, const std::string &ttf_font_name, int pointsize)
{
// Font Atlas ImGui Management
ImGuiIO& io = ImGui::GetIO();
const ImWchar* glyph_ranges = io.Fonts->GetGlyphRangesDefault();
std::string filename = "fonts/" + ttf_font_name + ".ttf";
std::string fontname = ttf_font_name + ", " + std::to_string(pointsize) + "px";
ImFontConfig font_config;
fontname.copy(font_config.Name, 40);
font_config.FontDataOwnedByAtlas = false; // data will be copied in font atlas
font_config.OversampleH = 7;
font_config.OversampleV = 3;
// read font in Resource manager
size_t data_size = 0;
const char* data_src = Resource::getData(filename, &data_size);
// temporary copy for instanciation
void *data = malloc( sizeof (char) * data_size);
memcpy(data, data_src, data_size);
// create and add font
fontmap[style] = io.Fonts->AddFontFromMemoryTTF(data, (int)data_size, static_cast<float>(pointsize), &font_config, glyph_ranges);
free(data);
// merge in icons from Font Awesome
{
// range for only FontAwesome characters
static const ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
font_config.MergeMode = true; // Merging glyphs into current font
font_config.PixelSnapH = true;
fontname = "icons" + fontname;
fontname.copy(font_config.Name, 40);
// load FontAwesome only once
static size_t icons_data_size = 0;
static const char* icons_data_src = Resource::getData( "fonts/" FONT_ICON_FILE_NAME_FAS, &icons_data_size);
// temporary copy for instanciation
void *icons_data = malloc( sizeof (char) * icons_data_size);
memcpy(icons_data, icons_data_src, icons_data_size);
// merge font
io.Fonts->AddFontFromMemoryTTF(icons_data, (int)icons_data_size, static_cast<float>(pointsize-2), &font_config, icons_ranges);
free(icons_data);
}
}
void ImGuiToolkit::PushFont(ImGuiToolkit::font_style style)
{
if (fontmap.count(style) > 0)
ImGui::PushFont( fontmap[style] );
else
ImGui::PushFont( NULL );
}
void ImGuiToolkit::SetAccentColor(accent_color color)
{
ImVec4* colors = ImGui::GetStyle().Colors;
if (color == ImGuiToolkit::ACCENT_ORANGE) {
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.55f, 0.55f, 0.55f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.13f, 0.13f, 0.14f, 0.94f);
colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.97f);
colors[ImGuiCol_Border] = ImVec4(0.69f, 0.69f, 0.69f, 0.25f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_FrameBg] = ImVec4(0.39f, 0.39f, 0.39f, 0.55f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.22f, 0.22f, 0.22f, 0.78f);
colors[ImGuiCol_TitleBg] = ImVec4(0.14f, 0.14f, 0.14f, 0.94f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.26f, 0.26f, 0.26f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.36f, 0.36f, 0.36f, 0.62f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
colors[ImGuiCol_CheckMark] = ImVec4(1.00f, 0.63f, 0.31f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.88f, 0.52f, 0.24f, 1.00f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.98f, 0.59f, 0.26f, 1.00f);
colors[ImGuiCol_Button] = ImVec4(0.47f, 0.47f, 0.47f, 0.72f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.24f, 0.24f, 0.24f, 0.90f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.24f, 0.24f, 0.24f, 0.67f);
colors[ImGuiCol_Header] = ImVec4(0.98f, 0.59f, 0.26f, 0.31f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.98f, 0.59f, 0.26f, 0.51f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.98f, 0.59f, 0.26f, 1.00f);
colors[ImGuiCol_Separator] = ImVec4(0.50f, 0.43f, 0.43f, 0.50f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.75f, 0.40f, 0.10f, 0.67f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.90f, 0.73f, 0.59f, 0.95f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.49f, 0.49f, 0.52f, 0.50f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.90f, 0.73f, 0.59f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.90f, 0.73f, 0.59f, 0.95f);
colors[ImGuiCol_Tab] = ImVec4(0.58f, 0.35f, 0.18f, 0.82f);
colors[ImGuiCol_TabHovered] = ImVec4(0.80f, 0.49f, 0.25f, 0.82f);
colors[ImGuiCol_TabActive] = ImVec4(0.80f, 0.49f, 0.25f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.15f, 0.10f, 0.07f, 0.97f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.42f, 0.26f, 0.14f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.94f, 0.57f, 0.01f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.82f, 0.00f, 1.00f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.98f, 0.59f, 0.26f, 0.64f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.98f, 0.59f, 0.26f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.13f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.10f, 0.10f, 0.10f, 0.60f);
}
else if (color == ImGuiToolkit::ACCENT_GREY) {
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.55f, 0.55f, 0.55f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.13f, 0.13f, 0.14f, 0.94f);
colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.97f);
colors[ImGuiCol_Border] = ImVec4(0.69f, 0.69f, 0.69f, 0.25f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_FrameBg] = ImVec4(0.39f, 0.39f, 0.39f, 0.55f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.39f, 0.39f, 0.39f, 0.78f);
colors[ImGuiCol_TitleBg] = ImVec4(0.14f, 0.14f, 0.14f, 0.94f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.26f, 0.26f, 0.26f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.36f, 0.36f, 0.36f, 0.62f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
colors[ImGuiCol_CheckMark] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.52f, 0.52f, 0.52f, 1.00f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.98f, 0.98f, 0.98f, 1.00f);
colors[ImGuiCol_Button] = ImVec4(0.47f, 0.47f, 0.47f, 0.72f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.24f, 0.24f, 0.24f, 0.90f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.24f, 0.24f, 0.24f, 0.67f);
colors[ImGuiCol_Header] = ImVec4(0.59f, 0.59f, 0.59f, 0.31f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.59f, 0.59f, 0.59f, 0.51f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.59f, 0.59f, 0.59f, 1.00f);
colors[ImGuiCol_Separator] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.75f, 0.75f, 0.75f, 0.67f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.75f, 0.75f, 0.75f, 0.95f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.49f, 0.49f, 0.52f, 0.50f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.90f, 0.90f, 0.90f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.90f, 0.90f, 0.90f, 0.95f);
colors[ImGuiCol_Tab] = ImVec4(0.47f, 0.47f, 0.47f, 0.82f);
colors[ImGuiCol_TabHovered] = ImVec4(0.39f, 0.39f, 0.39f, 0.82f);
colors[ImGuiCol_TabActive] = ImVec4(0.47f, 0.47f, 0.47f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.10f, 0.10f, 0.10f, 0.97f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.94f, 0.57f, 0.01f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.82f, 0.00f, 1.00f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.64f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.13f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.10f, 0.10f, 0.10f, 0.60f);
}
else {
// default BLUE
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.55f, 0.55f, 0.55f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.13f, 0.13f, 0.14f, 0.94f);
colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.97f);
colors[ImGuiCol_Border] = ImVec4(0.69f, 0.69f, 0.69f, 0.25f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_FrameBg] = ImVec4(0.39f, 0.39f, 0.39f, 0.55f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.22f, 0.22f, 0.22f, 0.78f);
colors[ImGuiCol_TitleBg] = ImVec4(0.14f, 0.14f, 0.14f, 0.94f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.26f, 0.26f, 0.26f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.36f, 0.36f, 0.36f, 0.62f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
colors[ImGuiCol_CheckMark] = ImVec4(0.31f, 0.63f, 1.00f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_Button] = ImVec4(0.47f, 0.47f, 0.47f, 0.72f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.24f, 0.24f, 0.24f, 0.90f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.24f, 0.24f, 0.24f, 0.67f);
colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.51f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_Separator] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.10f, 0.40f, 0.75f, 0.67f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.59f, 0.73f, 0.90f, 0.95f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.49f, 0.49f, 0.52f, 0.50f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.59f, 0.73f, 0.90f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.59f, 0.73f, 0.90f, 0.95f);
colors[ImGuiCol_Tab] = ImVec4(0.18f, 0.35f, 0.58f, 0.82f);
colors[ImGuiCol_TabHovered] = ImVec4(0.25f, 0.49f, 0.80f, 0.82f);
colors[ImGuiCol_TabActive] = ImVec4(0.25f, 0.49f, 0.80f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.07f, 0.10f, 0.15f, 0.97f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.14f, 0.26f, 0.42f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.94f, 0.57f, 0.01f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.82f, 0.00f, 1.00f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.64f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.13f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.10f, 0.10f, 0.10f, 0.60f);
}
}
void SetButtonHighlightColor()
{
ImGui::PushStyleColor( ImGuiCol_Button, (ImVec4)ImColor::HSV( 0.35f, 0.6f, 0.6f ) );
ImGui::PushStyleColor( ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV( 0.35f, 0.8f, 0.8f ) );
ImGui::PushStyleColor( ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV( 0.35f, 0.7f, 0.7f ) );
}
void ToggleButton( const char* label, bool& toggle )
{
const auto active = toggle;
if( active ) SetButtonHighlightColor();
if( ImGui::Button( label ) ) toggle = !toggle;
if( active ) ImGui::PopStyleColor( 3 );
}

39
ImGuiToolkit.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef __IMGUI_TOOLKIT_H_
#define __IMGUI_TOOLKIT_H_
#include <glib.h>
#include <string>
#include "rsc/fonts/IconsFontAwesome5.h"
namespace ImGuiToolkit
{
// Icons from resource icon.dds
void Icon(int i, int j);
bool ButtonIcon(int i, int j);
void ShowIconsWindow(bool* p_open);
void ToggleButton( const char* label, bool& toggle );
void Bar(float value, float in, float out, float min, float max, const char* title, bool expand);
bool TimelineSlider(const char* label, guint64 *time, guint64 begin, guint64 end, guint64 duration, guint64 step);
// fonts from ressources 'fonts/'
typedef enum {
FONT_DEFAULT =0,
FONT_BOLD,
FONT_ITALIC,
FONT_MONO
} font_style;
void SetFont(font_style type, const std::string &ttf_font_name, int pointsize);
void PushFont(font_style type);
// color of gui items
typedef enum {
ACCENT_BLUE =0,
ACCENT_ORANGE,
ACCENT_GREY
} accent_color;
void SetAccentColor(accent_color color);
std::string DateTime();
};
#endif // __IMGUI_TOOLKIT_H_

135
Log.cpp Normal file
View File

@@ -0,0 +1,135 @@
#include "Log.h"
#include "imgui.h"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include "imgui_internal.h"
#include "ImGuiToolkit.h"
struct AppLog
{
ImGuiTextBuffer Buf;
ImGuiTextFilter Filter;
ImVector<int> LineOffsets;
AppLog()
{
Clear();
}
void Clear()
{
Buf.clear();
LineOffsets.clear();
LineOffsets.push_back(0);
}
void AddLog(const char* fmt, va_list args)
{
int old_size = Buf.size();
Buf.appendfv(fmt, args);
Buf.append("\n");
for (int new_size = Buf.size(); old_size < new_size; old_size++)
if (Buf[old_size] == '\n')
LineOffsets.push_back(old_size + 1);
}
void Draw(const char* title, bool* p_open = NULL)
{
if (!ImGui::Begin(title, p_open))
{
ImGui::End();
return;
}
// window
ImGui::SameLine(0, 0);
bool clear = ImGui::Button( ICON_FA_BACKSPACE " Clear");
ImGui::SameLine();
bool copy = ImGui::Button( ICON_FA_COPY " Copy");
ImGui::SameLine();
Filter.Draw("Filter", -60.0f);
ImGui::Separator();
ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_HorizontalScrollbar);
if (clear)
Clear();
if (copy)
ImGui::LogToClipboard();
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
const char* buf = Buf.begin();
const char* buf_end = Buf.end();
if (Filter.IsActive())
{
// In this example we don't use the clipper when Filter is enabled.
// This is because we don't have a random access on the result on our filter.
// A real application processing logs with ten of thousands of entries may want to store the result of search/filter.
// especially if the filtering function is not trivial (e.g. reg-exp).
for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
{
const char* line_start = buf + LineOffsets[line_no];
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
if (Filter.PassFilter(line_start, line_end))
ImGui::TextUnformatted(line_start, line_end);
}
}
else
{
// The simplest and easy way to display the entire buffer:
// ImGui::TextUnformatted(buf_begin, buf_end);
// And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines.
// Here we instead demonstrate using the clipper to only process lines that are within the visible area.
// If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended.
// Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height,
// both of which we can handle since we an array pointing to the beginning of each line of text.
// When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper.
// Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries)
ImGuiListClipper clipper;
clipper.Begin(LineOffsets.Size);
while (clipper.Step())
{
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
{
const char* line_start = buf + LineOffsets[line_no];
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
ImGui::TextUnformatted(line_start, line_end);
}
}
clipper.End();
}
ImGui::PopStyleVar();
ImGui::PopFont();
// Auto scroll
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
ImGui::End();
}
};
static AppLog logs;
void Log::Info(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
logs.AddLog(fmt, args);
va_end(args);
}
void Log::ShowLogWindow(bool* p_open)
{
ImGui::SetNextWindowSize(ImVec2(500, 600), ImGuiCond_FirstUseEver);
logs.Draw( ICON_FA_LIST_UL " Logs", p_open);
}

12
Log.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef __LOG_H_
#define __LOG_H_
namespace Log
{
// log info
void Info(const char* fmt, ...);
// Draw logs
void ShowLogWindow(bool* p_open = nullptr);
};
#endif // __LOG_H_

971
MediaPlayer.cpp Normal file
View File

@@ -0,0 +1,971 @@
#include "MediaPlayer.h"
// vmix
#include "defines.h"
#include "Log.h"
#include "RenderingManager.h"
#include "ResourceManager.h"
#include "UserInterfaceManager.h"
#include "GstToolkit.h"
// Desktop OpenGL function loader
#include <glad/glad.h>
// GStreamer
#include <gst/gl/gl.h>
#include <gst/gstformat.h>
#include <gst/pbutils/gstdiscoverer.h>
#include <gst/app/gstappsink.h>
// opengl texture
static GLuint tex_index_black = 0;
static GstStaticCaps gl_render_caps = GST_STATIC_CAPS ("video/x-raw(memory:GLMemory),format=RGBA,texture-target=2D");
static GstStaticCaps frame_render_caps = GST_STATIC_CAPS ("video/x-raw,format=RGB");
GLuint blackTexture()
{
// generate texture (once) & clear
if (tex_index_black == 0) {
glGenTextures(1, &tex_index_black);
glBindTexture( GL_TEXTURE_2D, tex_index_black);
unsigned char clearColor[3] = {0};
// texture with one black pixel
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, clearColor);
}
return tex_index_black;
}
MediaPlayer::MediaPlayer(string name) : id(name)
{
uri = "undefined";
ready = false;
seekable = false;
isimage = false;
pipeline = nullptr;
discoverer = nullptr;
width = 640;
height = 480;
position = GST_CLOCK_TIME_NONE;
duration = GST_CLOCK_TIME_NONE;
start_position = GST_CLOCK_TIME_NONE;
frame_duration = GST_CLOCK_TIME_NONE;
desired_state = GST_STATE_PAUSED;
rate = 1.0;
framerate = 1.0;
loop = LoopMode::LOOP_REWIND;
need_loop = false;
v_frame_is_full = false;
textureindex = 0;
}
MediaPlayer::~MediaPlayer()
{
Close();
// g_free(v_frame);
}
void MediaPlayer::Bind()
{
glBindTexture(GL_TEXTURE_2D, Texture());
}
guint MediaPlayer::Texture() const
{
if (textureindex == 0)
return blackTexture();
return textureindex;
}
void MediaPlayer::Open(string uri)
{
// set uri to open
this->uri = uri;
// reset
ready = false;
/* Instantiate the Discoverer */
GError *err = NULL;
discoverer = gst_discoverer_new (5 * GST_SECOND, &err);
if (!discoverer) {
Log::Info("Error creating discoverer instance: %s\n", err->message);
g_clear_error (&err);
return;
}
// set callback for filling in information into this MediaPlayer
g_signal_connect (discoverer, "discovered", G_CALLBACK (callback_discoverer_process), this);
// set callback when finished discovering
g_signal_connect (discoverer, "finished", G_CALLBACK (callback_discoverer_finished), this);
// start discoverer
gst_discoverer_start(discoverer);
// Add the request to process asynchronously the URI
if (!gst_discoverer_discover_uri_async (discoverer, uri.c_str())) {
UserInterface::Warning("Failed to start discovering URI '%s'\n", uri.c_str());
g_object_unref (discoverer);
discoverer = nullptr;
}
// and wait for discoverer to finish...
}
void MediaPlayer::execute_open()
{
// build string describing pipeline
string description = "uridecodebin uri=" + uri + " name=decoder ! videoconvert ! "
"video/x-raw,format=RGB ! appsink name=sink";
// parse pipeline descriptor
GError *error = NULL;
pipeline = gst_parse_launch (description.c_str(), &error);
if (error != NULL) {
UserInterface::Warning("Could not construct pipeline %s:\n%s\n", description.c_str(), error->message);
g_clear_error (&error);
return;
}
g_object_set(G_OBJECT(pipeline), "name", id.c_str(), NULL);
// GstCaps *caps = gst_static_caps_get (&frame_render_caps);
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());
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));
return;
}
// setup appsink
GstElement *sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
if (sink) {
// set all properties
g_object_set (sink, "emit-signals", TRUE, "sync", TRUE, "enable-last-sample", TRUE,
"wait-on-eos", FALSE, "max-buffers", 1000, "caps", caps, NULL);
// connect callbacks
g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_pull_sample_video), this);
g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_pull_sample_video), this);
g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_video), this);
// Instruct appsink to drop old buffers when the maximum amount of queued buffers is reached.
// here max-buffers set to 1000
gst_app_sink_set_drop ( (GstAppSink*) sink, true);
// done with ref to sink
gst_object_unref (sink);
}
else {
UserInterface::Warning("%s: Could not configure MediaPlayer sink\n", gst_element_get_name(pipeline));
return;
}
gst_caps_unref (caps);
// capture bus signals to force a unique opengl context for all GST elements
//Rendering::LinkPipeline(GST_PIPELINE (pipeline));
// set to desired state (PLAY or PAUSE)
GstStateChangeReturn ret = gst_element_set_state (pipeline, desired_state);
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());
}
else {
Log::Info("%s: Media Player openned %s\n", gst_element_get_name(pipeline), uri.c_str());
// all good
ready = true;
}
discoverer_message.clear();
}
bool MediaPlayer::isOpen() const
{
return ready;
}
void MediaPlayer::Close()
{
// stop discovering stream
if (discoverer != nullptr) {
gst_discoverer_stop (discoverer);
g_object_unref (discoverer);
discoverer = nullptr;
}
if (!ready)
return;
/* clean up GST */
if (pipeline != nullptr) {
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
pipeline = nullptr;
}
// nothing to display
textureindex = blackTexture();
// un-ready the media player
ready = false;
}
GstClockTime MediaPlayer::Duration()
{
if (duration == GST_CLOCK_TIME_NONE && pipeline != nullptr) {
gint64 d = GST_CLOCK_TIME_NONE;
if ( gst_element_query_duration(pipeline, GST_FORMAT_TIME, &d) )
duration = d;
}
return duration;
}
GstClockTime MediaPlayer::FrameDuration()
{
return frame_duration;
}
guint MediaPlayer::Width() const
{
return width;
}
guint MediaPlayer::Height() const
{
return height;
}
float MediaPlayer::AspectRatio() const
{
return static_cast<float>(width) / static_cast<float>(height);
}
GstClockTime MediaPlayer::Position()
{
GstClockTime pos = position;
if (pos == GST_CLOCK_TIME_NONE && pipeline != nullptr) {
gint64 p = GST_CLOCK_TIME_NONE;
if ( gst_element_query_position (pipeline, GST_FORMAT_TIME, &p) )
pos = p;
}
return pos - start_position;
}
void MediaPlayer::Play(bool on)
{
if (isimage)
return;
// request state
desired_state = on ? GST_STATE_PLAYING : GST_STATE_PAUSED;
// if not ready yet, the requested state will be handled later
if ( pipeline == nullptr )
return;
// requesting to play, but stopped at end of stream : rewind first !
if ( desired_state == GST_STATE_PLAYING) {
if ( ( rate>0.0 ? duration - Position() : Position() ) < 2 * frame_duration )
Rewind();
}
// all ready, apply state change immediately
GstStateChangeReturn ret = gst_element_set_state (pipeline, desired_state);
if (ret == GST_STATE_CHANGE_FAILURE) {
UserInterface::Warning("Failed to start up Media %s\n", gst_element_get_name(pipeline));
}
else if (on)
Log::Info("Start Media %s\n", gst_element_get_name(pipeline));
else
Log::Info("Stop Media %s\n", gst_element_get_name(pipeline));
}
bool MediaPlayer::isPlaying() const
{
// image cannot play
if (isimage)
return false;
// if not ready yet, answer with requested state
if ( pipeline == nullptr )
return desired_state == GST_STATE_PLAYING;
// if ready, answer with actual state
GstState state;
gst_element_get_state (pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
return state == GST_STATE_PLAYING;
// return desired_state == GST_STATE_PLAYING;
}
MediaPlayer::LoopMode MediaPlayer::Loop() const
{
return loop;
}
void MediaPlayer::setLoop(MediaPlayer::LoopMode mode)
{
loop = mode;
}
void MediaPlayer::Rewind()
{
if (!seekable)
return;
if (rate > 0.0)
// playing forward, loop to begin
execute_seek_command(0);
else
// playing backward, loop to end
execute_seek_command(duration);
}
void MediaPlayer::SeekNextFrame()
{
// useful only when Paused
if (isPlaying())
return;
if ( loop != LOOP_NONE) {
// eventually loop if mode allows
if ( ( rate>0.0 ? duration - Position() : Position() ) < 2 * frame_duration )
need_loop = true;
}
// step
gst_element_send_event (pipeline, gst_event_new_step (GST_FORMAT_BUFFERS, 1, ABS(rate), TRUE, FALSE));
}
void MediaPlayer::SeekTo(GstClockTime pos)
{
if (!seekable)
return;
GstClockTime target = CLAMP(pos, 0, duration);
execute_seek_command(target);
}
void MediaPlayer::FastForward()
{
if (!seekable)
return;
double step = SIGN(rate) * 0.01 * static_cast<double>(duration);
GstClockTime target = Position() + static_cast<GstClockTime>(step);
// manage loop
if ( target > duration ) {
if (loop == LOOP_NONE)
target = duration;
else
target = target - duration;
}
SeekTo(target);
}
void MediaPlayer::Update()
{
// discard
if (!ready) return;
// done discovering stream
if (discoverer != nullptr) {
gst_discoverer_stop (discoverer);
g_object_unref (discoverer);
discoverer = nullptr;
}
// apply texture
if (v_frame_is_full) {
if (textureindex==0) {
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &textureindex);
glBindTexture(GL_TEXTURE_2D, textureindex);
glPixelStorei(GL_UNPACK_ALIGNMENT, 3);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height,
0, GL_RGB, GL_UNSIGNED_BYTE, v_frame.data[0]);
}
else
{
glBindTexture(GL_TEXTURE_2D, textureindex);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height,
GL_RGB, GL_UNSIGNED_BYTE, v_frame.data[0]);
}
// sync with callback_pull_last_sample_video
v_frame_is_full = false;
}
// manage loop mode
if (need_loop && !isimage) {
if (loop == LOOP_NONE)
Play(false);
else
execute_loop_command();
need_loop = false;
}
}
void MediaPlayer::execute_loop_command()
{
if ( pipeline == nullptr) return;
if (loop==LOOP_REWIND) {
Rewind();
}
else if (loop==LOOP_BIDIRECTIONAL) {
rate *= - 1.f;
execute_seek_command();
}
}
void MediaPlayer::execute_seek_command(GstClockTime target)
{
if ( pipeline == nullptr || !seekable)
return;
GstEvent *seek_event = nullptr;
// seek position : default to target
GstClockTime seek_pos = target;
// seek speed rate
gdouble seek_rate = rate;
// seek with flush (always)
int seek_flags = GST_SEEK_FLAG_FLUSH;
// no target given
if (target == GST_CLOCK_TIME_NONE)
// create seek event with current position (rate changed ?)
seek_pos = position;
// target is given but useless
else if (target == Position())
// ignore request
return;
// seek with trick mode if fast speed
if ( ABS(seek_rate) > 2.0 )
seek_flags |= GST_SEEK_FLAG_TRICKMODE | GST_SEEK_FLAG_TRICKMODE_NO_AUDIO;
// create seek event depending on direction
if (seek_rate > 0) {
seek_event = gst_event_new_seek (seek_rate, GST_FORMAT_TIME, (GstSeekFlags) seek_flags,
GST_SEEK_TYPE_SET, seek_pos, GST_SEEK_TYPE_END, 0);
} else {
seek_event = gst_event_new_seek (seek_rate, GST_FORMAT_TIME, (GstSeekFlags) seek_flags,
GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, seek_pos);
}
/* Send the event */
if (seek_event && !gst_element_send_event(pipeline, seek_event) )
Log::Info("Seek failed in Media %s\n", gst_element_get_name(pipeline));
else
{
Log::Info("Seek Media %s %ld %f\n", gst_element_get_name(pipeline), seek_pos, seek_rate);
}
}
void MediaPlayer::SetPlaySpeed(double s)
{
if (isimage)
return;
// bound to interval [-MAX_PLAY_SPEED MAX_PLAY_SPEED]
rate = CLAMP(s, -MAX_PLAY_SPEED, MAX_PLAY_SPEED);
// skip interval [-MIN_PLAY_SPEED MIN_PLAY_SPEED]
if (ABS(rate) < MIN_PLAY_SPEED)
rate = SIGN(rate) * MIN_PLAY_SPEED;
// apply with seek
execute_seek_command();
}
double MediaPlayer::PlaySpeed() const
{
return rate;
}
double MediaPlayer::FrameRate() const
{
return framerate;
}
double MediaPlayer::UpdateFrameRate() const
{
return timecount.framerate();
}
// CALLBACKS
bool MediaPlayer::fill_v_frame(GstBuffer *buf)
{
// always empty frame before filling it again
if (v_frame.buffer)
gst_video_frame_unmap(&v_frame);
// get the frame from buffer
if ( !gst_video_frame_map (&v_frame, &v_frame_video_info, buf, GST_MAP_READ ) ) {
Log::Info("Failed to map the video buffer");
return false;
}
// validate frame format
if( GST_VIDEO_INFO_IS_RGB(&(v_frame).info) && GST_VIDEO_INFO_N_PLANES(&(v_frame).info) == 1) {
// validate time
if (position != buf->pts) {
// got a new RGB frame !
v_frame_is_full = true;
// get presentation time stamp
position = buf->pts;
// set start position (i.e. pts of first frame we got)
if (start_position == GST_CLOCK_TIME_NONE)
start_position = position;
}
}
return true;
}
GstFlowReturn MediaPlayer::callback_get_last_sample_video (GstElement *bin, MediaPlayer *m)
{
GstFlowReturn ret = GST_FLOW_OK;
if (m && !m->v_frame_is_full) {
// get last sample (non blocking)
GstSample *sample = nullptr;
g_object_get (bin, "last-sample", &sample, NULL);
if (sample != nullptr) {
GstBuffer *buf;
buf = gst_buffer_ref ( gst_sample_get_buffer (sample) );
gst_sample_unref (sample);
if ( !m->fill_v_frame(buf) )
ret = GST_FLOW_ERROR;
gst_buffer_unref (buf);
}
}
return ret;
}
GstFlowReturn MediaPlayer::callback_pull_sample_video (GstElement *bin, MediaPlayer *m)
{
GstFlowReturn ret = GST_FLOW_OK;
if (m && !m->v_frame_is_full) {
// get last sample (non blocking)
GstSample *sample = nullptr;
g_object_get (bin, "last-sample", &sample, NULL);
// if got a valid sample
if (sample != nullptr) {
// get buffer from sample
GstBuffer *buf = gst_buffer_ref ( gst_sample_get_buffer (sample) );
// fill frame from buffer
if ( !m->fill_v_frame(buf) )
ret = GST_FLOW_ERROR;
// free buffer
gst_buffer_unref (buf);
}
else
ret = GST_FLOW_FLUSHING;
// cleanup stack of samples (non blocking)
// NB : overkill as gst_app_sink_set_drop is set to TRUE, but better be safe than leak memory...
while (sample != nullptr) {
gst_sample_unref (sample);
sample = gst_app_sink_try_pull_sample( (GstAppSink * ) bin, 0 );
}
// keep update time (i.e. actual FPS of update)
m->timecount.tic();
}
return ret;
}
void MediaPlayer::callback_end_of_video (GstElement *bin, MediaPlayer *m)
{
if (m) {
// reached end of stream (eos) : might need to loop !
m->need_loop = true;
}
}
void MediaPlayer::callback_discoverer_process (GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, MediaPlayer *m)
{
// handle general errors
const gchar *uri = gst_discoverer_info_get_uri (info);
GstDiscovererResult result = gst_discoverer_info_get_result (info);
switch (result) {
case GST_DISCOVERER_URI_INVALID:
// g_print ("Invalid URI '%s'\n", uri);
m->discoverer_message << "Invalid URI: " << uri;
break;
case GST_DISCOVERER_ERROR:
// g_print ("Discoverer error: %s\n", err->message);
m->discoverer_message << "Error: " << err->message;
break;
case GST_DISCOVERER_TIMEOUT:
m->discoverer_message << "Time out";
break;
case GST_DISCOVERER_BUSY:
m->discoverer_message << "Busy";
break;
case GST_DISCOVERER_MISSING_PLUGINS:
{
const GstStructure *s = gst_discoverer_info_get_misc (info);
gchar *str = gst_structure_to_string (s);
m->discoverer_message << "Missing plugin " << str;
g_free (str);
}
break;
case GST_DISCOVERER_OK:
break;
}
// no error, handle information found
if ( result == GST_DISCOVERER_OK && m) {
// look for video stream at that uri
bool foundvideostream = false;
GList *streams = gst_discoverer_info_get_video_streams(info);
GList *tmp;
for (tmp = streams; tmp && !foundvideostream; tmp = tmp->next ) {
GstDiscovererStreamInfo *tmpinf = (GstDiscovererStreamInfo *) tmp->data;
if ( GST_IS_DISCOVERER_VIDEO_INFO(tmpinf) )
{
GstDiscovererVideoInfo* vinfo = GST_DISCOVERER_VIDEO_INFO(tmpinf);
m->width = gst_discoverer_video_info_get_width(vinfo);
m->height = gst_discoverer_video_info_get_height(vinfo);
m->isimage = gst_discoverer_video_info_is_image(vinfo);
if ( !m->isimage ) {
m->duration = gst_discoverer_info_get_duration (info);
m->seekable = gst_discoverer_info_get_seekable (info);
guint frn = gst_discoverer_video_info_get_framerate_num(vinfo);
guint frd = gst_discoverer_video_info_get_framerate_denom(vinfo);
m->framerate = static_cast<double>(frn) / static_cast<double>(frd);
m->frame_duration = (GST_SECOND * static_cast<guint64>(frd)) / (static_cast<guint64>(frn));
}
foundvideostream = true;
}
}
gst_discoverer_stream_info_list_free (streams);
if (!foundvideostream) {
m->discoverer_message << "No video stream.";
}
}
}
void MediaPlayer::callback_discoverer_finished(GstDiscoverer *discoverer, MediaPlayer *m)
{
// finished the discoverer : finalize open status
if (m)
m->execute_open();
}
TimeCounter::TimeCounter() {
current_time = gst_util_get_timestamp ();
last_time = gst_util_get_timestamp();
nbFrames = 0;
fps = 0.f;
}
void TimeCounter::tic ()
{
current_time = gst_util_get_timestamp ();
nbFrames++ ;
if ((current_time - last_time) >= GST_SECOND)
{
last_time = current_time;
fps = 0.2f * fps + 0.8f * static_cast<float>(nbFrames);
nbFrames = 0;
}
}
float TimeCounter::framerate() const
{
return fps;
}
int TimeCounter::framecount() const
{
return nbFrames;
}
// /* TESTING */
// static void cb_new_pad (GstElement* decodebin, GstPad* pad, GstElement* target)
// {
// GstPad* target_pad = gst_element_get_static_pad (target, "sink");
// if (!target_pad)
// g_warning ("target has no sink\n");
// //only link once
// if (GST_PAD_IS_LINKED (target_pad))
// {
// gst_object_unref (target_pad);
// g_warning ("target not free\n");
// return;
// }
// GstCaps* caps = gst_pad_get_current_caps (pad);
// GstStructure* str = gst_caps_get_structure (caps, 0);
// if (!g_strrstr (gst_structure_get_name (str), "video"))
// {
// gst_caps_unref (caps);
// gst_object_unref (target_pad);
// g_warning ("decodebin not a video %s\n", gst_structure_get_name (str));
// return;
// }
// gst_caps_unref (caps);
// GstPadLinkReturn ret = gst_pad_link (pad, target_pad);
// if (ret != GST_PAD_LINK_OK)
// g_warning ("Failed to link decodebin %s with target %s ! %d\n", gst_pad_get_name(pad), gst_pad_get_name(target_pad), ret);
// }
// GstElement* videosrc = gst_element_factory_make ("filesrc", "filesrc0");
// GstElement* decodebin = gst_element_factory_make ("decodebin", "decodebin0");
// // GstElement* convertbin = gst_element_factory_make ("videoconvert", "converterbin0");
// GstElement* convertbin = gst_element_factory_make ("glupload", "converterbin0");
// // GstElement* glimagesink = gst_element_factory_make ("glimagesink", "glimagesink0");
// GstElement* glimagesink = gst_element_factory_make ("glimagesinkelement", "glimagesink0");
// // GstElement* glimagesink = gst_element_factory_make ("fakesink", "glimagesink0");
// if (!videosrc || !decodebin || !convertbin || !glimagesink)
// {
// UserInterface::Error ("A Gstreamer element could not be found \n");
// return 1;
// }
// /* configure elements */
// g_object_set(G_OBJECT(videosrc), "num-buffers", 800, NULL);
// g_object_set(G_OBJECT(videosrc), "location", video_location.c_str(), NULL);
// g_signal_connect(G_OBJECT(decodebin), "drained", G_CALLBACK (endVideoCallback), NULL);
// // glimagesink
// // g_signal_connect(G_OBJECT(glimagesink), "client-draw", G_CALLBACK (drawCallback), NULL);
// // glimagesinkelement
// // g_signal_connect(G_OBJECT(glimagesink), "client-reshape", G_CALLBACK (scaleTextureCallback), NULL);
// g_signal_connect(G_OBJECT(glimagesink), "client-draw", G_CALLBACK (textureCallback), NULL);
// // // g_object_set(G_OBJECT(glimagesink), "show-preroll-frame", TRUE, NULL);
// g_object_set(G_OBJECT(glimagesink), "sync", TRUE, NULL);
// g_object_set(G_OBJECT(glimagesink), "handle-events", FALSE, NULL);
// // fakesink
// // g_signal_connect(G_OBJECT(glimagesink), "handoff", G_CALLBACK (fakesinkCallback), NULL);
// // g_object_set(G_OBJECT(glimagesink), "signal-handoffs", TRUE, NULL);
// // g_object_set(G_OBJECT(glimagesink), "sync", TRUE, NULL);
// /* add elements */
// gst_bin_add_many (GST_BIN (pipeline), videosrc, decodebin, convertbin, glimagesink, NULL);
// /* link elements */
// if (!gst_element_link (videosrc, decodebin) )
// // if (!gst_element_link_pads (videosrc, "src", decodebin, "sink") )
// {
// g_warning ("Failed to link videosrc to decodebin !\n");
// return -1;
// }
// g_signal_connect (decodebin, "pad-added", G_CALLBACK (cb_new_pad), convertbin);
// if(!gst_element_link (convertbin, glimagesink))
// {
// g_warning("Failed to link convertbin to glimagesink!\n") ;
// return -1 ;
// }
// static gboolean drawTextureCallback (GstElement *object, guint texture, guint width, guint height, GstElement *pipeline)
// {
// //printStreamPosition(pipeline);
// texture_gst = texture;
// //std::cerr << "Texture " << width << " x " << height << std::endl;
// return TRUE;
// }
// static void fakesinkCallback (GstElement *bin, GstBuffer buffer, GstPad pad, GstElement *pipeline)
// {
// static GstClockTime current_time;
// static GstClockTime last_time = gst_util_get_timestamp();
// static gint nbFrames = 0;
// current_time = gst_util_get_timestamp ();
// nbFrames++ ;
// if ((current_time - last_time) >= GST_SECOND)
// {
// //std::cerr << nbFrames << " frame / s" << std::endl;
// // GstElement *pipeline = (GstElement*)data;
// last_time = current_time;
// nbFrames = 0;
// // if (!gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
// // GST_SEEK_TYPE_SET, 0,
// // GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) {
// // g_print ("Seek failed!\n");
// // }
// }
// }
// static void endVideoCallback (GstElement *bin, GstElement *pipeline)
// {
// g_warning ("end of stream %s\n", gst_element_get_name(pipeline));
// printStreamPosition(pipeline);
// }
// video
// gchar *descr;
// // descr = g_strdup_printf ("videotestsrc pattern=ball ! video/x-raw,format=RGBA ! glimagesink");
// // descr = g_strdup_printf ("videotestsrc ! glupload ! glfilterapp name=capture ! fakesink name=sink");
// descr = g_strdup_printf ("uridecodebin uri=file:///home/bhbn/Videos/glmixervideo180324111104.mp4 name=decoder ! videoconvert ! "
// "video/x-raw,format=RGBA ! glupload ! glfilterapp name=capture ! fakesink name=sink");
// g_print ("pipeline: %s\n", descr);
// GError *error = NULL;
// GstElement *pipeline = gst_parse_launch (descr, &error);
// if (error != NULL) {
// g_print ("could not construct pipeline: %s\n", error->message);
// g_clear_error (&error);
// exit (-1);
// }
// g_object_set(G_OBJECT(pipeline), "name", "vmixpipeline", NULL);
// GstElement *capture = gst_bin_get_by_name (GST_BIN (pipeline), "capture");
// if (capture)
// g_signal_connect(G_OBJECT(capture), "client-draw", G_CALLBACK (drawTextureCallback), pipeline);
// GstElement *sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
// if (sink) {
// g_signal_connect(G_OBJECT(sink), "handoff", G_CALLBACK (fakesinkCallback), pipeline);
// g_object_set(G_OBJECT(sink), "signal-handoffs", TRUE, NULL);
// g_object_set(G_OBJECT(sink), "sync", TRUE, NULL);
// }
// GstElement *decoder = gst_bin_get_by_name (GST_BIN (pipeline), "decoder");
// if (decoder)
// g_signal_connect(G_OBJECT(decoder), "drained", G_CALLBACK (endVideoCallback), pipeline);
// // g_signal_connect(G_OBJECT(decoder), "about-to-finish", G_CALLBACK (endVideoCallback), pipeline);
// // g_timeout_add (200, (GSourceFunc) printStreamPosition, pipeline);
// // /* WORKING PIPELINE PURE OGL */
// // GstElement* pipeline = gst_pipeline_new ("pipeline");
// // GstElement *src = gst_element_factory_make ("gltestsrc", NULL);
// // // GstElement *sink = gst_element_factory_make ("glimagesinkelement", NULL);
// // // g_signal_connect(G_OBJECT(sink), "client-draw", G_CALLBACK (drawCallback), NULL);
// // // g_object_set(G_OBJECT(sink), "show-preroll-frame", FALSE, NULL);
// // // g_object_set(G_OBJECT(sink), "handle-events", FALSE, NULL);
// // GstElement *capture = gst_element_factory_make ("glfilterapp", NULL);
// // g_signal_connect(G_OBJECT(capture), "client-draw", G_CALLBACK (drawTextureCallback), NULL);
// // GstElement* sink = gst_element_factory_make ("fakesink", NULL);
// // g_signal_connect(G_OBJECT(sink), "handoff", G_CALLBACK (fakesinkCallback), NULL);
// // g_object_set(G_OBJECT(sink), "signal-handoffs", TRUE, NULL);
// // g_object_set(G_OBJECT(sink), "sync", TRUE, NULL);
// // gst_bin_add_many (GST_BIN (pipeline), src, capture, sink, NULL);
// // gst_element_link_many (src, capture, sink, NULL);
// // capture bus signals to force a unique opengl context for all GST elements
// Rendering::LinkPipeline(GST_PIPELINE (pipeline));
// /* run */
// GstStateChangeReturn ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
// if (ret == GST_STATE_CHANGE_FAILURE)
// {
// g_warning ("Failed to start up pipeline!\n");
// return -1;
// }
// string description = "uridecodebin uri=" + uri + " name=decoder ! videoconvert ! "
// "video/x-raw,format=RGBA ! glupload ! glfilterapp name=capture ! appsink name=sink";
// GError *error = NULL;
// pipeline = gst_parse_launch (description.c_str(), &error);
// if (error != NULL) {
// ImGuiToolkit::Log("Could not construct pipeline %s:\n%s\n", description.c_str(), error->message);
// g_clear_error (&error);
// return false;
// }
// g_object_set(G_OBJECT(pipeline), "name", id.c_str(), NULL);
// GstElement *capture = gst_bin_get_by_name (GST_BIN (pipeline), "capture");
// if (capture) {
// g_signal_connect(G_OBJECT(capture), "client-draw", G_CALLBACK (textureCallback), this);
// }
// GstElement *sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
// if (sink) {
// GstCaps *caps = gst_static_caps_get (&render_caps);
// g_object_set (sink, "emit-signals", TRUE, "enable-last-sample", TRUE, "sync", TRUE, "wait-on-eos", FALSE, "caps", caps, NULL);
// g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (sampleCallback), this);
// g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (sampleCallback), this);
// g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (endMediaCallback), this);
// gst_object_unref (sink);
// gst_caps_unref (caps);
// }
// // capture bus signals to force a unique opengl context for all GST elements
// Rendering::LinkPipeline(GST_PIPELINE (pipeline));
// /* set to PAUSED to make the first frame arrive in the sink */
// GstStateChangeReturn ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
// if (ret == GST_STATE_CHANGE_FAILURE) {
// ImGuiToolkit::Log("%s: Failed to open media %s\n", gst_element_get_name(pipeline), uri.c_str());
// return false;
// }

187
MediaPlayer.h Normal file
View File

@@ -0,0 +1,187 @@
#ifndef __GST_MEDIA_PLAYER_H_
#define __GST_MEDIA_PLAYER_H_
#include <string>
#include <atomic>
#include <sstream>
using namespace std;
#include <gst/gst.h>
#include <gst/gl/gl.h>
#include <gst/pbutils/pbutils.h>
#define MAX_PLAY_SPEED 20.0
#define MIN_PLAY_SPEED 0.1
struct TimeCounter {
GstClockTime current_time;
GstClockTime last_time;
int nbFrames;
float fps;
public:
TimeCounter();
void tic();
float framerate() const;
int framecount() const;
};
class MediaPlayer {
public:
/**
* Constructor of a GStreamer Media
*
*/
MediaPlayer(string name);
/**
* Destructor.
*
*/
~MediaPlayer();
/**
* Open a media using gstreamer URI
* */
void Open(string uri);
/**
* True if a media was oppenned
* */
bool isOpen() const;
/**
* Close the Media
* */
void Close();
/**
* Update status
* Must be called in update loop
* */
void Update();
/**
* Pause / Play
* Can play backward if play speed is negative
* */
void Play(bool on);
/**
* Get Pause / Play
* */
bool isPlaying() const;
/**
* Speed factor for playing
* Can be negative.
* */
double PlaySpeed() const;
/**
* Set the speed factor for playing
* Can be negative.
* */
void SetPlaySpeed(double s);
/**
* True if the player will loop when at begin or end
* */
typedef enum {
LOOP_NONE = 0,
LOOP_REWIND = 1,
LOOP_BIDIRECTIONAL = 2
} LoopMode;
LoopMode Loop() const;
/**
* Set the player to loop
* */
void setLoop(LoopMode mode);
/**
* Restart from zero
* */
void Rewind();
/**
* Seek to next frame when paused
* Can go backward if play speed is negative
* */
void SeekNextFrame();
/**
* Seek to any position in media
* pos in nanoseconds.
* */
void SeekTo(GstClockTime pos);
/**
* Jump by 10% of the duration
* */
void FastForward();
/**
* Get position time
* */
GstClockTime Position();
/**
* Get total duration time
* */
GstClockTime Duration();
/**
* Get duration of one frame
* */
GstClockTime FrameDuration();
/**
* Get framerate of the media
* */
double FrameRate() const;
/**
* Get rendering update framerate
* measured during play
* */
double UpdateFrameRate() const;
/**
* Bind / Get the OpenGL texture
* Must be called in OpenGL context
* */
void Bind();
guint Texture() const;
/**
* Get Image properties
* */
guint Width() const;
guint Height() const;
float AspectRatio() const;
private:
string id;
string uri;
guint textureindex;
guint width;
guint height;
GstClockTime position;
GstClockTime start_position;
GstClockTime duration;
GstClockTime frame_duration;
gdouble rate;
LoopMode loop;
TimeCounter timecount;
gdouble framerate;
GstState desired_state;
GstElement *pipeline;
GstDiscoverer *discoverer;
std::stringstream discoverer_message;
GstVideoFrame v_frame;
GstVideoInfo v_frame_video_info;
std::atomic<bool> v_frame_is_full;
std::atomic<bool> need_loop;
bool ready;
bool seekable;
bool isimage;
void execute_open();
void execute_loop_command();
void execute_seek_command(GstClockTime target = GST_CLOCK_TIME_NONE);
bool fill_v_frame(GstBuffer *buf);
static GstFlowReturn callback_pull_sample_video (GstElement *bin, MediaPlayer *m);
static GstFlowReturn callback_get_last_sample_video (GstElement *bin, MediaPlayer *m);
static void callback_end_of_video (GstElement *bin, MediaPlayer *m);
static void callback_discoverer_process (GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, MediaPlayer *m);
static void callback_discoverer_finished(GstDiscoverer *discoverer, MediaPlayer *m);
};
#endif // __GST_MEDIA_PLAYER_H_

504
RenderingManager.cpp Normal file
View File

@@ -0,0 +1,504 @@
#include <cstring>
#include <thread>
#include <mutex>
#include <chrono>
#include <assert.h>
// Desktop OpenGL function loader
#include <glad/glad.h> // Initialized with gladLoadGLLoader()
// Include glfw3.h after our OpenGL definitions
#define GLFW_INCLUDE_GLEXT
#include <GLFW/glfw3.h>
#ifdef APPLE
#include "osx/CocoaToolkit.h"
#define GLFW_EXPOSE_NATIVE_COCOA
#define GLFW_EXPOSE_NATIVE_NSGL
#else
#define GLFW_EXPOSE_NATIVE_X11
#define GLFW_EXPOSE_NATIVE_GLX
#endif
#include <GLFW/glfw3native.h>
#include <glm/ext/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale
#include <glm/ext/matrix_clip_space.hpp> // glm::perspective
// Include GStreamer
#include <gst/gl/gl.h>
#include <gst/gl/gstglcontext.h>
#ifdef GLFW_EXPOSE_NATIVE_COCOA
#include <gst/gl/cocoa/gstgldisplay_cocoa.h>
#endif
#ifdef GLFW_EXPOSE_NATIVE_GLX
#include <gst/gl/x11/gstgldisplay_x11.h>
#endif
// standalone image loader
#include <stb_image.h>
#include <stb_image_write.h>
// vmix
#include "defines.h"
#include "Log.h"
#include "ResourceManager.h"
#include "SettingsManager.h"
#include "UserInterfaceManager.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
static GstGLContext *global_gl_context = NULL;
static GstGLDisplay *global_display = NULL;
static guintptr global_window_handle = 0;
static void glfw_error_callback(int error, const char* description)
{
std::string msg = "Glfw Error: " + std::string(description);
UserInterface::Error(msg, APP_TITLE);
}
static void WindowRefreshCallback( GLFWwindow* window )
{
Rendering::Draw();
}
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;
}
bool Rendering::Init()
{
// Setup window
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit()){
UserInterface::Error("Failed to Initialize GLFW.");
return false;
}
// Decide GL+GLSL versions GL 3.2 + GLSL 150
glsl_version = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
#if __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
#endif
WindowSettings winset = Settings::application.windows.front();
// Create window with graphics context
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
window = glfwCreateWindow(winset.w, winset.h, winset.name.c_str(), NULL, NULL);
if (window == NULL){
UserInterface::Error("Failed to Create GLFW Window.");
return false;
}
// Add application icon
size_t fpsize = 0;
const char *fp = Resource::getData("images/glmixer_256x256.png", &fpsize);
if (fp != nullptr) {
GLFWimage icon;
icon.pixels = stbi_load_from_memory( (const stbi_uc*)fp, fpsize, &icon.width, &icon.height, nullptr, 4 );
glfwSetWindowIcon( window, 1, &icon );
free( icon.pixels );
}
glfwSetWindowPos(window, winset.x, winset.y);
glfwMakeContextCurrent(window);
glfwSwapInterval(1); // Enable vsync3
glfwSetWindowRefreshCallback( window, WindowRefreshCallback );
// Initialize OpenGL loader
bool err = gladLoadGLLoader((GLADloadproc) glfwGetProcAddress) == 0;
if (err) {
UserInterface::Error("Failed to initialize OpenGL loader.");
return false;
}
// show window
glfwShowWindow(window);
// restore fullscreen
if (winset.fullscreen)
ToggleFullscreen();
// Rendering area (not necessarily same as window)
glfwGetFramebufferSize(window, &render_width, &render_height);
glViewport(0, 0, render_width, render_height);
// Gstreamer link to context
g_setenv ("GST_GL_API", "opengl3", FALSE);
gst_init (NULL, NULL);
#if GST_GL_HAVE_PLATFORM_WGL
context =
gst_gl_context_new_wrapped (display, (guintptr) wglGetCurrentContext (),
GST_GL_PLATFORM_WGL, GST_GL_API_OPENGL);
#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;
// GstGLDisplay *gst_display;
//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
//
global_display = (GstGLDisplay*) gst_gl_display_x11_new_with_display( glfwGetX11Display() );
global_gl_context = gst_gl_context_new_wrapped (global_display,
(guintptr) glfwGetGLXContext(window),
GST_GL_PLATFORM_GLX, GST_GL_API_OPENGL);
global_window_handle = (guintptr) glfwGetX11Window(window);
#endif
// TODO : force GPU decoding
// GstElementFactory *vdpauh264dec = gst_element_factory_find("vdpauh264dec");
// if (vdpauh264dec)
// gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE(vdpauh264dec), GST_RANK_PRIMARY); // or GST_RANK_MARGINAL
// GstElementFactory *vdpaumpeg4dec = gst_element_factory_find("vdpaumpeg4dec");
// if (vdpaumpeg4dec)
// gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE(vdpaumpeg4dec), GST_RANK_PRIMARY);
// GstElementFactory *vdpaumpegdec = gst_element_factory_find("vdpaumpegdec");
// if (vdpaumpegdec)
// gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE(vdpaumpegdec), GST_RANK_PRIMARY);
// file drop callback
glfwSetDropCallback(window, Rendering::FileDropped);
return true;
}
bool Rendering::isActive()
{
return !glfwWindowShouldClose(window);
}
void Rendering::AddDrawCallback(RenderingCallback function)
{
drawCallbacks.push_back(function);
}
void Rendering::Draw()
{
if (Rendering::Begin() )
{
UserInterface::NewFrame();
std::list<Rendering::RenderingCallback>::iterator iter;
for (iter=drawCallbacks.begin(); iter != drawCallbacks.end(); iter++)
{
(*iter)();
}
UserInterface::Render();
Rendering::End();
}
// no g_main_loop_run(loop) : update global GMainContext
g_main_context_iteration(NULL, FALSE);
}
bool Rendering::Begin()
{
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
glfwPollEvents();
glfwMakeContextCurrent(window);
if( glfwGetWindowAttrib( window, GLFW_ICONIFIED ) )
{
std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) );
return false;
}
// handle window resize
glfwGetFramebufferSize(window, &render_width, &render_height);
// handle window resize
glViewport(0, 0, render_width, render_height);
// GL Colors
glClearColor(0.2f, 0.2f, 0.2f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
return true;
}
void Rendering::End()
{
glfwMakeContextCurrent(window);
// perform screenshot if requested
if (request_screenshot) {
window_screenshot.CreateFromCaptureGL(0, 0, render_width, render_height);
request_screenshot = false;
}
// swap GL buffers
glfwSwapBuffers(window);
}
void Rendering::Terminate()
{
// settings
if ( !Settings::application.windows.front().fullscreen) {
int x, y;
glfwGetWindowPos(window, &x, &y);
Settings::application.windows.front().x = x;
Settings::application.windows.front().y = y;
glfwGetWindowSize(window,&x, &y);
Settings::application.windows.front().w = x;
Settings::application.windows.front().h = y;
}
// close window
glfwDestroyWindow(window);
glfwTerminate();
}
void Rendering::Close()
{
glfwSetWindowShouldClose(window, true);
}
void Rendering::ToggleFullscreen()
{
// if in fullscreen mode
if (glfwGetWindowMonitor(window) != nullptr) {
// set to window mode
glfwSetWindowMonitor( window, nullptr, Settings::application.windows.front().x,
Settings::application.windows.front().y,
Settings::application.windows.front().w,
Settings::application.windows.front().h, 0 );
Settings::application.windows.front().fullscreen = false;
}
// not in fullscreen mode
else {
// remember window geometry
int x, y;
glfwGetWindowPos(window, &x, &y);
Settings::application.windows.front().x = x;
Settings::application.windows.front().y = y;
glfwGetWindowSize(window,&x, &y);
Settings::application.windows.front().w = x;
Settings::application.windows.front().h = y;
// select monitor
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
const GLFWvidmode * mode = glfwGetVideoMode(monitor);
// set to fullscreen mode
glfwSetWindowMonitor( window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
Settings::application.windows.front().fullscreen = true;
}
}
float Rendering::AspectRatio()
{
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[])
{
for (int i = 0; i < path_count; ++i) {
Log::Info("Dropped file %s\n", paths[i]);
}
}
Screenshot *Rendering::CurrentScreenshot()
{
return &window_screenshot;
}
void Rendering::RequestScreenshot()
{
window_screenshot.Clear();
request_screenshot = true;
}
void Screenshot::CreateEmpty(int w, int h)
{
Clear();
Width = w;
Height = h;
Data = (unsigned int*) malloc(Width * Height * 4 * sizeof(unsigned int));
memset(Data, 0, Width * Height * 4);
}
void Screenshot::CreateFromCaptureGL(int x, int y, int w, int h)
{
Clear();
Width = w;
Height = h;
Data = (unsigned int*) malloc(Width * Height * 4);
// actual capture of frame buffer
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, Data);
// make it usable
RemoveAlpha();
FlipVertical();
}
void Screenshot::SaveFile(const char* filename)
{
if (Data)
stbi_write_png(filename, Width, Height, 4, Data, Width * 4);
}
void Screenshot::RemoveAlpha()
{
unsigned int* p = Data;
int n = Width * Height;
while (n-- > 0)
{
*p |= 0xFF000000;
p++;
}
}
void Screenshot::BlitTo(Screenshot* dst, int src_x, int src_y, int dst_x, int dst_y, int w, int h) const
{
const Screenshot* src = this;
assert(dst != src);
assert(dst != NULL);
assert(src_x >= 0 && src_y >= 0);
assert(src_x + w <= src->Width);
assert(src_y + h <= src->Height);
assert(dst_x >= 0 && dst_y >= 0);
assert(dst_x + w <= dst->Width);
assert(dst_y + h <= dst->Height);
for (int y = 0; y < h; y++)
memcpy(dst->Data + dst_x + (dst_y + y) * dst->Width, src->Data + src_x + (src_y + y) * src->Width, w * 4);
}
void Screenshot::FlipVertical()
{
int comp = 4;
int stride = Width * comp;
unsigned char* line_tmp = new unsigned char[stride];
unsigned char* line_a = (unsigned char*)Data;
unsigned char* line_b = (unsigned char*)Data + (stride * (Height - 1));
while (line_a < line_b)
{
memcpy(line_tmp, line_a, stride);
memcpy(line_a, line_b, stride);
memcpy(line_b, line_tmp, stride);
line_a += stride;
line_b -= stride;
}
delete[] line_tmp;
}

95
RenderingManager.h Normal file
View File

@@ -0,0 +1,95 @@
#ifndef __RENDERING_MANAGER_H_
#define __RENDERING_MANAGER_H_
#include <string>
#include <list>
#include <gst/gl/gl.h>
#include <glm/glm.hpp>
class Screenshot
{
int Width, Height;
unsigned int * Data;
public:
Screenshot() { Width = Height = 0; Data = nullptr; }
~Screenshot() { Clear(); }
bool IsFull() { return Data != nullptr; }
void Clear() { if (IsFull()) free(Data); Data = nullptr; }
void CreateEmpty(int w, int h);
void CreateFromCaptureGL(int x, int y, int w, int h);
void RemoveAlpha();
void FlipVertical();
void SaveFile(const char* filename);
void BlitTo(Screenshot* dst, int src_x, int src_y, int dst_x, int dst_y, int w, int h) const;
};
class Rendering
{
friend class UserInterface;
// GLFW integration in OS window management
static class GLFWwindow* window;
static Screenshot window_screenshot;
static std::string glsl_version;
static int render_width, render_height;
static bool request_screenshot;
public:
// Initialization OpenGL and GLFW window creation
static bool Init();
// true if active rendering window
static bool isActive();
// request close of the UI (Quit the program)
static void Close();
// Post-loop termination
static void Terminate();
// draw one frame
static void Draw();
// add function to call during Draw
typedef void (* RenderingCallback)(void);
static void AddDrawCallback(RenderingCallback function);
// request screenshot
static void RequestScreenshot();
// get Screenshot
static Screenshot *CurrentScreenshot();
// request fullscreen
static void ToggleFullscreen();
// get width of rendering area
static float Width() { return render_width; }
// get height of rendering area
static float Height() { return render_height; }
// get aspect ratio of rendering area
static float AspectRatio();
// get projection matrix (for sharers)
static glm::mat4 Projection();
// for opengl pipile in gstreamer
static void LinkPipeline( GstPipeline *pipeline );
private:
// loop update to begin new frame
static bool Begin();
// loop update end frame
static void End();
// list of functions to call at each Draw
static std::list<RenderingCallback> drawCallbacks;
// file drop callback
static void FileDropped(GLFWwindow* window, int path_count, const char* paths[]);
};
#endif /* #define __RENDERING_MANAGER_H_ */

225
ResourceManager.cpp Normal file
View File

@@ -0,0 +1,225 @@
#include "defines.h"
#include "ResourceManager.h"
#include "Log.h"
#include <fstream>
#include <sstream>
#include <iostream>
#include <cstring>
// Desktop OpenGL function loader
#include <glad/glad.h>
// multiplatform message box
#include <tinyfiledialogs.h>
// standalone image loader
#include "stb_image.h"
// CMake Ressource Compiler
#include <cmrc/cmrc.hpp>
CMRC_DECLARE(vmix);
std::map<std::string, unsigned int> Resource::textureIndex;
const char *Resource::getData(const std::string& path, size_t* out_file_size){
auto fs = cmrc::vmix::get_filesystem();
cmrc::file file;
const char* data = nullptr;
*out_file_size = 0;
try {
file = fs.open(path.c_str());
const auto rc_size = std::distance(file.begin(), file.end());
*out_file_size = rc_size;
cmrc::file::iterator it = file.begin();
data = static_cast<const char *>(it);
}
catch (std::system_error e) {
std::string msg = "Could not access ressource " + std::string(path);
tinyfd_messageBox(APP_TITLE, msg.c_str(), "ok", "error", 0);
}
return data;
}
std::string Resource::getText(const std::string& path){
auto fs = cmrc::vmix::get_filesystem();
cmrc::file file;
std::stringstream file_stream;
try {
file = fs.open(path.c_str());
file_stream << std::string(file.begin(), file.end()) << std::endl;
}
catch (std::system_error e) {
std::string msg = "Could not access ressource " + std::string(path);
tinyfd_messageBox(APP_TITLE, msg.c_str(), "ok", "error", 0);
}
return file_stream.str();
}
#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
unsigned int Resource::getTextureDDS(const std::string& path)
{
GLuint textureID = 0;
if (textureIndex.count(path) > 0)
return textureIndex[path];
// Get the pointer
size_t size = 0;
const char *fp = getData(path, &size);
if ( size==0 ){
std::string msg = "Empty ressource " + std::string(path);
tinyfd_messageBox(APP_TITLE, msg.c_str(), "ok", "error", 0);
return 0;
}
// verify the type of file = bytes [0 - 3]
const char *filecode = fp;
if (strncmp(filecode, "DDS ", 4) != 0){
std::string msg = "Not a DDS image " + std::string(path);
tinyfd_messageBox(APP_TITLE, msg.c_str(), "ok", "error", 0);
return 0;
}
// get the surface desc = bytes [4 - 127]
const char *header = fp + 4;
unsigned int height = *(unsigned int*)&(header[8 ]);
unsigned int width = *(unsigned int*)&(header[12]);
unsigned int linearSize = *(unsigned int*)&(header[16]);
unsigned int mipMapCount = *(unsigned int*)&(header[24]);
unsigned int fourCC = *(unsigned int*)&(header[80]);
// how big is it going to be including all mipmaps?
unsigned int bufsize;
bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize;
// get the buffer = bytes [128 - ]
const char *buffer = fp + 128;
unsigned int components = (fourCC == FOURCC_DXT1) ? 3 : 4;
unsigned int format;
switch(fourCC)
{
case FOURCC_DXT1:
format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
break;
case FOURCC_DXT3:
format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
break;
case FOURCC_DXT5:
format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
default:
{
std::string msg = "Not a DXT1, DXT3 or DXT5 texture " + std::string(path);
tinyfd_messageBox(APP_TITLE, msg.c_str(), "ok", "error", 0);
return 0;
}
}
// Create one OpenGL texture
glGenTextures(1, &textureID);
// Bind the newly created texture
glBindTexture(GL_TEXTURE_2D, textureID);
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
unsigned int offset = 0;
// load the mipmaps
for (unsigned int level = 0; level < mipMapCount && (width || height); ++level)
{
unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height,
0, size, buffer + offset);
glGenerateMipmap(GL_TEXTURE_2D);
offset += size;
width /= 2;
height /= 2;
// Deal with Non-Power-Of-Two textures.
if(width < 1) width = 1;
if(height < 1) height = 1;
}
textureIndex[path] = textureID;
return textureID;
}
unsigned int Resource::getTextureImage(const std::string& path)
{
GLuint textureID = 0;
if (textureIndex.count(path) > 0)
return textureIndex[path];
int w, h, n;
unsigned char* img;
// Get the pointer
size_t size = 0;
const char *fp = getData(path, &size);
if ( size==0 ){
std::string msg = "Empty ressource " + std::string(path);
tinyfd_messageBox(APP_TITLE, msg.c_str(), "ok", "error", 0);
return 0;
}
img = stbi_load_from_memory(reinterpret_cast<const unsigned char *>(fp), size, &w, &h, &n, 4);
if (img == NULL) {
std::string msg = "Failed to load " + std::string(path) + " : " + std::string(stbi_failure_reason());
tinyfd_messageBox(APP_TITLE, msg.c_str(), "ok", "error", 0);
return 0;
}
glGenTextures(1, &textureID);
glBindTexture( GL_TEXTURE_2D, textureID);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
//glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
stbi_image_free(img);
textureIndex[path] = textureID;
return textureID;
}
void Resource::listFiles()
{
// enter icons directory
auto fs = cmrc::vmix::get_filesystem();
cmrc::file file;
cmrc::directory_iterator it = fs.iterate_directory("");
cmrc::directory_iterator itend = it.end();
// loop over all icons
while(it != itend){
cmrc::directory_entry file = *it;
if (file.is_file()) {
std::cerr << "Found file " << file.filename() << std::endl;
}
it++;
}
}

33
ResourceManager.h Normal file
View File

@@ -0,0 +1,33 @@
#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 */

130
SettingsManager.cpp Normal file
View File

@@ -0,0 +1,130 @@
#include "SettingsManager.h"
#include "defines.h"
#include <iostream>
#include <tinyxml2.h>
using namespace tinyxml2;
#ifndef XMLCheckResult
#define XMLCheckResult(a_eResult) if (a_eResult != XML_SUCCESS) { printf("Settings warning: %i\n", a_eResult); return; }
#endif
AppSettings Settings::application(APP_NAME);
AppSettings::AppSettings(const string& name) {
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()
{
XMLDocument xmlDoc;
string s;
XMLDeclaration *pDec = xmlDoc.NewDeclaration();
xmlDoc.InsertFirstChild(pDec);
XMLElement *pRoot = xmlDoc.NewElement(application.name.c_str());
xmlDoc.InsertEndChild(pRoot);
s="Settings for "+application.name;
XMLComment *pComment = xmlDoc.NewComment(s.c_str());
pRoot->InsertEndChild(pComment);
// block: windows
{
XMLElement *windowsNode = xmlDoc.NewElement( "Windows" );
list<WindowSettings>::iterator iter;
for (iter=application.windows.begin(); iter != application.windows.end(); iter++)
{
const WindowSettings& w=*iter;
XMLElement *window = xmlDoc.NewElement( "Window" );
window->SetAttribute("name", w.name.c_str());
window->SetAttribute("x", w.x);
window->SetAttribute("y", w.y);
window->SetAttribute("w", w.w);
window->SetAttribute("h", w.h);
window->SetAttribute("f", w.fullscreen);
windowsNode->InsertEndChild(window);
}
pRoot->InsertEndChild(windowsNode);
}
XMLElement *applicationNode = xmlDoc.NewElement( "Application" );
applicationNode->SetAttribute("scale", application.scale);
applicationNode->SetAttribute("color", application.color);
pRoot->InsertEndChild(applicationNode);
XMLError eResult = xmlDoc.SaveFile(application.filename.c_str());
XMLCheckResult(eResult);
}
void Settings::Load()
{
XMLDocument xmlDoc;
XMLError eResult = xmlDoc.LoadFile(application.filename.c_str());
// do not warn if non existing file
if (eResult == XML_ERROR_FILE_NOT_FOUND)
return;
// warn on other errors
XMLCheckResult(eResult);
XMLElement *pRoot = xmlDoc.FirstChildElement(application.name.c_str());
if (pRoot == nullptr) return;
if (application.name.compare( string( pRoot->Value() ) ) != 0 )
// different root name
return;
// block: windows
{
application.windows.clear(); // trash existing list
XMLElement * pElement = pRoot->FirstChildElement("Windows");
if (pElement == nullptr) return;
XMLElement* pWindowNode = pElement->FirstChildElement("Window");
for( ; pWindowNode ; pWindowNode=pWindowNode->NextSiblingElement())
{
WindowSettings w;
const char *pName=pWindowNode->Attribute("name");
if (pName) w.name=pName;
pWindowNode->QueryIntAttribute("x", &w.x); // If this fails, original value is left as-is
pWindowNode->QueryIntAttribute("y", &w.y);
pWindowNode->QueryIntAttribute("w", &w.w);
pWindowNode->QueryIntAttribute("h", &w.h);
pWindowNode->QueryBoolAttribute("f", &w.fullscreen);
application.windows.push_back(w);
}
}
XMLElement * pElement = pRoot->FirstChildElement("Application");
if (pElement == nullptr) return;
pElement->QueryFloatAttribute("scale", &application.scale);
pElement->QueryIntAttribute("color", &application.color);
}
void Settings::Check()
{
Settings::Save();
XMLDocument xmlDoc;
XMLError eResult = xmlDoc.LoadFile(application.filename.c_str());
XMLCheckResult(eResult);
xmlDoc.Print();
}

722
UserInterfaceManager.cpp Normal file
View File

@@ -0,0 +1,722 @@
#include <iostream>
#include <cstring>
#include <fstream>
#include <sstream>
// ImGui
#include "imgui.h"
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui_internal.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
// Desktop OpenGL function loader
#include <glad/glad.h>
// Include glfw3.h after our OpenGL definitions
#include <GLFW/glfw3.h>
// multiplatform
#include <tinyfiledialogs.h>
// generic image loader
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
#ifdef _WIN32
# include <windows.h>
# include <shellapi.h>
#endif
#include "defines.h"
#include "Log.h"
#include "TextEditor.h"
#include "UserInterfaceManager.h"
#include "RenderingManager.h"
#include "ResourceManager.h"
#include "SettingsManager.h"
#include "FileDialog.h"
#include "ImGuiToolkit.h"
MessageBox UserInterface::warnings;
MainWindow UserInterface::mainwindow;
std::string UserInterface::currentFileDialog = "";
std::string UserInterface::currentTextEdit = "";
static std::thread loadThread;
static bool loadThreadDone = false;
static TextEditor editor;
static void NativeOpenFile(std::string ext)
{
char const * lTheOpenFileName;
char const * lFilterPatterns[2] = { "*.txt", "*.text" };
lTheOpenFileName = tinyfd_openFileDialog( "Open a text file", "", 2, lFilterPatterns, nullptr, 0);
if (!lTheOpenFileName)
{
tinyfd_messageBox(APP_TITLE, "Open file name is NULL. ", "ok", "warning", 1);
}
else
{
char buf[1024];
sprintf( buf, "he selected file is :\n %s", lTheOpenFileName );
tinyfd_messageBox(APP_TITLE, buf, "ok", "info", 1);
}
loadThreadDone = true;
}
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
}
bool UserInterface::Init()
{
if (Rendering::window == nullptr)
return false;
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.MouseDrawCursor = true;
// Setup Platform/Renderer bindings
ImGui_ImplGlfw_InitForOpenGL(Rendering::window, true);
ImGui_ImplOpenGL3_Init(Rendering::glsl_version.c_str());
// Setup Dear ImGui style
ImGuiToolkit::SetAccentColor(static_cast<ImGuiToolkit::accent_color>(Settings::application.color));
// Load Fonts (using resource manager, a temporary copy of the raw data is necessary)
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_DEFAULT, "Roboto-Regular", 22);
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_BOLD, "Roboto-Bold", 22);
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_ITALIC, "Roboto-Italic", 22);
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_MONO, "Hack-Regular", 20);
io.FontGlobalScale = Settings::application.scale;
// Style
ImGuiStyle& style = ImGui::GetStyle();
style.WindowPadding.x = 12.f;
style.WindowPadding.y = 6.f;
style.FramePadding.x = 10.f;
style.FramePadding.y = 5.f;
style.IndentSpacing = 22.f;
style.ItemSpacing.x = 12.f;
style.ItemSpacing.y = 4.f;
style.ItemInnerSpacing.x = 8.f;
style.ItemInnerSpacing.y = 3.f;
style.WindowRounding = 8.f;
style.ChildRounding = 4.f;
style.FrameRounding = 4.f;
style.GrabRounding = 2.f;
style.GrabMinSize = 14.f;
style.Alpha = 0.92f;
// prevent bug with imgui clipboard (null at start)
ImGui::SetClipboardText("");
return true;
}
void UserInterface::handleKeyboard()
{
ImGuiIO& io = ImGui::GetIO();
// Application "CTRL +"" Shortcuts
if ( io.KeyCtrl ) {
if (ImGui::IsKeyPressed( GLFW_KEY_Q ))
Rendering::Close();
else if (ImGui::IsKeyPressed( GLFW_KEY_O ))
UserInterface::OpenFileMedia();
else if (ImGui::IsKeyPressed( GLFW_KEY_S ))
std::cerr <<" Save File " << std::endl;
else if (ImGui::IsKeyPressed( GLFW_KEY_W ))
std::cerr <<" Close File " << std::endl;
else if (ImGui::IsKeyPressed( GLFW_KEY_L ))
mainwindow.ToggleLogs();
}
// Application F-Keys
if (ImGui::IsKeyPressed( GLFW_KEY_F12 ))
Rendering::ToggleFullscreen();
else if (ImGui::IsKeyPressed( GLFW_KEY_F11 ))
mainwindow.StartScreenshot();
}
void UserInterface::handleMouse()
{
ImGuiIO& io = ImGui::GetIO();
// if not on any window
if ( !ImGui::IsAnyWindowHovered() )
{
// Mouse wheel over background
if ( io.MouseWheel > 0) {
}
if ( ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
}
if ( ImGui::IsMouseReleased(ImGuiMouseButton_Left) )
{
}
}
}
void UserInterface::NewFrame()
{
// deal with keyboard and mouse events
handleKeyboard();
handleMouse();
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// Main window
mainwindow.Render();
}
void UserInterface::Render()
{
ImVec2 geometry(static_cast<float>(Rendering::render_width), static_cast<float>(Rendering::render_height));
// warning modal dialog
warnings.Render( geometry.x * 0.4f ); // 40% width
// file modal dialog
geometry.x *= 0.4f;
geometry.y *= 0.4f;
if ( !currentFileDialog.empty() && FileDialog::Instance()->Render(currentFileDialog.c_str(), geometry)) {
if (FileDialog::Instance()->IsOk == true) {
std::string filePathNameText = FileDialog::Instance()->GetFilepathName();
// done
currentFileDialog = "";
}
FileDialog::Instance()->CloseDialog(currentFileDialog.c_str());
}
// Rendering
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
void UserInterface::Terminate()
{
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
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
inline void InfosPane(std::string vFilter, bool *vCantContinue)
// if vCantContinue is false, the user cant validate the dialog
{
ImGui::TextColored(ImVec4(0, 1, 1, 1), "Infos Pane");
ImGui::Text("Selected Filter : %s", vFilter.c_str());
// File Manager information pannel
bool canValidateDialog = false;
ImGui::Checkbox("if not checked you cant validate the dialog", &canValidateDialog);
if (vCantContinue)
*vCantContinue = canValidateDialog;
}
inline void TextInfosPane(std::string vFilter, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog
{
ImGui::TextColored(ImVec4(0, 1, 1, 1), "Text");
static std::string filepathcurrent;
static std::string text;
// if different file selected
if ( filepathcurrent.compare(FileDialog::Instance()->GetFilepathName()) != 0)
{
filepathcurrent = FileDialog::Instance()->GetFilepathName();
// fill text with file content
std::ifstream textfilestream(filepathcurrent, std::ios::in);
if(textfilestream.is_open()){
std::stringstream sstr;
sstr << textfilestream.rdbuf();
text = sstr.str();
textfilestream.close();
}
else
{
// empty text
text = "";
}
}
// show text
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 340);
ImGui::Text("%s", text.c_str());
// release Ok button if text is not empty
if (vCantContinue)
*vCantContinue = text.size() > 0;
}
inline void ImageInfosPane(std::string vFilter, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog
{
// opengl texture
static GLuint tex = 0;
static std::string filepathcurrent;
static std::string message = "Please select an image (" + vFilter + ").";
static unsigned char* img = NULL;
static ImVec2 image_size(330, 330);
// generate texture (once) & clear
if (tex == 0) {
glGenTextures(1, &tex);
glBindTexture( GL_TEXTURE_2D, tex);
unsigned char clearColor[4] = {0};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, clearColor);
}
// if different file selected
if ( filepathcurrent.compare(FileDialog::Instance()->GetFilepathName()) != 0)
{
// remember path
filepathcurrent = FileDialog::Instance()->GetFilepathName();
// prepare texture
glBindTexture( GL_TEXTURE_2D, tex);
// load image
int w, h, n;
img = stbi_load(filepathcurrent.c_str(), &w, &h, &n, 4);
if (img != NULL) {
// apply img to texture
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
// adjust image display aspect ratio
image_size.y = image_size.x * static_cast<float>(h) / static_cast<float>(w);
// free loaded image
stbi_image_free(img);
message = FileDialog::Instance()->GetCurrentFileName() + "(" + std::to_string(w) + "x" + std::to_string(h) + ")";
}
else {
// clear image
unsigned char clearColor[4] = {0};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, clearColor);
message = "Please select an image (" + vFilter + ").";
}
}
// draw text and image
ImGui::TextColored(ImVec4(0, 1, 1, 1), "%s", message.c_str());
ImGui::Image((void*)(intptr_t)tex, image_size);
// release Ok button if image is not null
if (vCantContinue)
*vCantContinue = img!=NULL;
}
void UserInterface::OpenFileText()
{
currentFileDialog = "ChooseFileText";
FileDialog::Instance()->ClearFilterColor();
FileDialog::Instance()->SetFilterColor(".cpp", ImVec4(1,1,0,0.5));
FileDialog::Instance()->SetFilterColor(".h", ImVec4(0,1,0,0.5));
FileDialog::Instance()->SetFilterColor(".hpp", ImVec4(0,0,1,0.5));
FileDialog::Instance()->OpenDialog(currentFileDialog.c_str(), "Open Text File", ".cpp\0.h\0.hpp\0\0", ".", "",
std::bind(&TextInfosPane, std::placeholders::_1, std::placeholders::_2), 350, "Text info");
}
void UserInterface::OpenFileImage()
{
currentFileDialog = "ChooseFileImage";
FileDialog::Instance()->ClearFilterColor();
FileDialog::Instance()->SetFilterColor(".png", ImVec4(0,1,1,1.0));
FileDialog::Instance()->SetFilterColor(".jpg", ImVec4(0,1,1,1.0));
FileDialog::Instance()->SetFilterColor(".gif", ImVec4(0,1,1,1.0));
FileDialog::Instance()->OpenDialog(currentFileDialog.c_str(), "Open Image File", ".*\0.png\0.jpg\0.gif\0\0", ".", "",
std::bind(&ImageInfosPane, std::placeholders::_1, std::placeholders::_2), 350, "Image info");
}
void UserInterface::OpenFileMedia()
{
currentFileDialog = "ChooseFileMedia";
FileDialog::Instance()->ClearFilterColor();
FileDialog::Instance()->SetFilterColor(".mp4", ImVec4(0,1,1,1.0));
FileDialog::Instance()->SetFilterColor(".avi", ImVec4(0,1,1,1.0));
FileDialog::Instance()->SetFilterColor(".mov", ImVec4(0,1,1,1.0));
FileDialog::Instance()->OpenDialog(currentFileDialog.c_str(), "Open Media File", ".*\0.mp4\0.avi\0.mov\0\0", ".", "", "Media");
}
MainWindow::MainWindow()
{
show_overlay_stats = false;
show_app_about = false;
show_demo_window = false;
show_logs_window = false;
show_icons_window = false;
show_editor_window = false;
screenshot_step = 0;
}
void MainWindow::ShowStats(bool* p_open)
{
const float DISTANCE = 10.0f;
static int corner = 1;
ImGuiIO& io = ImGui::GetIO();
if (corner != -1)
{
ImVec2 window_pos = ImVec2((corner & 1) ? io.DisplaySize.x - DISTANCE : DISTANCE, (corner & 2) ? io.DisplaySize.y - DISTANCE : DISTANCE);
ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f);
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
}
ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
if (ImGui::Begin("v-mix statistics", NULL, (corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav))
{
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
if (ImGui::IsMousePosValid())
ImGui::Text("Mouse (%.1f,%.1f)", io.MousePos.x, io.MousePos.y);
else
ImGui::Text("Mouse <invalid>");
ImGui::Text("Rendering %.1f FPS", io.Framerate);
ImGui::PopFont();
if (ImGui::BeginPopupContextWindow())
{
if (ImGui::MenuItem("Custom", NULL, corner == -1)) corner = -1;
if (ImGui::MenuItem("Top-left", NULL, corner == 0)) corner = 0;
if (ImGui::MenuItem("Top-right", NULL, corner == 1)) corner = 1;
if (ImGui::MenuItem("Bottom-left", NULL, corner == 2)) corner = 2;
if (ImGui::MenuItem("Bottom-right", NULL, corner == 3)) corner = 3;
if (p_open && ImGui::MenuItem("Close")) *p_open = false;
ImGui::EndPopup();
}
}
ImGui::End();
}
void MainWindow::ToggleLogs()
{
show_logs_window = !show_logs_window;
}
void MainWindow::ToggleStats()
{
show_overlay_stats = !show_overlay_stats;
}
void MainWindow::StartScreenshot()
{
screenshot_step = 1;
}
void MainWindow::Render()
{
// We specify a default position/size in case there's no data in the .ini file. Typically this isn't required! We only do it to make the Demo applications a little more welcoming.
ImGui::SetNextWindowPos(ImVec2(40, 40), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(300, 300), ImGuiCond_FirstUseEver);
ImGui::Begin( ICON_FA_DOT_CIRCLE " v-mix", NULL, ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoCollapse);
// Menu Bar
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("File"))
{
if (ImGui::MenuItem( ICON_FA_FILE " New")) {
}
if (ImGui::MenuItem( ICON_FA_FILE_UPLOAD " Open", "Ctrl+O")) {
UserInterface::OpenFileMedia();
}
if (ImGui::MenuItem( ICON_FA_POWER_OFF " Quit", "Ctrl+Q")) {
Rendering::Close();
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Appearance"))
{
ImGui::SetNextItemWidth(200);
if ( ImGui::SliderFloat("Scale", &Settings::application.scale, 0.8f, 1.2f, "%.1f"))
ImGui::GetIO().FontGlobalScale = Settings::application.scale;
ImGui::SetNextItemWidth(200);
if ( ImGui::Combo("Color", &Settings::application.color, "Blue\0Orange\0Grey\0\0"))
ImGuiToolkit::SetAccentColor(static_cast<ImGuiToolkit::accent_color>(Settings::application.color));
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Tools"))
{
ImGui::MenuItem( ICON_FA_CODE " Shader Editor", NULL, &show_editor_window);
ImGui::MenuItem( ICON_FA_LIST " Logs", "Ctrl+L", &show_logs_window);
ImGui::MenuItem( ICON_FA_TACHOMETER_ALT " Metrics", NULL, &show_overlay_stats);
if ( ImGui::MenuItem( ICON_FA_CAMERA_RETRO " Screenshot", NULL) )
StartScreenshot();
ImGui::MenuItem("--", NULL, false, false);
ImGui::MenuItem("Icons", NULL, &show_icons_window);
ImGui::MenuItem("Demo ImGui", NULL, &show_demo_window);
ImGui::MenuItem("About ImGui", NULL, &show_app_about);
if ( ImGui::MenuItem("About Gstreamer", NULL) )
OpenWebpage("https://gstreamer.freedesktop.org/");
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
// content
ImGuiToolkit::Icon(7, 1);
ImGui::SameLine(0, 10);
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_ITALIC);
ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
ImGui::PopFont();
if( ImGui::Button( ICON_FA_FOLDER_OPEN " Open File" ) && !loadThread.joinable())
{
loadThreadDone = false;
loadThread = std::thread(NativeOpenFile, "hello");
}
if( loadThreadDone && loadThread.joinable() ) {
loadThread.join();
// TODO : deal with filename
}
ImGui::End(); // "v-mix"
if (show_logs_window)
Log::ShowLogWindow(&show_logs_window);
if (show_overlay_stats)
ShowStats(&show_overlay_stats);
if (show_editor_window)
drawTextEditor(&show_editor_window);
if (show_icons_window)
ImGuiToolkit::ShowIconsWindow(&show_icons_window);
if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window);
if (show_app_about)
ImGui::ShowAboutWindow(&show_app_about);
// taking screenshot is in 3 steps
// 1) wait 1 frame that the menu / action showing button to take screenshot disapears
// 2) wait 1 frame that rendering manager takes the actual screenshot
// 3) if rendering manager current screenshot is ok, save it
if (screenshot_step > 0) {
switch(screenshot_step) {
case 1:
screenshot_step = 2;
break;
case 2:
Rendering::RequestScreenshot();
screenshot_step = 3;
break;
case 3:
{
if ( Rendering::CurrentScreenshot()->IsFull() ){
std::time_t t = std::time(0); // get time now
std::tm* now = std::localtime(&t);
std::string filename = ImGuiToolkit::DateTime() + "_vmixcapture.png";
Rendering::CurrentScreenshot()->SaveFile( filename.c_str() );
Rendering::CurrentScreenshot()->Clear();
}
screenshot_step = 4;
}
break;
default:
screenshot_step = 0;
break;
}
}
}
void UserInterface::OpenTextEditor(std::string text)
{
currentTextEdit = text;
auto lang = TextEditor::LanguageDefinition::GLSL();
editor.SetLanguageDefinition(lang);
editor.SetText(currentTextEdit);
}
void MainWindow::drawTextEditor(bool* p_open)
{
static bool show_statusbar = true;
ImGui::Begin( ICON_FA_CODE " Shader Editor", p_open, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_MenuBar);
ImGui::SetWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("Edit"))
{
bool ro = editor.IsReadOnly();
if (ImGui::MenuItem("Read-only mode", nullptr, &ro))
editor.SetReadOnly(ro);
ImGui::Separator();
if (ImGui::MenuItem( ICON_FA_UNDO " Undo", "Ctrl-Z", nullptr, !ro && editor.CanUndo()))
editor.Undo();
if (ImGui::MenuItem( ICON_FA_REDO " Redo", "Ctrl-Y", nullptr, !ro && editor.CanRedo()))
editor.Redo();
ImGui::Separator();
if (ImGui::MenuItem( ICON_FA_COPY " Copy", "Ctrl-C", nullptr, editor.HasSelection()))
editor.Copy();
if (ImGui::MenuItem( ICON_FA_CUT " Cut", "Ctrl-X", nullptr, !ro && editor.HasSelection()))
editor.Cut();
if (ImGui::MenuItem( ICON_FA_ERASER " Delete", "Del", nullptr, !ro && editor.HasSelection()))
editor.Delete();
if (ImGui::MenuItem( ICON_FA_PASTE " Paste", "Ctrl-V", nullptr, !ro && ImGui::GetClipboardText() != nullptr))
editor.Paste();
ImGui::Separator();
if (ImGui::MenuItem( "Select all", nullptr, nullptr))
editor.SetSelection(TextEditor::Coordinates(), TextEditor::Coordinates(editor.GetTotalLines(), 0));
ImGui::EndMenu();
}
if (ImGui::BeginMenu("View"))
{
bool ws = editor.IsShowingWhitespaces();
if (ImGui::MenuItem( ICON_FA_LONG_ARROW_ALT_RIGHT " Whitespace", nullptr, &ws))
editor.SetShowWhitespaces(ws);
ImGui::MenuItem( ICON_FA_WINDOW_MAXIMIZE " Statusbar", nullptr, &show_statusbar);
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
if (show_statusbar) {
auto cpos = editor.GetCursorPosition();
ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s ", cpos.mLine + 1, cpos.mColumn + 1, editor.GetTotalLines(),
editor.IsOverwrite() ? "Ovr" : "Ins",
editor.CanUndo() ? "*" : " ",
editor.GetLanguageDefinition().mName.c_str());
}
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
editor.Render("TextEditor");
ImGui::PopFont();
ImGui::End();
}

78
UserInterfaceManager.h Normal file
View File

@@ -0,0 +1,78 @@
#ifndef __UI_MANAGER_H_
#define __UI_MANAGER_H_
#include <string>
#include <list>
using namespace std;
class MessageBox
{
public:
list<string> messages;
MessageBox();
void Append(const string& text);
void Render(float width);
};
class MainWindow
{
bool show_overlay_stats;
bool show_app_about;
bool show_demo_window;
bool show_logs_window;
bool show_icons_window;
bool show_editor_window;
unsigned int screenshot_step;
void ShowStats(bool* p_open);
void drawTextEditor(bool* p_open);
public:
MainWindow();
void ToggleLogs();
void ToggleStats();
void StartScreenshot();
void Render();
};
class UserInterface
{
// own implementation of ImGui
static unsigned int textureicons;
static MainWindow mainwindow;
static MessageBox warnings;
static std::string currentFileDialog;
static std::string currentTextEdit;
static void handleKeyboard();
static void handleMouse();
public:
// pre-loop initialization
static bool Init();
// loop update start new frame
static void NewFrame();
// loop update rendering
static void Render();
// Post-loop termination
static 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
static void OpenFileText();
// Open an Open File dialog for IMAGE file
static void OpenFileImage();
// Open an Open File dialog for MEDIA file
static void OpenFileMedia();
static void OpenTextEditor(std::string text);
};
#endif /* #define __UI_MANAGER_H_ */

617
cmake/modules/CMakeRC.cmake Normal file
View File

@@ -0,0 +1,617 @@
# This block is executed when generating an intermediate resource file, not when
# running in CMake configure mode
if(_CMRC_GENERATE_MODE)
# Read in the digits
file(READ "${INPUT_FILE}" bytes HEX)
# Format each pair into a character literal. Heuristics seem to favor doing
# the conversion in groups of five for fastest conversion
string(REGEX REPLACE "(..)(..)(..)(..)(..)" "'\\\\x\\1','\\\\x\\2','\\\\x\\3','\\\\x\\4','\\\\x\\5'," chars "${bytes}")
# Since we did this in groups, we have some leftovers to clean up
string(LENGTH "${bytes}" n_bytes2)
math(EXPR n_bytes "${n_bytes2} / 2")
math(EXPR remainder "${n_bytes} % 5") # <-- '5' is the grouping count from above
set(cleanup_re "$")
set(cleanup_sub )
while(remainder)
set(cleanup_re "(..)${cleanup_re}")
set(cleanup_sub "'\\\\x\\${remainder}',${cleanup_sub}")
math(EXPR remainder "${remainder} - 1")
endwhile()
if(NOT cleanup_re STREQUAL "$")
string(REGEX REPLACE "${cleanup_re}" "${cleanup_sub}" chars "${chars}")
endif()
string(CONFIGURE [[
namespace { const char file_array[] = { @chars@ 0 }; }
namespace cmrc { namespace @NAMESPACE@ { namespace res_chars {
extern const char* const @SYMBOL@_begin = file_array;
extern const char* const @SYMBOL@_end = file_array + @n_bytes@;
}}}
]] code)
file(WRITE "${OUTPUT_FILE}" "${code}")
# Exit from the script. Nothing else needs to be processed
return()
endif()
set(_version 2.0.0)
cmake_minimum_required(VERSION 3.3)
include(CMakeParseArguments)
if(COMMAND cmrc_add_resource_library)
if(NOT DEFINED _CMRC_VERSION OR NOT (_version STREQUAL _CMRC_VERSION))
message(WARNING "More than one CMakeRC version has been included in this project.")
endif()
# CMakeRC has already been included! Don't do anything
return()
endif()
set(_CMRC_VERSION "${_version}" CACHE INTERNAL "CMakeRC version. Used for checking for conflicts")
set(_CMRC_SCRIPT "${CMAKE_CURRENT_LIST_FILE}" CACHE INTERNAL "Path to CMakeRC script")
function(_cmrc_normalize_path var)
set(path "${${var}}")
file(TO_CMAKE_PATH "${path}" path)
while(path MATCHES "//")
string(REPLACE "//" "/" path "${path}")
endwhile()
string(REGEX REPLACE "/+$" "" path "${path}")
set("${var}" "${path}" PARENT_SCOPE)
endfunction()
get_filename_component(_inc_dir "${CMAKE_BINARY_DIR}/_cmrc/include" ABSOLUTE)
set(CMRC_INCLUDE_DIR "${_inc_dir}" CACHE INTERNAL "Directory for CMakeRC include files")
# Let's generate the primary include file
file(MAKE_DIRECTORY "${CMRC_INCLUDE_DIR}/cmrc")
set(hpp_content [==[
#ifndef CMRC_CMRC_HPP_INCLUDED
#define CMRC_CMRC_HPP_INCLUDED
#include <cassert>
#include <functional>
#include <iterator>
#include <list>
#include <map>
#include <mutex>
#include <string>
#include <system_error>
#include <type_traits>
namespace cmrc { namespace detail { struct dummy; } }
#define CMRC_DECLARE(libid) \
namespace cmrc { namespace detail { \
struct dummy; \
static_assert(std::is_same<dummy, ::cmrc::detail::dummy>::value, "CMRC_DECLARE() must only appear at the global namespace"); \
} } \
namespace cmrc { namespace libid { \
cmrc::embedded_filesystem get_filesystem(); \
} } static_assert(true, "")
namespace cmrc {
class file {
const char* _begin = nullptr;
const char* _end = nullptr;
public:
using iterator = const char*;
using const_iterator = iterator;
iterator begin() const noexcept { return _begin; }
iterator cbegin() const noexcept { return _begin; }
iterator end() const noexcept { return _end; }
iterator cend() const noexcept { return _end; }
std::size_t size() const { return std::distance(begin(), end()); }
file() = default;
file(iterator beg, iterator end) noexcept : _begin(beg), _end(end) {}
};
class directory_entry;
namespace detail {
class directory;
class file_data;
class file_or_directory {
union _data_t {
class file_data* file_data;
class directory* directory;
} _data;
bool _is_file = true;
public:
explicit file_or_directory(file_data& f) {
_data.file_data = &f;
}
explicit file_or_directory(directory& d) {
_data.directory = &d;
_is_file = false;
}
bool is_file() const noexcept {
return _is_file;
}
bool is_directory() const noexcept {
return !is_file();
}
const directory& as_directory() const noexcept {
assert(!is_file());
return *_data.directory;
}
const file_data& as_file() const noexcept {
assert(is_file());
return *_data.file_data;
}
};
class file_data {
public:
const char* begin_ptr;
const char* end_ptr;
file_data(const file_data&) = delete;
file_data(const char* b, const char* e) : begin_ptr(b), end_ptr(e) {}
};
inline std::pair<std::string, std::string> split_path(const std::string& path) {
auto first_sep = path.find("/");
if (first_sep == path.npos) {
return std::make_pair(path, "");
} else {
return std::make_pair(path.substr(0, first_sep), path.substr(first_sep + 1));
}
}
struct created_subdirectory {
class directory& directory;
class file_or_directory& index_entry;
};
class directory {
std::list<file_data> _files;
std::list<directory> _dirs;
std::map<std::string, file_or_directory> _index;
using base_iterator = std::map<std::string, file_or_directory>::const_iterator;
public:
directory() = default;
directory(const directory&) = delete;
created_subdirectory add_subdir(std::string name) & {
_dirs.emplace_back();
auto& back = _dirs.back();
auto& fod = _index.emplace(name, file_or_directory{back}).first->second;
return created_subdirectory{back, fod};
}
file_or_directory* add_file(std::string name, const char* begin, const char* end) & {
assert(_index.find(name) == _index.end());
_files.emplace_back(begin, end);
return &_index.emplace(name, file_or_directory{_files.back()}).first->second;
}
const file_or_directory* get(const std::string& path) const {
auto pair = split_path(path);
auto child = _index.find(pair.first);
if (child == _index.end()) {
return nullptr;
}
auto& entry = child->second;
if (pair.second.empty()) {
// We're at the end of the path
return &entry;
}
if (entry.is_file()) {
// We can't traverse into a file. Stop.
return nullptr;
}
// Keep going down
return entry.as_directory().get(pair.second);
}
class iterator {
base_iterator _base_iter;
base_iterator _end_iter;
public:
using value_type = directory_entry;
using difference_type = std::ptrdiff_t;
using pointer = const value_type*;
using reference = const value_type&;
using iterator_category = std::input_iterator_tag;
iterator() = default;
explicit iterator(base_iterator iter, base_iterator end) : _base_iter(iter), _end_iter(end) {}
iterator begin() const noexcept {
return *this;
}
iterator end() const noexcept {
return iterator(_end_iter, _end_iter);
}
inline value_type operator*() const noexcept;
bool operator==(const iterator& rhs) const noexcept {
return _base_iter == rhs._base_iter;
}
bool operator!=(const iterator& rhs) const noexcept {
return !(*this == rhs);
}
iterator operator++() noexcept {
auto cp = *this;
++_base_iter;
return cp;
}
iterator& operator++(int) noexcept {
++_base_iter;
return *this;
}
};
using const_iterator = iterator;
iterator begin() const noexcept {
return iterator(_index.begin(), _index.end());
}
iterator end() const noexcept {
return iterator();
}
};
inline std::string normalize_path(std::string path) {
while (path.find("/") == 0) {
path.erase(path.begin());
}
while (!path.empty() && (path.rfind("/") == path.size() - 1)) {
path.pop_back();
}
auto off = path.npos;
while ((off = path.find("//")) != path.npos) {
path.erase(path.begin() + off);
}
return path;
}
using index_type = std::map<std::string, const cmrc::detail::file_or_directory*>;
} // detail
class directory_entry {
std::string _fname;
const detail::file_or_directory* _item;
public:
directory_entry() = delete;
explicit directory_entry(std::string filename, const detail::file_or_directory& item)
: _fname(filename)
, _item(&item)
{}
const std::string& filename() const & {
return _fname;
}
std::string filename() const && {
return std::move(_fname);
}
bool is_file() const {
return _item->is_file();
}
bool is_directory() const {
return _item->is_directory();
}
};
directory_entry detail::directory::iterator::operator*() const noexcept {
assert(begin() != end());
return directory_entry(_base_iter->first, _base_iter->second);
}
using directory_iterator = detail::directory::iterator;
class embedded_filesystem {
// Never-null:
const cmrc::detail::index_type* _index;
const detail::file_or_directory* _get(std::string path) const {
path = detail::normalize_path(path);
auto found = _index->find(path);
if (found == _index->end()) {
return nullptr;
} else {
return found->second;
}
}
public:
explicit embedded_filesystem(const detail::index_type& index)
: _index(&index)
{}
file open(const std::string& path) const {
auto entry_ptr = _get(path);
if (!entry_ptr || !entry_ptr->is_file()) {
throw std::system_error(make_error_code(std::errc::no_such_file_or_directory), path);
}
auto& dat = entry_ptr->as_file();
return file{dat.begin_ptr, dat.end_ptr};
}
bool is_file(const std::string& path) const noexcept {
auto entry_ptr = _get(path);
return entry_ptr && entry_ptr->is_file();
}
bool is_directory(const std::string& path) const noexcept {
auto entry_ptr = _get(path);
return entry_ptr && entry_ptr->is_directory();
}
bool exists(const std::string& path) const noexcept {
return !!_get(path);
}
directory_iterator iterate_directory(const std::string& path) const {
auto entry_ptr = _get(path);
if (!entry_ptr) {
throw std::system_error(make_error_code(std::errc::no_such_file_or_directory), path);
}
if (!entry_ptr->is_directory()) {
throw std::system_error(make_error_code(std::errc::not_a_directory), path);
}
return entry_ptr->as_directory().begin();
}
};
}
#endif // CMRC_CMRC_HPP_INCLUDED
]==])
set(cmrc_hpp "${CMRC_INCLUDE_DIR}/cmrc/cmrc.hpp" CACHE INTERNAL "")
set(_generate 1)
if(EXISTS "${cmrc_hpp}")
file(READ "${cmrc_hpp}" _current)
if(_current STREQUAL hpp_content)
set(_generate 0)
endif()
endif()
file(GENERATE OUTPUT "${cmrc_hpp}" CONTENT "${hpp_content}" CONDITION ${_generate})
add_library(cmrc-base INTERFACE)
target_include_directories(cmrc-base INTERFACE "${CMRC_INCLUDE_DIR}")
# Signal a basic C++11 feature to require C++11.
target_compile_features(cmrc-base INTERFACE cxx_nullptr)
set_property(TARGET cmrc-base PROPERTY INTERFACE_CXX_EXTENSIONS OFF)
add_library(cmrc::base ALIAS cmrc-base)
function(cmrc_add_resource_library name)
set(args ALIAS NAMESPACE)
cmake_parse_arguments(ARG "" "${args}" "" "${ARGN}")
# Generate the identifier for the resource library's namespace
set(ns_re "[a-zA-Z_][a-zA-Z0-9_]*")
if(NOT DEFINED ARG_NAMESPACE)
# Check that the library name is also a valid namespace
if(NOT name MATCHES "${ns_re}")
message(SEND_ERROR "Library name is not a valid namespace. Specify the NAMESPACE argument")
endif()
set(ARG_NAMESPACE "${name}")
else()
if(NOT ARG_NAMESPACE MATCHES "${ns_re}")
message(SEND_ERROR "NAMESPACE for ${name} is not a valid C++ namespace identifier (${ARG_NAMESPACE})")
endif()
endif()
set(libname "${name}")
# Generate a library with the compiled in character arrays.
string(CONFIGURE [=[
#include <cmrc/cmrc.hpp>
#include <map>
#include <utility>
namespace cmrc {
namespace @ARG_NAMESPACE@ {
namespace res_chars {
// These are the files which are available in this resource library
$<JOIN:$<TARGET_PROPERTY:@libname@,CMRC_EXTERN_DECLS>,
>
}
namespace {
const cmrc::detail::index_type&
get_root_index() {
static cmrc::detail::directory root_directory_;
static cmrc::detail::file_or_directory root_directory_fod{root_directory_};
static cmrc::detail::index_type root_index;
root_index.emplace("", &root_directory_fod);
struct dir_inl {
class cmrc::detail::directory& directory;
};
dir_inl root_directory_dir{root_directory_};
(void)root_directory_dir;
$<JOIN:$<TARGET_PROPERTY:@libname@,CMRC_MAKE_DIRS>,
>
$<JOIN:$<TARGET_PROPERTY:@libname@,CMRC_MAKE_FILES>,
>
return root_index;
}
}
cmrc::embedded_filesystem get_filesystem() {
static auto& index = get_root_index();
return cmrc::embedded_filesystem{index};
}
} // @ARG_NAMESPACE@
} // cmrc
]=] cpp_content @ONLY)
get_filename_component(libdir "${CMAKE_CURRENT_BINARY_DIR}/__cmrc_${name}" ABSOLUTE)
get_filename_component(lib_tmp_cpp "${libdir}/lib_.cpp" ABSOLUTE)
string(REPLACE "\n " "\n" cpp_content "${cpp_content}")
file(GENERATE OUTPUT "${lib_tmp_cpp}" CONTENT "${cpp_content}")
get_filename_component(libcpp "${libdir}/lib.cpp" ABSOLUTE)
add_custom_command(OUTPUT "${libcpp}"
DEPENDS "${lib_tmp_cpp}" "${cmrc_hpp}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${lib_tmp_cpp}" "${libcpp}"
COMMENT "Generating ${name} resource loader"
)
# Generate the actual static library. Each source file is just a single file
# with a character array compiled in containing the contents of the
# corresponding resource file.
add_library(${name} STATIC ${libcpp})
set_property(TARGET ${name} PROPERTY CMRC_LIBDIR "${libdir}")
set_property(TARGET ${name} PROPERTY CMRC_NAMESPACE "${ARG_NAMESPACE}")
target_link_libraries(${name} PUBLIC cmrc::base)
set_property(TARGET ${name} PROPERTY CMRC_IS_RESOURCE_LIBRARY TRUE)
if(ARG_ALIAS)
add_library("${ARG_ALIAS}" ALIAS ${name})
endif()
cmrc_add_resources(${name} ${ARG_UNPARSED_ARGUMENTS})
endfunction()
function(_cmrc_register_dirs name dirpath)
if(dirpath STREQUAL "")
return()
endif()
# Skip this dir if we have already registered it
get_target_property(registered "${name}" _CMRC_REGISTERED_DIRS)
if(dirpath IN_LIST registered)
return()
endif()
# Register the parent directory first
get_filename_component(parent "${dirpath}" DIRECTORY)
if(NOT parent STREQUAL "")
_cmrc_register_dirs("${name}" "${parent}")
endif()
# Now generate the registration
set_property(TARGET "${name}" APPEND PROPERTY _CMRC_REGISTERED_DIRS "${dirpath}")
_cm_encode_fpath(sym "${dirpath}")
if(parent STREQUAL "")
set(parent_sym root_directory)
else()
_cm_encode_fpath(parent_sym "${parent}")
endif()
get_filename_component(leaf "${dirpath}" NAME)
set_property(
TARGET "${name}"
APPEND PROPERTY CMRC_MAKE_DIRS
"static auto ${sym}_dir = ${parent_sym}_dir.directory.add_subdir(\"${leaf}\")\;"
"root_index.emplace(\"${dirpath}\", &${sym}_dir.index_entry)\;"
)
endfunction()
function(cmrc_add_resources name)
get_target_property(is_reslib ${name} CMRC_IS_RESOURCE_LIBRARY)
if(NOT TARGET ${name} OR NOT is_reslib)
message(SEND_ERROR "cmrc_add_resources called on target '${name}' which is not an existing resource library")
return()
endif()
set(options)
set(args WHENCE PREFIX)
set(list_args)
cmake_parse_arguments(ARG "${options}" "${args}" "${list_args}" "${ARGN}")
if(NOT ARG_WHENCE)
set(ARG_WHENCE ${CMAKE_CURRENT_SOURCE_DIR})
endif()
_cmrc_normalize_path(ARG_WHENCE)
get_filename_component(ARG_WHENCE "${ARG_WHENCE}" ABSOLUTE)
# Generate the identifier for the resource library's namespace
get_target_property(lib_ns "${name}" CMRC_NAMESPACE)
get_target_property(libdir ${name} CMRC_LIBDIR)
get_target_property(target_dir ${name} SOURCE_DIR)
file(RELATIVE_PATH reldir "${target_dir}" "${CMAKE_CURRENT_SOURCE_DIR}")
if(reldir MATCHES "^\\.\\.")
message(SEND_ERROR "Cannot call cmrc_add_resources in a parent directory from the resource library target")
return()
endif()
foreach(input IN LISTS ARG_UNPARSED_ARGUMENTS)
_cmrc_normalize_path(input)
get_filename_component(abs_in "${input}" ABSOLUTE)
# Generate a filename based on the input filename that we can put in
# the intermediate directory.
file(RELATIVE_PATH relpath "${ARG_WHENCE}" "${abs_in}")
if(relpath MATCHES "^\\.\\.")
# For now we just error on files that exist outside of the soure dir.
message(SEND_ERROR "Cannot add file '${input}': File must be in a subdirectory of ${ARG_WHENCE}")
continue()
endif()
if(DEFINED ARG_PREFIX)
_cmrc_normalize_path(ARG_PREFIX)
endif()
if(ARG_PREFIX AND NOT ARG_PREFIX MATCHES "/$")
set(ARG_PREFIX "${ARG_PREFIX}/")
endif()
get_filename_component(dirpath "${ARG_PREFIX}${relpath}" DIRECTORY)
_cmrc_register_dirs("${name}" "${dirpath}")
get_filename_component(abs_out "${libdir}/intermediate/${relpath}.cpp" ABSOLUTE)
# Generate a symbol name relpath the file's character array
_cm_encode_fpath(sym "${relpath}")
# Get the symbol name for the parent directory
if(dirpath STREQUAL "")
set(parent_sym root_directory)
else()
_cm_encode_fpath(parent_sym "${dirpath}")
endif()
# Generate the rule for the intermediate source file
_cmrc_generate_intermediate_cpp(${lib_ns} ${sym} "${abs_out}" "${abs_in}")
target_sources(${name} PRIVATE "${abs_out}")
set_property(TARGET ${name} APPEND PROPERTY CMRC_EXTERN_DECLS
"// Pointers to ${input}"
"extern const char* const ${sym}_begin\;"
"extern const char* const ${sym}_end\;"
)
get_filename_component(leaf "${relpath}" NAME)
set_property(
TARGET ${name}
APPEND PROPERTY CMRC_MAKE_FILES
"root_index.emplace("
" \"${ARG_PREFIX}${relpath}\","
" ${parent_sym}_dir.directory.add_file("
" \"${leaf}\","
" res_chars::${sym}_begin,"
" res_chars::${sym}_end"
" )"
")\;"
)
endforeach()
endfunction()
function(_cmrc_generate_intermediate_cpp lib_ns symbol outfile infile)
add_custom_command(
# This is the file we will generate
OUTPUT "${outfile}"
# These are the primary files that affect the output
DEPENDS "${infile}" "${_CMRC_SCRIPT}"
COMMAND
"${CMAKE_COMMAND}"
-D_CMRC_GENERATE_MODE=TRUE
-DNAMESPACE=${lib_ns}
-DSYMBOL=${symbol}
"-DINPUT_FILE=${infile}"
"-DOUTPUT_FILE=${outfile}"
-P "${_CMRC_SCRIPT}"
COMMENT "Generating intermediate file for ${infile}"
)
endfunction()
function(_cm_encode_fpath var fpath)
string(MAKE_C_IDENTIFIER "${fpath}" ident)
string(MD5 hash "${fpath}")
string(SUBSTRING "${hash}" 0 4 hash)
set(${var} f_${hash}_${ident} PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 vector-of-bool <vectorofbool@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,22 @@
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,64 @@
# - Try to find the GLIB2 libraries
# Once done this will define
#
# GLIB2_FOUND - system has glib2
# GLIB2_INCLUDE_DIR - the glib2 include directory
# GLIB2_LIBRARIES - glib2 library
# Copyright (c) 2008 Laurent Montel, <montel@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
if(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES)
# Already in cache, be silent
set(GLIB2_FIND_QUIETLY TRUE)
endif(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES)
if (NOT WIN32)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PKG_GLIB REQUIRED glib-2.0)
endif(NOT WIN32)
find_path(GLIB2_MAIN_INCLUDE_DIR glib.h
PATH_SUFFIXES glib-2.0
HINTS ${PKG_GLIB_INCLUDE_DIRS} ${PKG_GLIB_INCLUDEDIR})
# search the glibconfig.h include dir under the same root where the library is found
find_library(GLIB2_LIBRARIES
NAMES glib-2.0
HINTS ${PKG_GLIB_LIBRARY_DIRS} ${PKG_GLIB_LIBDIR})
find_path(GLIB2_INTERNAL_INCLUDE_DIR glibconfig.h
PATH_SUFFIXES glib-2.0/include ../lib/glib-2.0/include
HINTS ${PKG_GLIB_INCLUDE_DIRS} ${PKG_GLIB_LIBRARIES} ${CMAKE_SYSTEM_LIBRARY_PATH})
set(GLIB2_INCLUDE_DIR ${GLIB2_MAIN_INCLUDE_DIR})
# not sure if this include dir is optional or required
# for now it is optional
if(GLIB2_INTERNAL_INCLUDE_DIR)
set(GLIB2_INCLUDE_DIR ${GLIB2_INCLUDE_DIR} ${GLIB2_INTERNAL_INCLUDE_DIR})
endif(GLIB2_INTERNAL_INCLUDE_DIR)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GLIB2 DEFAULT_MSG GLIB2_LIBRARIES GLIB2_MAIN_INCLUDE_DIR)
mark_as_advanced(GLIB2_INCLUDE_DIR GLIB2_LIBRARIES)
find_program(GLIB2_GENMARSHAL_UTIL glib-genmarshal)
macro(glib2_genmarshal output_name)
file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/genmarshal_tmp)
foreach(_declaration ${ARGN})
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/genmarshal_tmp "${_declaration}\n")
endforeach()
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${output_name}.h ${CMAKE_CURRENT_BINARY_DIR}/${output_name}.c
COMMAND ${GLIB2_GENMARSHAL_UTIL} --header genmarshal_tmp > ${output_name}.h
COMMAND ${GLIB2_GENMARSHAL_UTIL} --body genmarshal_tmp > ${output_name}.c
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
endmacro()

View File

@@ -0,0 +1,63 @@
# - Try to find GObject
# Once done this will define
#
# GOBJECT_FOUND - system has GObject
# GOBJECT_INCLUDE_DIR - the GObject include directory
# GOBJECT_LIBRARIES - the libraries needed to use GObject
# GOBJECT_DEFINITIONS - Compiler switches required for using GObject
# Copyright (c) 2006, Tim Beaulen <tbscope@gmail.com>
# Copyright (c) 2008 Helio Chissini de Castro, <helio@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
IF (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES)
# in cache already
SET(GObject_FIND_QUIETLY TRUE)
ELSE (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES)
SET(GObject_FIND_QUIETLY FALSE)
ENDIF (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES)
IF (NOT WIN32)
FIND_PACKAGE(PkgConfig REQUIRED)
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
PKG_CHECK_MODULES(PKG_GOBJECT2 REQUIRED gobject-2.0)
SET(GOBJECT_DEFINITIONS ${PKG_GOBJECT2_CFLAGS})
ENDIF (NOT WIN32)
FIND_PATH(GOBJECT_INCLUDE_DIR gobject/gobject.h
HINTS ${PKG_GOBJECT2_INCLUDE_DIRS} ${PKG_GOBJECT2_INCLUDEDIR}
PATHS /usr/include/glib-2.0/
PATH_SUFFIXES glib-2.0
)
FIND_LIBRARY(_GObjectLibs NAMES gobject-2.0
HINTS
${PKG_GOBJECT2_LIBRARY_DIRS}
${PKG_GOBJECT2_LIBDIR}
)
FIND_LIBRARY(_GModuleLibs NAMES gmodule-2.0
HINTS
${PKG_GOBJECT2_LIBRARY_DIRS}
${PKG_GOBJECT2_LIBDIR}
)
FIND_LIBRARY(_GThreadLibs NAMES gthread-2.0
HINTS
${PKG_GOBJECT2_LIBRARY_DIRS}
${PKG_GOBJECT2_LIBDIR}
)
FIND_LIBRARY(_GLibs NAMES glib-2.0
HINTS
${PKG_GOBJECT2_LIBRARY_DIRS}
${PKG_GOBJECT2_LIBDIR}
)
SET (GOBJECT_LIBRARIES ${_GObjectLibs} ${_GModuleLibs} ${_GThreadLibs} ${_GLibs})
MARK_AS_ADVANCED(GOBJECT_INCLUDE_DIR GOBJECT_LIBRARIES)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GObject DEFAULT_MSG GOBJECT_INCLUDE_DIR GOBJECT_LIBRARIES)

View File

@@ -0,0 +1,132 @@
# - Try to find GStreamer
# Once done this will define
#
# GSTREAMER_FOUND - system has GStreamer
# GSTREAMER_INCLUDE_DIR - the GStreamer include directory
# GSTREAMER_LIBRARY - the main GStreamer library
# GSTREAMER_PLUGIN_DIR - the GStreamer plugin directory
#
# And for all the plugin libraries specified in the COMPONENTS
# of find_package, this module will define:
#
# GSTREAMER_<plugin_lib>_LIBRARY_FOUND - system has <plugin_lib>
# GSTREAMER_<plugin_lib>_LIBRARY - the <plugin_lib> library
# GSTREAMER_<plugin_lib>_INCLUDE_DIR - the <plugin_lib> include directory
#
# Copyright (c) 2010, Collabora Ltd.
# @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
if (GSTREAMER_INCLUDE_DIR AND GSTREAMER_LIBRARY)
set(GStreamer_FIND_QUIETLY TRUE)
else()
set(GStreamer_FIND_QUIETLY FALSE)
endif()
set(GSTREAMER_ABI_VERSION "1.0")
# Find the main library
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_check_modules(PKG_GSTREAMER gstreamer-${GSTREAMER_ABI_VERSION})
exec_program(${PKG_CONFIG_EXECUTABLE}
ARGS --variable pluginsdir gstreamer-${GSTREAMER_ABI_VERSION}
OUTPUT_VARIABLE PKG_GSTREAMER_PLUGIN_DIR)
endif()
find_library(GSTREAMER_LIBRARY
NAMES gstreamer-${GSTREAMER_ABI_VERSION}
HINTS ${PKG_GSTREAMER_LIBRARY_DIRS} ${PKG_GSTREAMER_LIBDIR})
find_path(GSTREAMER_INCLUDE_DIR
gst/gst.h
HINTS ${PKG_GSTREAMER_INCLUDE_DIRS} ${PKG_GSTREAMER_INCLUDEDIR}
PATH_SUFFIXES gstreamer-${GSTREAMER_ABI_VERSION})
if (PKG_GSTREAMER_PLUGIN_DIR)
set(_GSTREAMER_PLUGIN_DIR ${PKG_GSTREAMER_PLUGIN_DIR})
else()
get_filename_component(_GSTREAMER_LIB_DIR ${GSTREAMER_LIBRARY} PATH)
set(_GSTREAMER_PLUGIN_DIR ${_GSTREAMER_LIB_DIR}/gstreamer-${GSTREAMER_ABI_VERSION})
endif()
set(GSTREAMER_PLUGIN_DIR ${_GSTREAMER_PLUGIN_DIR}
CACHE PATH "The path to the gstreamer plugins installation directory")
mark_as_advanced(GSTREAMER_LIBRARY GSTREAMER_INCLUDE_DIR GSTREAMER_PLUGIN_DIR)
# Find additional libraries
include(MacroFindGStreamerLibrary)
macro(_find_gst_component _name _header)
find_gstreamer_library(${_name} ${_header} ${GSTREAMER_ABI_VERSION})
set(_GSTREAMER_EXTRA_VARIABLES ${_GSTREAMER_EXTRA_VARIABLES}
GSTREAMER_${_name}_LIBRARY GSTREAMER_${_name}_INCLUDE_DIR)
endmacro()
foreach(_component ${GStreamer_FIND_COMPONENTS})
if (${_component} STREQUAL "base")
_find_gst_component(BASE gstbasesink.h)
elseif (${_component} STREQUAL "check")
_find_gst_component(CHECK gstcheck.h)
elseif (${_component} STREQUAL "controller")
_find_gst_component(CONTROLLER gstargbcontrolbinding.h)
elseif (${_component} STREQUAL "net")
_find_gst_component(NET gstnet.h)
else()
message (AUTHOR_WARNING "FindGStreamerPluginsBase.cmake: Invalid component \"${_component}\" was specified")
endif()
endforeach()
# Version check
if (GStreamer_FIND_VERSION)
if (PKG_GSTREAMER_FOUND)
if("${PKG_GSTREAMER_VERSION}" VERSION_LESS "${GStreamer_FIND_VERSION}")
message(STATUS "Found GStreamer version ${PKG_GSTREAMER_VERSION}, but at least version ${GStreamer_FIND_VERSION} is required")
set(GSTREAMER_VERSION_COMPATIBLE FALSE)
else()
set(GSTREAMER_VERSION_COMPATIBLE TRUE)
endif()
elseif(GSTREAMER_INCLUDE_DIR)
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_INCLUDES ${GSTREAMER_INCLUDE_DIR})
string(REPLACE "." "," _comma_version ${GStreamer_FIND_VERSION})
# Hack to invalidate the cached value
set(GSTREAMER_VERSION_COMPATIBLE GSTREAMER_VERSION_COMPATIBLE)
check_cxx_source_compiles("
#define G_BEGIN_DECLS
#define G_END_DECLS
#include <gst/gstversion.h>
#if GST_CHECK_VERSION(${_comma_version})
int main() { return 0; }
#else
# error \"GStreamer version incompatible\"
#endif
" GSTREAMER_VERSION_COMPATIBLE)
if (NOT GSTREAMER_VERSION_COMPATIBLE)
message(STATUS "GStreamer ${GStreamer_FIND_VERSION} is required, but the version found is older")
endif()
else()
# We didn't find gstreamer at all
set(GSTREAMER_VERSION_COMPATIBLE FALSE)
endif()
else()
# No version constrain was specified, thus we consider the version compatible
set(GSTREAMER_VERSION_COMPATIBLE TRUE)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GStreamer DEFAULT_MSG
GSTREAMER_LIBRARY GSTREAMER_INCLUDE_DIR
GSTREAMER_VERSION_COMPATIBLE ${_GSTREAMER_EXTRA_VARIABLES})

View File

@@ -0,0 +1,90 @@
# - Try to find gst-plugins-base
# Once done this will define
#
# GSTREAMER_PLUGINS_BASE_FOUND - system has gst-plugins-base
#
# And for all the plugin libraries specified in the COMPONENTS
# of find_package, this module will define:
#
# GSTREAMER_<plugin_lib>_LIBRARY_FOUND - system has <plugin_lib>
# GSTREAMER_<plugin_lib>_LIBRARY - the <plugin_lib> library
# GSTREAMER_<plugin_lib>_INCLUDE_DIR - the <plugin_lib> include directory
#
# Copyright (c) 2010, Collabora Ltd.
# @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
set(GSTREAMER_ABI_VERSION "1.0")
# Find the pkg-config file for doing the version check
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_check_modules(PKG_GSTREAMER_PLUGINS_BASE gstreamer-plugins-base-${GSTREAMER_ABI_VERSION})
endif()
# Find the plugin libraries
include(MacroFindGStreamerLibrary)
macro(_find_gst_plugins_base_component _name _header)
find_gstreamer_library(${_name} ${_header} ${GSTREAMER_ABI_VERSION})
set(_GSTREAMER_PLUGINS_BASE_EXTRA_VARIABLES ${_GSTREAMER_PLUGINS_BASE_EXTRA_VARIABLES}
GSTREAMER_${_name}_LIBRARY GSTREAMER_${_name}_INCLUDE_DIR)
endmacro()
foreach(_component ${GStreamerPluginsBase_FIND_COMPONENTS})
if (${_component} STREQUAL "app")
_find_gst_plugins_base_component(APP gstappsrc.h)
elseif (${_component} STREQUAL "audio")
_find_gst_plugins_base_component(AUDIO audio.h)
elseif (${_component} STREQUAL "fft")
_find_gst_plugins_base_component(FFT gstfft.h)
elseif (${_component} STREQUAL "riff")
_find_gst_plugins_base_component(RIFF riff-ids.h)
elseif (${_component} STREQUAL "rtp")
_find_gst_plugins_base_component(RTP gstrtpbuffer.h)
elseif (${_component} STREQUAL "rtsp")
_find_gst_plugins_base_component(RTSP gstrtspdefs.h)
elseif (${_component} STREQUAL "sdp")
_find_gst_plugins_base_component(SDP gstsdp.h)
elseif (${_component} STREQUAL "tag")
_find_gst_plugins_base_component(TAG tag.h)
elseif (${_component} STREQUAL "pbutils")
_find_gst_plugins_base_component(PBUTILS pbutils.h)
elseif (${_component} STREQUAL "video")
_find_gst_plugins_base_component(VIDEO video.h)
elseif (${_component} STREQUAL "gl")
_find_gst_plugins_base_component(GL gstglconfig.h)
else()
message (AUTHOR_WARNING "FindGStreamer.cmake: Invalid component \"${_component}\" was specified")
endif()
endforeach()
# Version check
if (GStreamerPluginsBase_FIND_VERSION)
if (PKG_GSTREAMER_PLUGINS_BASE_FOUND)
if("${PKG_GSTREAMER_PLUGINS_BASE_VERSION}" VERSION_LESS "${GStreamerPluginsBase_FIND_VERSION}")
message(STATUS "Found gst-plugins-base version ${PKG_GSTREAMER_PLUGINS_BASE_VERSION}, but at least version ${GStreamerPluginsBase_FIND_VERSION} is required")
set(GSTREAMER_PLUGINS_BASE_VERSION_COMPATIBLE FALSE)
else()
set(GSTREAMER_PLUGINS_BASE_VERSION_COMPATIBLE TRUE)
endif()
else()
# We can't make any version checks without pkg-config, just assume version is compatible and hope...
set(GSTREAMER_PLUGINS_BASE_VERSION_COMPATIBLE TRUE)
endif()
else()
# No version constrain was specified, thus we consider the version compatible
set(GSTREAMER_PLUGINS_BASE_VERSION_COMPATIBLE TRUE)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GStreamerPluginsBase DEFAULT_MSG
GSTREAMER_PLUGINS_BASE_VERSION_COMPATIBLE
${_GSTREAMER_PLUGINS_BASE_EXTRA_VARIABLES})

View File

@@ -0,0 +1,57 @@
# - macro find_gstreamer_library
#
# Copyright (c) 2010, Collabora Ltd.
# @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
macro(find_gstreamer_library _name _header _abi_version)
string(TOLOWER ${_name} _lower_name)
string(TOUPPER ${_name} _upper_name)
if (GSTREAMER_${_upper_name}_LIBRARY AND GSTREAMER_${_upper_name}_INCLUDE_DIR)
set(_GSTREAMER_${_upper_name}_QUIET TRUE)
else()
set(_GSTREAMER_${_upper_name}_QUIET FALSE)
endif()
if (PKG_CONFIG_FOUND)
pkg_check_modules(PKG_GSTREAMER_${_upper_name} gstreamer-${_lower_name}-${_abi_version})
endif()
find_library(GSTREAMER_${_upper_name}_LIBRARY
NAMES gst${_lower_name}-${_abi_version}
HINTS ${PKG_GSTREAMER_${_upper_name}_LIBRARY_DIRS}
${PKG_GSTREAMER_${_upper_name}_LIBDIR}
)
find_path(GSTREAMER_${_upper_name}_INCLUDE_DIR
gst/${_lower_name}/${_header}
HINTS ${PKG_GSTREAMER_${_upper_name}_INCLUDE_DIRS}
${PKG_GSTREAMER_${_upper_name}_INCLUDEDIR}
PATH_SUFFIXES gstreamer-${_abi_version}
)
if (GSTREAMER_${_upper_name}_LIBRARY AND GSTREAMER_${_upper_name}_INCLUDE_DIR)
set(GSTREAMER_${_upper_name}_LIBRARY_FOUND TRUE)
else()
set(GSTREAMER_${_upper_name}_LIBRARY_FOUND FALSE)
endif()
if (NOT _GSTREAMER_${_upper_name}_QUIET)
if (GSTREAMER_${_upper_name}_LIBRARY)
message(STATUS "Found GSTREAMER_${_upper_name}_LIBRARY: ${GSTREAMER_${_upper_name}_LIBRARY}")
else()
message(STATUS "Could NOT find GSTREAMER_${_upper_name}_LIBRARY")
endif()
if (GSTREAMER_${_upper_name}_INCLUDE_DIR)
message(STATUS "Found GSTREAMER_${_upper_name}_INCLUDE_DIR: ${GSTREAMER_${_upper_name}_INCLUDE_DIR}")
else()
message(STATUS "Could NOT find GSTREAMER_${_upper_name}_INCLUDE_DIR")
endif()
endif()
mark_as_advanced(GSTREAMER_${_upper_name}_LIBRARY GSTREAMER_${_upper_name}_INCLUDE_DIR)
endmacro()

View File

@@ -0,0 +1,146 @@
# This file defines the Feature Logging macros.
#
# MACRO_LOG_FEATURE(VAR FEATURE DESCRIPTION URL [REQUIRED [MIN_VERSION [COMMENTS]]])
# Logs the information so that it can be displayed at the end
# of the configure run
# VAR : TRUE or FALSE, indicating whether the feature is supported
# FEATURE: name of the feature, e.g. "libjpeg"
# DESCRIPTION: description what this feature provides
# URL: home page
# REQUIRED: TRUE or FALSE, indicating whether the featue is required
# MIN_VERSION: minimum version number. empty string if unneeded
# COMMENTS: More info you may want to provide. empty string if unnecessary
#
# MACRO_DISPLAY_FEATURE_LOG()
# Call this to display the collected results.
# Exits CMake with a FATAL error message if a required feature is missing
#
# Example:
#
# INCLUDE(MacroLogFeature)
#
# FIND_PACKAGE(JPEG)
# MACRO_LOG_FEATURE(JPEG_FOUND "libjpeg" "Support JPEG images" "http://www.ijg.org" TRUE "3.2a" "")
# ...
# MACRO_DISPLAY_FEATURE_LOG()
# Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org>
# Copyright (c) 2006, Allen Winter, <winter@kde.org>
# Copyright (c) 2009, Sebastian Trueg, <trueg@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
IF (NOT _macroLogFeatureAlreadyIncluded)
SET(_file ${CMAKE_BINARY_DIR}/MissingRequirements.txt)
IF (EXISTS ${_file})
FILE(REMOVE ${_file})
ENDIF (EXISTS ${_file})
SET(_file ${CMAKE_BINARY_DIR}/EnabledFeatures.txt)
IF (EXISTS ${_file})
FILE(REMOVE ${_file})
ENDIF (EXISTS ${_file})
SET(_file ${CMAKE_BINARY_DIR}/DisabledFeatures.txt)
IF (EXISTS ${_file})
FILE(REMOVE ${_file})
ENDIF (EXISTS ${_file})
SET(_macroLogFeatureAlreadyIncluded TRUE)
ENDIF (NOT _macroLogFeatureAlreadyIncluded)
MACRO(MACRO_LOG_FEATURE _var _package _description _url ) # _required _minvers _comments)
STRING(TOUPPER "${ARGV4}" _required)
SET(_minvers "${ARGV5}")
SET(_comments "${ARGV6}")
IF (${_var})
SET(_LOGFILENAME ${CMAKE_BINARY_DIR}/EnabledFeatures.txt)
ELSE (${_var})
IF ("${_required}" STREQUAL "TRUE")
SET(_LOGFILENAME ${CMAKE_BINARY_DIR}/MissingRequirements.txt)
ELSE ("${_required}" STREQUAL "TRUE")
SET(_LOGFILENAME ${CMAKE_BINARY_DIR}/DisabledFeatures.txt)
ENDIF ("${_required}" STREQUAL "TRUE")
ENDIF (${_var})
SET(_logtext " * ${_package}")
IF (NOT ${_var})
IF (${_minvers} MATCHES ".*")
SET(_logtext "${_logtext} (${_minvers} or higher)")
ENDIF (${_minvers} MATCHES ".*")
SET(_logtext "${_logtext} <${_url}>\n ")
ELSE (NOT ${_var})
SET(_logtext "${_logtext} - ")
ENDIF (NOT ${_var})
SET(_logtext "${_logtext}${_description}")
IF (NOT ${_var})
IF (${_comments} MATCHES ".*")
SET(_logtext "${_logtext}\n ${_comments}")
ENDIF (${_comments} MATCHES ".*")
# SET(_logtext "${_logtext}\n") #double-space missing features?
ENDIF (NOT ${_var})
FILE(APPEND "${_LOGFILENAME}" "${_logtext}\n")
ENDMACRO(MACRO_LOG_FEATURE)
MACRO(MACRO_DISPLAY_FEATURE_LOG)
SET(_missingFile ${CMAKE_BINARY_DIR}/MissingRequirements.txt)
SET(_enabledFile ${CMAKE_BINARY_DIR}/EnabledFeatures.txt)
SET(_disabledFile ${CMAKE_BINARY_DIR}/DisabledFeatures.txt)
IF (EXISTS ${_missingFile} OR EXISTS ${_enabledFile} OR EXISTS ${_disabledFile})
SET(_printSummary TRUE)
ENDIF (EXISTS ${_missingFile} OR EXISTS ${_enabledFile} OR EXISTS ${_disabledFile})
IF(_printSummary)
SET(_missingDeps 0)
IF (EXISTS ${_enabledFile})
FILE(READ ${_enabledFile} _enabled)
FILE(REMOVE ${_enabledFile})
SET(_summary "${_summary}\n-----------------------------------------------------------------------------\n-- The following external packages were located on your system.\n-- This installation will have the extra features provided by these packages.\n-----------------------------------------------------------------------------\n${_enabled}")
ENDIF (EXISTS ${_enabledFile})
IF (EXISTS ${_disabledFile})
SET(_missingDeps 1)
FILE(READ ${_disabledFile} _disabled)
FILE(REMOVE ${_disabledFile})
SET(_summary "${_summary}\n-----------------------------------------------------------------------------\n-- The following OPTIONAL packages could NOT be located on your system.\n-- Consider installing them to enable more features from this software.\n-----------------------------------------------------------------------------\n${_disabled}")
ENDIF (EXISTS ${_disabledFile})
IF (EXISTS ${_missingFile})
SET(_missingDeps 1)
FILE(READ ${_missingFile} _requirements)
SET(_summary "${_summary}\n-----------------------------------------------------------------------------\n-- The following REQUIRED packages could NOT be located on your system.\n-- You must install these packages before continuing.\n-----------------------------------------------------------------------------\n${_requirements}")
FILE(REMOVE ${_missingFile})
SET(_haveMissingReq 1)
ENDIF (EXISTS ${_missingFile})
IF (NOT ${_missingDeps})
SET(_summary "${_summary}\n-----------------------------------------------------------------------------\n-- Congratulations! All external packages have been found.")
ENDIF (NOT ${_missingDeps})
MESSAGE(${_summary})
MESSAGE("-----------------------------------------------------------------------------\n")
IF(_haveMissingReq)
MESSAGE(FATAL_ERROR "Exiting: Missing Requirements")
ENDIF(_haveMissingReq)
ENDIF(_printSummary)
ENDMACRO(MACRO_DISPLAY_FEATURE_LOG)

25
imgui.ini Normal file
View File

@@ -0,0 +1,25 @@
[Window][Debug##Default]
Pos=60,60
Size=400,400
Collapsed=0
[Window][v-mix]
Pos=40,40
Size=300,300
Collapsed=0
[Window][Media Player]
Pos=8,678
Size=947,624
Collapsed=0
[Window][Text Editor]
Pos=696,206
Size=800,600
Collapsed=0
[Window][Image properties]
Pos=511,51
Size=172,256
Collapsed=0

354
main.cpp Normal file
View File

@@ -0,0 +1,354 @@
#include <stdio.h>
#include <iostream>
// standalone image loader
#include "stb_image.h"
// Opengl
#include <glad/glad.h>
#include <glm/glm.hpp>
// GStreamer
#include <gst/gst.h>
#include <gst/gstbin.h>
#include <gst/gl/gl.h>
// vmix
#include "defines.h"
#include "ShaderManager.h"
#include "SettingsManager.h"
#include "ResourceManager.h"
#include "RenderingManager.h"
#include "UserInterfaceManager.h"
#include "imgui.h"
#include "ImGuiToolkit.h"
#include "GstToolkit.h"
#include "MediaPlayer.h"
#define PI 3.14159265358979323846
MediaPlayer testmedia("testmedia");
MediaPlayer testmedia2("testmedia2");
Shader rendering_shader;
unsigned int vbo, vao, ebo;
float texturear = 1.0;
GLuint textureimagepng = 0;
void create_square(unsigned int &vbo, unsigned int &vao, unsigned int &ebo)
{
// create the triangle
float _vertices[] = {
-1.f, -1.f, 0.0f, // position vertex 3
0.0f, 1.0f, 0.0f, // uv vertex 3
-1.f, 1.f, 0.0f, // position vertex 0
0.0f, 0.0f, 0.0f, // uv vertex 0
1.f, -1.f, 0.0f, // position vertex 2
1.0f, 1.0f, 0.0f, // uv vertex 2
1.f, 1.f, 0.0f, // position vertex 1
1.0f, 0.0f, 0.0f, // uv vertex 1
};
unsigned int _indices[] = { 0, 1, 2, 3 };
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(_vertices), _vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices), _indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
GLuint loadPNG(const char *imagepath, float *aspectrato)
{
int w, h, n, image;
/* FIXME: remove once the example supports gl3 and/or gles2 */
g_setenv ("GST_GL_API", "opengl", FALSE);
unsigned char* img;
*aspectrato = 1.f;
img = stbi_load(imagepath, &w, &h, &n, 3);
if (img == NULL) {
printf("Failed to load png - %s\n", stbi_failure_reason());
return 0;
}
GLuint tex;
glGenTextures(1, &tex);
glBindTexture( GL_TEXTURE_2D, tex);
glPixelStorei(GL_UNPACK_ALIGNMENT, 3);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, img);
stbi_image_free(img);
glBindTexture( GL_TEXTURE_2D, 0);
*aspectrato = static_cast<float>(w) / static_cast<float>(h);
return tex;
}
// from https://gstreamer.freedesktop.org/documentation/application-development/advanced/pipeline-manipulation.html?gi-language=c
void drawMediaBackgound()
{
if ( !testmedia2.isOpen() )
return;
// rendering TRIANGLE geometries
rendering_shader.use();
rendering_shader.setUniform("render_projection", Rendering::Projection());
testmedia2.Update();
testmedia2.Bind();
// rendering_shader.setUniform("aspectratio", texturear);
// glBindTexture(GL_TEXTURE_2D, textureimagepng);
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
// render TRIANGLE GUI
{
ImGui::Begin("Image properties");
static float translation[] = {0.f, 0.f};
if (ImGuiToolkit::ButtonIcon(6, 15)) {
translation[0] = 0.f;
translation[1] = 0.f;
}
ImGui::SameLine(0, 10);
ImGui::SliderFloat2("position", translation, -5.0, 5.0);
static float rotation = 0.f;
if (ImGuiToolkit::ButtonIcon(4, 15))
rotation = 0.f;
ImGui::SameLine(0, 10);
ImGui::SliderFloat("rotation", &rotation, 0, 2 * PI);
static float scale = 1.f;
if (ImGuiToolkit::ButtonIcon(3, 15))
scale = 1.f;
ImGui::SameLine(0, 10);
ImGui::SliderFloat("scale", &scale, 0.1f, 10.f, "%.3f", 3.f);
// color picker
static float color[4] = { 1.0f,1.0f,1.0f,1.0f };
if (ImGuiToolkit::ButtonIcon(16, 8)) {
color[0] = 1.f;
color[1] = 1.f;
color[2] = 1.f;
color[3] = 1.f;
}
ImGui::SameLine(0, 10);
ImGui::ColorEdit3("color", color);
static float brightness = 0.0;
if (ImGuiToolkit::ButtonIcon(4, 1))
brightness = 0.f;
ImGui::SameLine(0, 10);
ImGui::SliderFloat("brightness", &brightness, -1.0, 1.0, "%.3f", 2.f);
static float contrast = 0.0;
if (ImGuiToolkit::ButtonIcon(2, 1))
contrast = 0.f;
ImGui::SameLine(0, 10);
ImGui::SliderFloat("contrast", &contrast, -1.0, 1.0, "%.3f", 2.f);
// pass the parameters to the shader
rendering_shader.setUniform("scale", scale);
rendering_shader.setUniform("rotation", rotation);
rendering_shader.setUniform("translation", translation[0], translation[1]);
rendering_shader.setUniform("color", color[0], color[1], color[2]);
rendering_shader.setUniform("brightness", brightness);
rendering_shader.setUniform("contrast", contrast);
rendering_shader.setUniform("aspectratio", testmedia2.AspectRatio());
ImGui::End();
}
// // add gui elements to vmix main window
// ImGui::Begin("v-mix");
// static float zoom = 0.3f;
// if (ImGuiToolkit::ButtonIcon(5, 7))
// zoom = 0.3f;
// ImGui::SameLine(0, 10);
// if (ImGui::SliderFloat("zoom", &zoom, 0.1f, 10.f, "%.4f", 3.f))
// UserInterface::Log("Zoom %f", zoom);
// rendering_shader.setUniform("render_zoom", zoom);
// ImGui::End();
Shader::enduse();
}
void drawMediaPlayer()
{
if ( !testmedia.isOpen() )
return;
testmedia.Update();
ImGui::Begin("Media Player");
float width = ImGui::GetContentRegionAvail().x;
float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
ImVec2 imagesize ( width, width / testmedia.AspectRatio());
ImGui::Image((void*)(intptr_t)testmedia.Texture(), imagesize);
if (ImGuiToolkit::ButtonIcon(17, 7)) testmedia.Rewind();
ImGui::SameLine(0, spacing);
if (testmedia.isPlaying()) {
// if (ImGuiToolkit::ButtonIcon(2, 8))
if (ImGui::Button(ICON_FA_STOP " Stop") )
testmedia.Play(false);
ImGui::SameLine(0, spacing);
ImGui::PushButtonRepeat(true);
// if (ImGui::Button(ICON_FA_FAST_FORWARD))
if (ImGuiToolkit::ButtonIcon(14, 7))
{
testmedia.FastForward ();
}
ImGui::PopButtonRepeat();
} else {
// if (ImGuiToolkit::ButtonIcon(9, 7))
if (ImGui::Button(ICON_FA_PLAY " Play") )
testmedia.Play(true);
ImGui::SameLine(0, spacing);
if (ImGuiToolkit::ButtonIcon(8, 0)) testmedia.SeekNextFrame();
}
ImGui::SameLine(0, spacing * 2.f);
ImGui::Dummy(ImVec2(width - 700.0, 0)); // right align
static int current_loop = 1;
static const char* loop_names[3] = { "Stop", "Rewind", "Bounce" };
const char* current_loop_name = loop_names[current_loop];
ImGui::SameLine(0, spacing);
if (current_loop == 0) ImGuiToolkit::Icon(0, 15);
else if (current_loop == 1) ImGuiToolkit::Icon(1, 15);
else ImGuiToolkit::Icon(19, 14);
ImGui::SameLine(0, spacing);
ImGui::SetNextItemWidth(90);
if ( ImGui::SliderInt("", &current_loop, 0, 2, current_loop_name) )
testmedia.setLoop( (MediaPlayer::LoopMode) current_loop );
float speed = static_cast<float>(testmedia.PlaySpeed());
ImGui::SameLine(0, spacing);
ImGui::SetNextItemWidth(270);
// ImGui::SetNextItemWidth(width - 90.0);
if (ImGui::SliderFloat( "Speed", &speed, -10.f, 10.f, "x %.1f", 2.f))
testmedia.SetPlaySpeed( static_cast<double>(speed) );
ImGui::SameLine(0, spacing);
if (ImGuiToolkit::ButtonIcon(19, 15)) {
speed = 1.f;
testmedia.SetPlaySpeed( static_cast<double>(speed) );
}
//ImGuiToolkit::Bar(0.6f, 0.1, 0.8, 0.0, 1.0, "time", true);
guint64 t = testmedia.Position();
guint64 begin = testmedia.Duration() / 5;
guint64 end = 4 * testmedia.Duration() / 5;
if (ImGuiToolkit::TimelineSlider( "timeline", &t, begin, end, testmedia.Duration(), testmedia.FrameDuration()) )
{
testmedia.SeekTo(t);
}
ImGui::Text("Dimension %d x %d", testmedia.Width(), testmedia.Height());
ImGui::Text("Framerate %.2f / %.2f", testmedia.UpdateFrameRate() , testmedia.FrameRate() );
ImGui::End();
}
int main(int, char**)
{
///
/// Settings
///
Settings::Load();
///
/// RENDERING INIT
///
if ( !Rendering::Init() )
return 1;
///
/// UI INIT
///
if ( !UserInterface::Init() )
return 1;
///
/// GStreamer
///
gst_debug_set_active(TRUE);
gst_debug_set_default_threshold (GST_LEVEL_WARNING);
// 1
// 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/SIGGRAPH92_1.avi");
// testmedia.Open("file:///home/bhbn/Videos/fish.mp4");
// testmedia.Open("file:///home/bhbn/Videos/iss.mov");
// testmedia.Open("file:///home/bhbn/Videos/TearsOfSteel_720p_h265.mkv");
testmedia.Open("file:///home/bhbn/Videos/Upgrade.2018.720p.AMZN.WEB-DL.DDP5.1.H.264-NTG.m4v");
testmedia.Play(false);
// Add draw callbacks to the Rendering
Rendering::AddDrawCallback(drawMediaPlayer);
// 2
testmedia2.Open("file:///home/bhbn/Videos/iss.mov");
// testmedia2.Open("file:///home/bhbn/Videos/balls.gif");
// testmedia2.Open("file:///home/bhbn/Videos/Upgrade.2018.720p.AMZN.WEB-DL.DDP5.1.H.264-NTG.m4v");
testmedia2.Play(true);
// create our geometries
create_square(vbo, vao, ebo);
// Add draw callbacks to the Rendering
Rendering::AddDrawCallback(drawMediaBackgound);
// // load an image
textureimagepng = loadPNG("/home/bhbn/Videos/iss_snap.png", &texturear);
// init shader
rendering_shader.load("shaders/texture-shader.vs", "shaders/texture-shader.fs");
UserInterface::OpenTextEditor( Resource::getText("shaders/texture-shader.vs") );
// Main loop
while ( Rendering::isActive() )
{
Rendering::Draw();
}
testmedia.Close();
testmedia2.Close();
UserInterface::Terminate();
Rendering::Terminate();
Settings::Save();
return 0;
}

14
osx/CocoaToolkit.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef COCOA_TOOLKIT_H
#define COCOA_TOOLKIT_H
#ifndef APPLE
#error "This file is only meant to be compiled for Mac OS X targets"
#endif
namespace CocoaToolkit {
void * get_current_nsopengl_context();
};
#endif // COCOA_TOOLKIT_H

28
osx/CocoaToolkit.mm Normal file
View File

@@ -0,0 +1,28 @@
/*
* GStreamer
* Copyright (C) 2010 Julien Isorce <julien.isorce@gmail.com>
* Copyright (C) 2010 Nuno Santos <nunosantos@imaginando.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "CocoaToolkit.h"
#import <Cocoa/Cocoa.h>
void * CocoaToolkit::get_current_nsopengl_context()
{
return CGLGetCurrentContext();
}