mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-06 07:50:00 +01:00
5
.gitignore
vendored
5
.gitignore
vendored
@@ -19,8 +19,13 @@ rules.ninja
|
||||
|
||||
rsc/shaders/paint.fs
|
||||
|
||||
.vscode/settings.json
|
||||
<<<<<<< HEAD
|
||||
|
||||
osx/.DS_Store
|
||||
|
||||
.DS_Store
|
||||
|
||||
osx/runvimix
|
||||
=======
|
||||
>>>>>>> dev
|
||||
|
||||
@@ -26,7 +26,6 @@ using namespace tinyxml2;
|
||||
|
||||
void captureMixerSession(tinyxml2::XMLDocument *doc, std::string node, std::string label)
|
||||
{
|
||||
|
||||
// create node
|
||||
XMLElement *sessionNode = doc->NewElement( node.c_str() );
|
||||
doc->InsertEndChild(sessionNode);
|
||||
@@ -37,7 +36,6 @@ void captureMixerSession(tinyxml2::XMLDocument *doc, std::string node, std::stri
|
||||
|
||||
// get session to operate on
|
||||
Session *se = Mixer::manager().session();
|
||||
se->lock();
|
||||
|
||||
// get the thumbnail (requires one opengl update to render)
|
||||
FrameBufferImage *thumbnail = se->thumbnail();
|
||||
@@ -51,7 +49,6 @@ void captureMixerSession(tinyxml2::XMLDocument *doc, std::string node, std::stri
|
||||
for (auto iter = se->begin(); iter != se->end(); ++iter, sv.setRoot(sessionNode) )
|
||||
(*iter)->accept(sv);
|
||||
|
||||
se->unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -319,6 +319,7 @@ set(VMIX_SRCS
|
||||
SearchVisitor.cpp
|
||||
ImGuiToolkit.cpp
|
||||
ImGuiVisitor.cpp
|
||||
InfoVisitor.cpp
|
||||
GstToolkit.cpp
|
||||
GlmToolkit.cpp
|
||||
SystemToolkit.cpp
|
||||
|
||||
@@ -384,6 +384,10 @@ void DeviceSource::setDevice(const std::string &devicename)
|
||||
|
||||
pipeline << " ! videoconvert";
|
||||
|
||||
// resize render buffer
|
||||
if (renderbuffer_)
|
||||
renderbuffer_->resize(best.width, best.height);
|
||||
|
||||
// open gstreamer
|
||||
stream_->open( pipeline.str(), best.width, best.height);
|
||||
stream_->play(true);
|
||||
|
||||
@@ -171,6 +171,31 @@ glm::vec3 FrameBuffer::resolution() const
|
||||
return glm::vec3(attrib_.viewport.x, attrib_.viewport.y, 0.f);
|
||||
}
|
||||
|
||||
void FrameBuffer::resize(int width, int height)
|
||||
{
|
||||
if (framebufferid_) {
|
||||
if (attrib_.viewport.x != width || attrib_.viewport.y != height)
|
||||
{
|
||||
// de-init
|
||||
glDeleteFramebuffers(1, &framebufferid_);
|
||||
framebufferid_ = 0;
|
||||
|
||||
if (intermediate_framebufferid_)
|
||||
glDeleteFramebuffers(1, &intermediate_framebufferid_);
|
||||
intermediate_framebufferid_ = 0;
|
||||
if (textureid_)
|
||||
glDeleteTextures(1, &textureid_);
|
||||
textureid_ = 0;
|
||||
if (intermediate_textureid_)
|
||||
glDeleteTextures(1, &intermediate_textureid_);
|
||||
intermediate_textureid_ = 0;
|
||||
|
||||
// change resolution
|
||||
attrib_.viewport = glm::ivec2(width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FrameBuffer::begin(bool clear)
|
||||
{
|
||||
if (!framebufferid_)
|
||||
|
||||
@@ -69,6 +69,7 @@ public:
|
||||
inline uint width() const { return attrib_.viewport.x; }
|
||||
inline uint height() const { return attrib_.viewport.y; }
|
||||
glm::vec3 resolution() const;
|
||||
void resize(int width, int height);
|
||||
float aspectRatio() const;
|
||||
std::string info() const;
|
||||
|
||||
|
||||
@@ -41,12 +41,18 @@ void FrameGrabbing::add(FrameGrabber *rec)
|
||||
grabbers_.push_back(rec);
|
||||
}
|
||||
|
||||
void FrameGrabbing::verify(FrameGrabber **rec)
|
||||
{
|
||||
if ( std::find(grabbers_.begin(), grabbers_.end(), *rec) == grabbers_.end() )
|
||||
*rec = nullptr;
|
||||
}
|
||||
|
||||
FrameGrabber *FrameGrabbing::front()
|
||||
{
|
||||
if (grabbers_.empty())
|
||||
return nullptr;
|
||||
else
|
||||
return grabbers_.front();
|
||||
|
||||
return grabbers_.front();
|
||||
}
|
||||
|
||||
struct fgId: public std::unary_function<FrameGrabber*, bool>
|
||||
|
||||
@@ -100,6 +100,7 @@ public:
|
||||
inline uint height() const { return height_; }
|
||||
|
||||
void add(FrameGrabber *rec);
|
||||
void verify(FrameGrabber **rec);
|
||||
FrameGrabber *front();
|
||||
FrameGrabber *get(uint64_t id);
|
||||
void stopAll();
|
||||
|
||||
@@ -927,10 +927,10 @@ View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::p
|
||||
sourceNode->rotation_.z = glm::radians( float(degrees) );
|
||||
overlay_rotation_clock_->visible_ = true;
|
||||
overlay_rotation_clock_->copyTransform(overlay_rotation_);
|
||||
info << "Angle " << degrees << "\u00b0"; // degree symbol
|
||||
info << "Angle " << degrees << UNICODE_DEGREE;
|
||||
}
|
||||
else
|
||||
info << "Angle " << std::fixed << std::setprecision(1) << glm::degrees(sourceNode->rotation_.z) << "\u00b0"; // degree symbol
|
||||
info << "Angle " << std::fixed << std::setprecision(1) << glm::degrees(sourceNode->rotation_.z) << UNICODE_DEGREE;
|
||||
|
||||
overlay_rotation_clock_hand_->visible_ = true;
|
||||
overlay_rotation_clock_hand_->translation_.x = s->stored_status_->translation_.x;
|
||||
|
||||
@@ -39,7 +39,7 @@ string GstToolkit::time_to_string(guint64 t, time_string_mode m)
|
||||
count++;
|
||||
}
|
||||
if (count < 2 )
|
||||
oss << '.'<< setw(1) << setfill('0') << (ms % 1000) / 10;
|
||||
oss << '.'<< setw(2) << setfill('0') << (ms % 1000) / 10;
|
||||
}
|
||||
else {
|
||||
// TIME_STRING_FIXED : fixed length string (11 chars) HH:mm:ss.ii"
|
||||
|
||||
482
ImGuiToolkit.cpp
482
ImGuiToolkit.cpp
@@ -145,10 +145,13 @@ bool ImGuiToolkit::ButtonIcon(int i, int j, const char *tooltip)
|
||||
ImVec2 uv1( uv0.x + 0.05, uv0.y + 0.05 );
|
||||
|
||||
ImGui::PushID( i*20 + j);
|
||||
bool ret = ImGui::ImageButton((void*)(intptr_t)textureicons, ImVec2(ImGui::GetTextLineHeightWithSpacing(),ImGui::GetTextLineHeightWithSpacing()), uv0, uv1, 3);
|
||||
ImGuiContext& g = *GImGui;
|
||||
bool ret = ImGui::ImageButton((void*)(intptr_t)textureicons,
|
||||
ImVec2(g.FontSize, g.FontSize),
|
||||
uv0, uv1, g.Style.FramePadding.y);
|
||||
ImGui::PopID();
|
||||
|
||||
if (tooltip != nullptr)
|
||||
if (tooltip != nullptr && ImGui::IsItemHovered())
|
||||
ImGuiToolkit::ToolTip(tooltip);
|
||||
|
||||
return ret;
|
||||
@@ -179,30 +182,45 @@ bool ImGuiToolkit::ButtonIconToggle(int i, int j, int i_toggle, int j_toggle, bo
|
||||
|
||||
bool ImGuiToolkit::IconButton(int i, int j, const char *tooltip)
|
||||
{
|
||||
bool ret = false;
|
||||
ImGui::PushID( i * 20 + j );
|
||||
|
||||
float frame_height = ImGui::GetFrameHeight();
|
||||
float frame_width = frame_height;
|
||||
ImVec2 draw_pos = ImGui::GetCursorScreenPos();
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
// toggle action : operate on the whole area
|
||||
ImGui::InvisibleButton("##iconbutton", ImVec2(frame_width, frame_height));
|
||||
if (ImGui::IsItemClicked())
|
||||
ret = true;
|
||||
// duplicate of ImGui::InvisibleButton to handle ImGuiButtonFlags_Repeat
|
||||
const ImGuiID id = window->GetID("##iconijbutton");
|
||||
float h = ImGui::GetFrameHeight();
|
||||
ImVec2 size = ImGui::CalcItemSize(ImVec2(h, h), 0.0f, 0.0f);
|
||||
ImVec2 draw_pos = window->DC.CursorPos;
|
||||
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
|
||||
ImGui::ItemSize(size);
|
||||
|
||||
ImGui::SetCursorScreenPos(draw_pos);
|
||||
Icon(i, j, !ret);
|
||||
if (!ImGui::ItemAdd(bb, id)){
|
||||
ImGui::PopID();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tooltip != nullptr && ImGui::IsItemHovered())
|
||||
ImGuiButtonFlags flags = 0;
|
||||
if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat)
|
||||
flags |= ImGuiButtonFlags_Repeat;
|
||||
bool hovered, held;
|
||||
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags);
|
||||
|
||||
// tooltip
|
||||
if (tooltip != nullptr && hovered)
|
||||
{
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("%s", tooltip);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
// draw icon
|
||||
ImGui::SetCursorScreenPos(draw_pos);
|
||||
Icon(i, j, !pressed);
|
||||
|
||||
ImGui::PopID();
|
||||
return ret;
|
||||
return pressed;
|
||||
}
|
||||
|
||||
|
||||
@@ -213,10 +231,11 @@ bool ImGuiToolkit::IconButton(const char* icon, const char *tooltip)
|
||||
|
||||
float frame_height = ImGui::GetFrameHeight();
|
||||
float frame_width = frame_height;
|
||||
|
||||
ImVec2 draw_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
// toggle action : operate on the whole area
|
||||
ImGui::InvisibleButton("##iconbutton", ImVec2(frame_width, frame_height));
|
||||
ImGui::InvisibleButton("##iconcharbutton", ImVec2(frame_width, frame_height));
|
||||
if (ImGui::IsItemClicked())
|
||||
ret = true;
|
||||
|
||||
@@ -337,6 +356,23 @@ bool ImGuiToolkit::ComboIcon (std::vector<std::pair<int, int> > icons, std::vect
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ImGuiToolkit::MenuItemIcon (int i, int j, const char* label, bool selected, bool enabled)
|
||||
{
|
||||
ImVec2 draw_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
// make some space
|
||||
char text_buf[256];
|
||||
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), " %s", label);
|
||||
|
||||
// draw menu item
|
||||
bool ret = ImGui::MenuItem(text_buf, NULL, selected, enabled);
|
||||
|
||||
// draw icon
|
||||
ImGui::SetCursorScreenPos(draw_pos);
|
||||
Icon(i, j, enabled);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ImGuiToolkit::ShowIconsWindow(bool* p_open)
|
||||
{
|
||||
@@ -419,10 +455,144 @@ void ImGuiToolkit::HelpIcon(const char* desc, int i, int j, const char* shortcut
|
||||
#define NUM_MARKS 10
|
||||
#define LARGE_TICK_INCREMENT 1
|
||||
#define LABEL_TICK_INCREMENT 3
|
||||
bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 start, guint64 end, guint64 step, const float width)
|
||||
|
||||
void ImGuiToolkit::RenderTimeline (ImGuiWindow* window, ImRect timeline_bbox, guint64 start, guint64 end, guint64 step, bool verticalflip)
|
||||
{
|
||||
static guint64 optimal_tick_marks[NUM_MARKS + LABEL_TICK_INCREMENT] = { 100 * MILISECOND, 500 * MILISECOND, 1 * SECOND, 2 * SECOND, 5 * SECOND, 10 * SECOND, 20 * SECOND, 1 * MINUTE, 2 * MINUTE, 5 * MINUTE, 10 * MINUTE, 60 * MINUTE, 60 * MINUTE };
|
||||
|
||||
const ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const float fontsize = g.FontSize;
|
||||
const ImU32 text_color = ImGui::GetColorU32(ImGuiCol_Text);
|
||||
|
||||
// by default, put a tick mark at every frame step and a large mark every second
|
||||
guint64 tick_step = step;
|
||||
guint64 large_tick_step = optimal_tick_marks[1+LARGE_TICK_INCREMENT];
|
||||
guint64 label_tick_step = optimal_tick_marks[1+LABEL_TICK_INCREMENT];
|
||||
guint64 tick_delta = 0;
|
||||
|
||||
// keep duration
|
||||
const guint64 duration = end - start;
|
||||
|
||||
// how many pixels to represent one frame step?
|
||||
const float step_ = static_cast<float> ( static_cast<double>(tick_step) / static_cast<double>(duration) );
|
||||
float tick_step_pixels = timeline_bbox.GetWidth() * step_;
|
||||
|
||||
// large space
|
||||
if (tick_step_pixels > 5.f && step > 0)
|
||||
{
|
||||
// try to put a label ticks every second
|
||||
label_tick_step = (SECOND / step) * step;
|
||||
large_tick_step = label_tick_step % 5 ? (label_tick_step % 2 ? label_tick_step : label_tick_step / 2 ) : label_tick_step / 5;
|
||||
tick_delta = SECOND - label_tick_step;
|
||||
|
||||
// round to nearest
|
||||
if (tick_delta > step / 2) {
|
||||
label_tick_step += step;
|
||||
large_tick_step += step;
|
||||
tick_delta = SECOND - label_tick_step;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// while there is less than 5 pixels between two tick marks (or at last optimal tick mark)
|
||||
for ( int i=0; i<10 && tick_step_pixels < 5.f; ++i )
|
||||
{
|
||||
// try to use the optimal tick marks pre-defined
|
||||
tick_step = optimal_tick_marks[i];
|
||||
large_tick_step = optimal_tick_marks[i+LARGE_TICK_INCREMENT];
|
||||
label_tick_step = optimal_tick_marks[i+LABEL_TICK_INCREMENT];
|
||||
tick_step_pixels = timeline_bbox.GetWidth() * static_cast<float> ( static_cast<double>(tick_step) / static_cast<double>(duration) );
|
||||
}
|
||||
}
|
||||
|
||||
// render tics and text
|
||||
char text_buf[24];
|
||||
|
||||
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
|
||||
|
||||
// render tick and text END
|
||||
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "%s",
|
||||
GstToolkit::time_to_string(end, GstToolkit::TIME_STRING_MINIMAL).c_str());
|
||||
ImVec2 duration_label_size = ImGui::CalcTextSize(text_buf, NULL);
|
||||
ImVec2 duration_label_pos = timeline_bbox.GetTR() + ImVec2( -2.f -duration_label_size.x, fontsize);
|
||||
if (verticalflip)
|
||||
duration_label_pos.y -= fontsize;
|
||||
ImGui::RenderTextClipped( duration_label_pos, duration_label_pos + duration_label_size,
|
||||
text_buf, NULL, &duration_label_size);
|
||||
window->DrawList->AddLine( timeline_bbox.GetTR(), timeline_bbox.GetBR(), text_color, 1.5f);
|
||||
|
||||
// render tick and text START
|
||||
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "%s",
|
||||
GstToolkit::time_to_string(start, GstToolkit::TIME_STRING_MINIMAL).c_str());
|
||||
ImVec2 beginning_label_size = ImGui::CalcTextSize(text_buf, NULL);
|
||||
ImVec2 beginning_label_pos = timeline_bbox.GetTL() + ImVec2(3.f, fontsize);
|
||||
if (verticalflip)
|
||||
beginning_label_pos.y -= fontsize;
|
||||
if ( beginning_label_pos.x + beginning_label_size.x < duration_label_pos . x) {
|
||||
ImGui::RenderTextClipped( beginning_label_pos, beginning_label_pos + beginning_label_size,
|
||||
text_buf, NULL, &beginning_label_size);
|
||||
}
|
||||
window->DrawList->AddLine( timeline_bbox.GetTL(), timeline_bbox.GetBL(), text_color, 1.5f);
|
||||
|
||||
ImGui::PopFont();
|
||||
|
||||
// render the tick marks along TIMELINE
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] -ImVec4(0.f,0.f,0.f,0.4f));
|
||||
ImVec2 pos = verticalflip ? timeline_bbox.GetBL() : timeline_bbox.GetTL();
|
||||
|
||||
// loop ticks from start to end
|
||||
guint64 tick = tick_step > 0 ? (start / tick_step) * tick_step : 0;
|
||||
while ( tick < end )
|
||||
{
|
||||
// large tick mark ?
|
||||
float tick_length = (tick%large_tick_step) ? style.FramePadding.y : fontsize - style.FramePadding.y;
|
||||
|
||||
// label tick mark
|
||||
if ( (tick%label_tick_step) < 1 )
|
||||
{
|
||||
// larger tick mark for label
|
||||
tick_length = fontsize;
|
||||
|
||||
// correct tick value for delta for approximation to rounded second marks
|
||||
guint64 ticklabel = tick + ( tick_delta * tick / label_tick_step);
|
||||
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "%s",
|
||||
GstToolkit::time_to_string(ticklabel, GstToolkit::TIME_STRING_MINIMAL).c_str());
|
||||
ImVec2 label_size = ImGui::CalcTextSize(text_buf, NULL);
|
||||
ImVec2 mini = ImVec2( pos.x - label_size.x / 2.f, pos.y);
|
||||
ImVec2 maxi = ImVec2( pos.x + label_size.x / 2.f, pos.y);
|
||||
|
||||
if (verticalflip) {
|
||||
mini.y -= tick_length + label_size.y;
|
||||
maxi.y -= tick_length;
|
||||
}
|
||||
else {
|
||||
mini.y += tick_length;
|
||||
maxi.y += tick_length + label_size.y;
|
||||
}
|
||||
|
||||
// do not overlap with labels for beginning and duration
|
||||
if (mini.x - style.ItemSpacing.x > (beginning_label_pos.x + beginning_label_size.x) && maxi.x + style.ItemSpacing.x < duration_label_pos.x)
|
||||
ImGui::RenderTextClipped(mini, maxi, text_buf, NULL, &label_size);
|
||||
}
|
||||
|
||||
// draw the tick mark each step
|
||||
window->DrawList->AddLine( pos, pos + ImVec2(0.f, verticalflip ? -tick_length : tick_length), text_color);
|
||||
|
||||
// next tick
|
||||
tick += tick_step;
|
||||
float tick_percent = static_cast<float> ( static_cast<double>(tick-start) / static_cast<double>(duration) );
|
||||
if (verticalflip)
|
||||
pos = ImLerp(timeline_bbox.GetBL(), timeline_bbox.GetBR(), tick_percent);
|
||||
else
|
||||
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
|
||||
|
||||
}
|
||||
ImGui::PopStyleColor(1);
|
||||
|
||||
}
|
||||
|
||||
bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 start, guint64 end, guint64 step, const float width)
|
||||
{
|
||||
// get window
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
@@ -448,19 +618,17 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 star
|
||||
return false;
|
||||
|
||||
// cursor size
|
||||
const float cursor_scale = 1.f;
|
||||
const float cursor_width = 0.5f * fontsize * cursor_scale;
|
||||
const float cursor_width = 0.5f * fontsize;
|
||||
|
||||
// TIMELINE is inside the bbox, in a slightly smaller bounding box
|
||||
ImRect timeline_bbox(bbox);
|
||||
timeline_bbox.Expand( ImVec2(-cursor_width, -style.FramePadding.y) );
|
||||
timeline_bbox.Expand( ImVec2() - style.FramePadding );
|
||||
|
||||
// SLIDER is inside the timeline
|
||||
ImRect slider_bbox( timeline_bbox.GetTL() + ImVec2(-cursor_width + 2.f, cursor_width + 4.f ), timeline_bbox.GetBR() + ImVec2( cursor_width - 2.f, 0.f ) );
|
||||
|
||||
// units conversion: from time to float (calculation made with higher precision first)
|
||||
float time_ = static_cast<float> ( static_cast<double>(*time - start) / static_cast<double>(end) );
|
||||
float step_ = static_cast<float> ( static_cast<double>(step) / static_cast<double>(end) );
|
||||
float time_ = static_cast<float> ( static_cast<double>(*time - start) / static_cast<double>(end - start) );
|
||||
|
||||
//
|
||||
// SECOND GET USER INPUT AND PERFORM CHANGES AND DECISIONS
|
||||
@@ -492,7 +660,7 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 star
|
||||
&time_end, "%.2f", 1.f, ImGuiSliderFlags_None, &grab_slider_bb);
|
||||
if (value_changed){
|
||||
// g_print("slider %f %ld \n", time_slider, static_cast<guint64> ( static_cast<double>(time_slider) * static_cast<double>(duration) ));
|
||||
*time = static_cast<guint64> ( 0.1 * static_cast<double>(time_slider) * static_cast<double>(end) ) + start;
|
||||
*time = static_cast<guint64> ( 0.1 * static_cast<double>(time_slider) * static_cast<double>(end - start) ) + start;
|
||||
grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
|
||||
}
|
||||
|
||||
@@ -504,84 +672,8 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 star
|
||||
const ImU32 frame_col = ImGui::GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
|
||||
ImGui::RenderFrame(bbox.Min, bbox.Max, frame_col, true, style.FrameRounding);
|
||||
|
||||
// by default, put a tick mark at every frame step and a large mark every second
|
||||
guint64 tick_step = step;
|
||||
guint64 large_tick_step = optimal_tick_marks[1+LARGE_TICK_INCREMENT];
|
||||
guint64 label_tick_step = optimal_tick_marks[1+LABEL_TICK_INCREMENT];
|
||||
|
||||
// how many pixels to represent one frame step?
|
||||
float tick_step_pixels = timeline_bbox.GetWidth() * step_;
|
||||
|
||||
// tick at each step: add a label every 0 frames
|
||||
if (tick_step_pixels > 5.f)
|
||||
{
|
||||
large_tick_step = 10 * step;
|
||||
label_tick_step = 30 * step;
|
||||
}
|
||||
else {
|
||||
// while there is less than 5 pixels between two tick marks (or at last optimal tick mark)
|
||||
for ( int i=0; i<10 && tick_step_pixels < 5.f; ++i )
|
||||
{
|
||||
// try to use the optimal tick marks pre-defined
|
||||
tick_step = optimal_tick_marks[i];
|
||||
large_tick_step = optimal_tick_marks[i+LARGE_TICK_INCREMENT];
|
||||
label_tick_step = optimal_tick_marks[i+LABEL_TICK_INCREMENT];
|
||||
tick_step_pixels = timeline_bbox.GetWidth() * static_cast<float> ( static_cast<double>(tick_step) / static_cast<double>(end) );
|
||||
}
|
||||
}
|
||||
|
||||
// render the tick marks along TIMELINE
|
||||
ImU32 color = ImGui::GetColorU32( style.Colors[ImGuiCol_Text] );
|
||||
pos = timeline_bbox.GetTL();
|
||||
guint64 tick = 0;
|
||||
char overlay_buf[24];
|
||||
|
||||
// render text duration
|
||||
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%s",
|
||||
GstToolkit::time_to_string(end, GstToolkit::TIME_STRING_MINIMAL).c_str());
|
||||
ImVec2 overlay_size = ImGui::CalcTextSize(overlay_buf, NULL);
|
||||
ImVec2 duration_label = bbox.GetBR() - overlay_size - ImVec2(3.f, 3.f);
|
||||
if (overlay_size.x > 0.0f)
|
||||
ImGui::RenderTextClipped( duration_label, bbox.Max, overlay_buf, NULL, &overlay_size);
|
||||
|
||||
// render tick marks
|
||||
while ( tick < end)
|
||||
{
|
||||
// large tick mark
|
||||
float tick_length = !(tick%large_tick_step) ? fontsize - style.FramePadding.y : style.FramePadding.y;
|
||||
|
||||
// label tick mark
|
||||
if ( !(tick%label_tick_step) ) {
|
||||
tick_length = fontsize;
|
||||
|
||||
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%s",
|
||||
GstToolkit::time_to_string(tick, GstToolkit::TIME_STRING_MINIMAL).c_str());
|
||||
overlay_size = ImGui::CalcTextSize(overlay_buf, NULL);
|
||||
ImVec2 mini = ImVec2( pos.x - overlay_size.x / 2.f, pos.y + tick_length );
|
||||
ImVec2 maxi = ImVec2( pos.x + overlay_size.x / 2.f, pos.y + tick_length + overlay_size.y );
|
||||
// do not overlap with label for duration
|
||||
if (maxi.x < duration_label.x)
|
||||
ImGui::RenderTextClipped(mini, maxi, overlay_buf, NULL, &overlay_size);
|
||||
}
|
||||
|
||||
// draw the 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>(end) );
|
||||
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
|
||||
}
|
||||
|
||||
// tick EOF
|
||||
window->DrawList->AddLine( timeline_bbox.GetTR(), timeline_bbox.GetTR() + ImVec2(0.f, fontsize), color);
|
||||
|
||||
// disabled: render position
|
||||
// ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%s", GstToolkit::time_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);
|
||||
// render the timeline
|
||||
RenderTimeline(window, timeline_bbox, start, end, step);
|
||||
|
||||
// draw slider grab handle
|
||||
if (grab_slider_bb.Max.x > grab_slider_bb.Min.x) {
|
||||
@@ -589,14 +681,67 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 star
|
||||
}
|
||||
|
||||
// draw the cursor
|
||||
color = ImGui::GetColorU32(style.Colors[ImGuiCol_SliderGrab]);
|
||||
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width, 2.f);
|
||||
ImGui::RenderArrow(window->DrawList, pos, color, ImGuiDir_Up, cursor_scale);
|
||||
ImGui::RenderArrow(window->DrawList, pos, ImGui::GetColorU32(ImGuiCol_SliderGrab), ImGuiDir_Up);
|
||||
|
||||
return left_mouse_press;
|
||||
}
|
||||
|
||||
//bool ImGuiToolkit::InvisibleSliderInt(const char* label, guint64 *index, guint64 min, guint64 max, ImVec2 size)
|
||||
|
||||
void ImGuiToolkit::Timeline (const char* label, guint64 time, guint64 start, guint64 end, guint64 step, const float width)
|
||||
{
|
||||
// get window
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return;
|
||||
|
||||
// get style & id
|
||||
const ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const float fontsize = g.FontSize;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
|
||||
//
|
||||
// FIRST PREPARE ALL data structures
|
||||
//
|
||||
|
||||
// widget bounding box
|
||||
const float height = 2.f * (fontsize + style.FramePadding.y);
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
ImVec2 size = ImVec2(width, height);
|
||||
ImRect bbox(pos, pos + size);
|
||||
ImGui::ItemSize(size, style.FramePadding.y);
|
||||
if (!ImGui::ItemAdd(bbox, id))
|
||||
return;
|
||||
|
||||
// cursor size
|
||||
const float cursor_width = 0.5f * fontsize;
|
||||
|
||||
// TIMELINE is inside the bbox, in a slightly smaller bounding box
|
||||
ImRect timeline_bbox(bbox);
|
||||
timeline_bbox.Expand( ImVec2() - style.FramePadding );
|
||||
|
||||
// units conversion: from time to float (calculation made with higher precision first)
|
||||
guint64 duration = end - start;
|
||||
float time_ = static_cast<float> ( static_cast<double>(time - start) / static_cast<double>(duration) );
|
||||
|
||||
//
|
||||
// THIRD RENDER
|
||||
//
|
||||
|
||||
// Render the bounding box
|
||||
ImGui::RenderFrame(bbox.Min, bbox.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
|
||||
|
||||
// render the timeline
|
||||
RenderTimeline(window, timeline_bbox, start, end, step);
|
||||
|
||||
// draw the cursor
|
||||
if ( time_ > -FLT_EPSILON && time_ < 1.f ) {
|
||||
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width, 2.f);
|
||||
ImGui::RenderArrow(window->DrawList, pos, ImGui::GetColorU32(ImGuiCol_SliderGrab), ImGuiDir_Up);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGuiToolkit::InvisibleSliderInt(const char* label, uint *index, uint min, uint max, ImVec2 size)
|
||||
{
|
||||
// get window
|
||||
@@ -737,8 +882,10 @@ bool ImGuiToolkit::EditPlotLines(const char* label, float *array, int values_cou
|
||||
return array_changed;
|
||||
}
|
||||
|
||||
|
||||
bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array, float *lines_array,
|
||||
int values_count, float values_min, float values_max, bool *released, const ImVec2 size)
|
||||
int values_count, float values_min, float values_max, guint64 start, guint64 end,
|
||||
bool edit_histogram, bool *released, const ImVec2 size)
|
||||
{
|
||||
bool array_changed = false;
|
||||
|
||||
@@ -748,7 +895,7 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array,
|
||||
return false;
|
||||
|
||||
// capture coordinates before any draw or action
|
||||
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
|
||||
const ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
|
||||
ImVec2 mouse_pos_in_canvas = ImVec2(ImGui::GetIO().MousePos.x - canvas_pos.x, ImGui::GetIO().MousePos.y - canvas_pos.y);
|
||||
|
||||
// get id
|
||||
@@ -764,9 +911,7 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array,
|
||||
*released = false;
|
||||
|
||||
// read user input and activate widget
|
||||
const bool left_mouse_press = ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
||||
const bool right_mouse_press = ImGui::IsMouseDown(ImGuiMouseButton_Right) | (ImGui::GetIO().KeyAlt & left_mouse_press) ;
|
||||
const bool mouse_press = left_mouse_press | right_mouse_press;
|
||||
const bool mouse_press = ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
||||
const bool hovered = ImGui::ItemHoverable(bbox, id);
|
||||
bool temp_input_is_active = ImGui::TempInputIsActive(id);
|
||||
if (!temp_input_is_active)
|
||||
@@ -782,30 +927,39 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array,
|
||||
else
|
||||
return false;
|
||||
|
||||
static ImVec4* colors = ImGui::GetStyle().Colors;
|
||||
ImVec4 bg_color = hovered ? colors[ImGuiCol_FrameBgHovered] : colors[ImGuiCol_FrameBg];
|
||||
const ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const float _h_space = style.WindowPadding.x;
|
||||
ImVec4 bg_color = hovered ? style.Colors[ImGuiCol_FrameBgHovered] : style.Colors[ImGuiCol_FrameBg];
|
||||
|
||||
// prepare index
|
||||
double x = (mouse_pos_in_canvas.x - _h_space) / (size.x - 2.f * _h_space);
|
||||
size_t index = CLAMP( (int) floor(static_cast<double>(values_count) * x), 0, values_count);
|
||||
char cursor_text[64];
|
||||
guint64 time = start + (index * end) / static_cast<guint64>(values_count);
|
||||
ImFormatString(cursor_text, IM_ARRAYSIZE(cursor_text), "%s",
|
||||
GstToolkit::time_to_string(time, GstToolkit::TIME_STRING_MINIMAL).c_str());
|
||||
|
||||
// enter edit if widget is active
|
||||
if (ImGui::GetActiveID() == id) {
|
||||
|
||||
bg_color = colors[ImGuiCol_FrameBgActive];
|
||||
bg_color = style.Colors[ImGuiCol_FrameBgActive];
|
||||
|
||||
// keep active area while mouse is pressed
|
||||
static bool active = false;
|
||||
static uint previous_index = UINT32_MAX;
|
||||
static size_t previous_index = UINT32_MAX;
|
||||
if (mouse_press)
|
||||
{
|
||||
|
||||
float x = (float) values_count * mouse_pos_in_canvas.x / bbox.GetWidth();
|
||||
uint index = CLAMP( (int) floor(x), 0, values_count-1);
|
||||
|
||||
float y = mouse_pos_in_canvas.y / bbox.GetHeight();
|
||||
y = CLAMP( (y * (values_max-values_min)) + values_min, values_min, values_max);
|
||||
float val = mouse_pos_in_canvas.y / bbox.GetHeight();
|
||||
val = CLAMP( (val * (values_max-values_min)) + values_min, values_min, values_max);
|
||||
|
||||
if (previous_index == UINT32_MAX)
|
||||
previous_index = index;
|
||||
|
||||
if (right_mouse_press){
|
||||
const size_t left = MIN(previous_index, index);
|
||||
const size_t right = MAX(previous_index, index);
|
||||
|
||||
if (edit_histogram){
|
||||
static float target_value = values_min;
|
||||
|
||||
// toggle value histo
|
||||
@@ -814,17 +968,18 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array,
|
||||
active = true;
|
||||
}
|
||||
|
||||
for (int i = MIN(previous_index, index); i < MAX(previous_index, index); ++i)
|
||||
for (size_t i = left; i < right; ++i)
|
||||
histogram_array[i] = target_value;
|
||||
}
|
||||
else {
|
||||
lines_array[index] = values_max - y;
|
||||
for (int i = MIN(previous_index, index); i < MAX(previous_index, index); ++i)
|
||||
lines_array[i] = values_max - y;
|
||||
const float target_value = values_max - val;
|
||||
|
||||
for (size_t i = left; i < right; ++i)
|
||||
lines_array[i] = target_value;
|
||||
|
||||
}
|
||||
|
||||
previous_index = index;
|
||||
|
||||
array_changed = true;
|
||||
}
|
||||
// release active widget on mouse release
|
||||
@@ -840,9 +995,85 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array,
|
||||
// back to draw
|
||||
ImGui::SetCursorScreenPos(canvas_pos);
|
||||
|
||||
// plot histogram (with frame)
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, bg_color);
|
||||
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, style.Colors[ImGuiCol_TitleBg]); // a dark color
|
||||
char buf[128];
|
||||
sprintf(buf, "##Histo%s", label);
|
||||
ImGui::PlotHistogram(buf, histogram_array, values_count, 0, NULL, values_min, values_max, size);
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
ImGui::SetCursorScreenPos(canvas_pos);
|
||||
|
||||
// plot (transparent) lines
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0));
|
||||
sprintf(buf, "##Lines%s", label);
|
||||
ImGui::PlotLines(buf, lines_array, values_count, 0, NULL, values_min, values_max, size);
|
||||
ImGui::PopStyleColor(1);
|
||||
|
||||
// draw the cursor
|
||||
if (hovered) {
|
||||
// prepare color and text
|
||||
const ImU32 cur_color = ImGui::GetColorU32(ImGuiCol_CheckMark);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, cur_color);
|
||||
ImVec2 label_size = ImGui::CalcTextSize(cursor_text, NULL);
|
||||
|
||||
// render cursor depending on action
|
||||
mouse_pos_in_canvas.x = CLAMP(mouse_pos_in_canvas.x, _h_space, size.x - _h_space);
|
||||
ImVec2 cursor_pos = canvas_pos;
|
||||
if (edit_histogram) {
|
||||
cursor_pos = cursor_pos + ImVec2(mouse_pos_in_canvas.x, 4.f);
|
||||
window->DrawList->AddLine( cursor_pos, cursor_pos + ImVec2(0.f, size.y - 8.f), cur_color);
|
||||
}
|
||||
else {
|
||||
cursor_pos = cursor_pos + mouse_pos_in_canvas;
|
||||
window->DrawList->AddCircleFilled( cursor_pos, 3.f, cur_color, 8);
|
||||
}
|
||||
|
||||
// draw text
|
||||
cursor_pos.y = canvas_pos.y + size.y - label_size.y - 1.f;
|
||||
if (mouse_pos_in_canvas.x + label_size.x < size.x - 2.f * _h_space)
|
||||
cursor_pos.x += _h_space;
|
||||
else
|
||||
cursor_pos.x -= label_size.x + _h_space;
|
||||
ImGui::RenderTextClipped(cursor_pos, cursor_pos + label_size, cursor_text, NULL, &label_size);
|
||||
|
||||
ImGui::PopStyleColor(1);
|
||||
}
|
||||
|
||||
return array_changed;
|
||||
}
|
||||
|
||||
void ImGuiToolkit::ShowPlotHistoLines(const char* label, float *histogram_array, float *lines_array, int values_count, float values_min, float values_max, const ImVec2 size)
|
||||
{
|
||||
// get window
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return;
|
||||
|
||||
// capture coordinates before any draw or action
|
||||
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
// get id
|
||||
const ImGuiID id = window->GetID(label);
|
||||
|
||||
// add item
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
ImRect bbox(pos, pos + size);
|
||||
ImGui::ItemSize(size);
|
||||
if (!ImGui::ItemAdd(bbox, id))
|
||||
return;
|
||||
|
||||
const ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
ImVec4 bg_color = style.Colors[ImGuiCol_FrameBg];
|
||||
|
||||
// back to draw
|
||||
ImGui::SetCursorScreenPos(canvas_pos);
|
||||
|
||||
// plot transparent histogram
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, bg_color);
|
||||
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, colors[ImGuiCol_TitleBg]);
|
||||
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0, 0, 0, 250));
|
||||
char buf[128];
|
||||
sprintf(buf, "##Histo%s", label);
|
||||
ImGui::PlotHistogram(buf, histogram_array, values_count, 0, NULL, values_min, values_max, size);
|
||||
@@ -856,7 +1087,6 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array,
|
||||
ImGui::PlotLines(buf, lines_array, values_count, 0, NULL, values_min, values_max, size);
|
||||
ImGui::PopStyleColor(1);
|
||||
|
||||
return array_changed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include "rsc/fonts/IconsFontAwesome5.h"
|
||||
|
||||
namespace ImGuiToolkit
|
||||
@@ -13,30 +13,35 @@ namespace ImGuiToolkit
|
||||
// Icons from resource icon.dds
|
||||
void Icon (int i, int j, bool enabled = true);
|
||||
bool IconButton (int i, int j, const char *tooltips = nullptr);
|
||||
bool IconButton (const char* icon = ICON_FA_EXCLAMATION_CIRCLE, const char *tooltips = nullptr);
|
||||
bool IconButton (const char* icon, const char *tooltips = nullptr);
|
||||
bool IconToggle (int i, int j, int i_toggle, int j_toggle, bool* toggle, const char *tooltips[] = nullptr);
|
||||
void ShowIconsWindow(bool* p_open);
|
||||
|
||||
// icon buttons
|
||||
// buttons and gui items with icon
|
||||
bool ButtonIcon (int i, int j, const char* tooltip = nullptr);
|
||||
bool ButtonIconToggle (int i, int j, int i_toggle, int j_toggle, bool* toggle);
|
||||
bool ButtonIconMultistate (std::vector<std::pair<int, int> > icons, int* state);
|
||||
bool ComboIcon (std::vector<std::pair<int, int> > icons, std::vector<std::string> labels, int* state);
|
||||
bool MenuItemIcon (int i, int j, const char* label, bool selected = false, bool enabled = true);
|
||||
|
||||
// utility buttons
|
||||
// buttons
|
||||
bool ButtonToggle (const char* label, bool* toggle);
|
||||
bool ButtonSwitch (const char* label, bool* toggle , const char *help = nullptr);
|
||||
void ButtonOpenUrl (const char* label, const char* url, const ImVec2& size_arg = ImVec2(0,0));
|
||||
|
||||
// tooltip and mouse over
|
||||
void ToolTip (const char* desc, const char* shortcut = nullptr);
|
||||
void HelpMarker (const char* desc, const char* icon = ICON_FA_QUESTION_CIRCLE, const char* shortcut = nullptr);
|
||||
void HelpIcon (const char* desc, int i = 19, int j = 5, const char* shortcut = nullptr);
|
||||
|
||||
// utility sliders
|
||||
// sliders
|
||||
bool TimelineSlider (const char* label, guint64 *time, guint64 start, guint64 end, guint64 step, const float width);
|
||||
void RenderTimeline (struct ImGuiWindow* window, struct ImRect timeline_bbox, guint64 start, guint64 end, guint64 step, bool verticalflip = false);
|
||||
void Timeline (const char* label, guint64 time, guint64 start, guint64 end, guint64 step, const float width);
|
||||
bool InvisibleSliderInt(const char* label, uint *index, uint min, uint max, const ImVec2 size);
|
||||
bool EditPlotLines(const char* label, float *array, int values_count, float values_min, float values_max, const ImVec2 size);
|
||||
bool EditPlotHistoLines(const char* label, float *histogram_array, float *lines_array, int values_count, float values_min, float values_max, bool *released, const ImVec2 size);
|
||||
bool EditPlotHistoLines(const char* label, float *histogram_array, float *lines_array, int values_count, float values_min, float values_max, guint64 start, guint64 end, bool cut, bool *released, const ImVec2 size);
|
||||
void ShowPlotHistoLines(const char* label, float *histogram_array, float *lines_array, int values_count, float values_min, float values_max, const ImVec2 size);
|
||||
|
||||
// fonts from ressources 'fonts/'
|
||||
typedef enum {
|
||||
@@ -49,12 +54,11 @@ namespace ImGuiToolkit
|
||||
void SetFont (font_style type, const std::string &ttf_font_name, int pointsize, int oversample = 2);
|
||||
void PushFont (font_style type);
|
||||
|
||||
void WindowText(const char* window_name, ImVec2 window_pos, const char* text);
|
||||
bool WindowButton(const char* window_name, ImVec2 window_pos, const char* text);
|
||||
void WindowDragFloat(const char* window_name, ImVec2 window_pos, float* v, float v_speed, float v_min, float v_max, const char* format);
|
||||
// text input
|
||||
bool InputText(const char* label, std::string* str);
|
||||
bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), int linesize = 0);
|
||||
|
||||
|
||||
// color of gui items
|
||||
// accent color of UI
|
||||
typedef enum {
|
||||
ACCENT_BLUE =0,
|
||||
ACCENT_ORANGE,
|
||||
@@ -63,8 +67,11 @@ namespace ImGuiToolkit
|
||||
void SetAccentColor (accent_color color);
|
||||
struct ImVec4 HighlightColor (bool active = true);
|
||||
|
||||
bool InputText(const char* label, std::string* str);
|
||||
bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), int linesize = 0);
|
||||
// varia
|
||||
void WindowText(const char* window_name, ImVec2 window_pos, const char* text);
|
||||
bool WindowButton(const char* window_name, ImVec2 window_pos, const char* text);
|
||||
void WindowDragFloat(const char* window_name, ImVec2 window_pos, float* v, float v_speed, float v_min, float v_max, const char* format);
|
||||
|
||||
}
|
||||
|
||||
#endif // __IMGUI_TOOLKIT_H_
|
||||
|
||||
148
ImGuiVisitor.cpp
148
ImGuiVisitor.cpp
@@ -553,9 +553,23 @@ void ImGuiVisitor::visit (MediaSource& s)
|
||||
else
|
||||
ImGui::Text("Video File");
|
||||
|
||||
if ( ImGui::Button(IMGUI_TITLE_MEDIAPLAYER, ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
UserInterface::manager().showMediaPlayer( s.mediaplayer());
|
||||
// Media info
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, 10.f + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
// folder
|
||||
std::string path = SystemToolkit::path_filename(s.path());
|
||||
std::string label = BaseToolkit::trunc_string(path, 25);
|
||||
label = BaseToolkit::transliterate(label);
|
||||
@@ -573,7 +587,27 @@ void ImGuiVisitor::visit (SessionFileSource& s)
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Session File");
|
||||
// ImGui::Text("%s", SystemToolkit::base_filename(s.path()).c_str());
|
||||
|
||||
// info
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, 10.f + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
if ( ImGui::Button( ICON_FA_FILE_EXPORT " Import", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
Mixer::manager().import( &s );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Sources");
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(3, 2)) s.session()->setFading(0.f);
|
||||
float f = s.session()->fading();
|
||||
@@ -586,7 +620,7 @@ void ImGuiVisitor::visit (SessionFileSource& s)
|
||||
oss << s.name() << ": Fading " << std::setprecision(2) << f;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
if ( ImGui::Button( ICON_FA_FILE_UPLOAD " Open Session", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
if ( ImGui::Button( ICON_FA_FILE_UPLOAD " Open", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
Mixer::manager().set( s.detach() );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("File");
|
||||
@@ -598,9 +632,6 @@ void ImGuiVisitor::visit (SessionFileSource& s)
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Folder");
|
||||
|
||||
ImGui::Text("Contains %d sources.", s.session()->numSource());
|
||||
if ( ImGui::Button( ICON_FA_FILE_EXPORT " Import", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
Mixer::manager().import( &s );
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit (SessionGroupSource& s)
|
||||
@@ -611,12 +642,26 @@ void ImGuiVisitor::visit (SessionGroupSource& s)
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Flat Sesion group");
|
||||
ImGui::Text("Contains %d sources.", s.session()->numSource());
|
||||
|
||||
// info
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, 10.f + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
if ( ImGui::Button( ICON_FA_UPLOAD " Expand", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ){
|
||||
Mixer::manager().import( &s );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit (RenderSource& s)
|
||||
@@ -645,12 +690,29 @@ void ImGuiVisitor::visit (PatternSource& s)
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Pattern");
|
||||
|
||||
// stream info
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, 10.f + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if (ImGui::BeginCombo("##Patterns", Pattern::pattern_types[s.pattern()->type()].c_str()) )
|
||||
{
|
||||
for (uint p = 0; p < Pattern::pattern_types.size(); ++p){
|
||||
if (ImGui::Selectable( Pattern::pattern_types[p].c_str() )) {
|
||||
s.setPattern(p, s.pattern()->resolution());
|
||||
info.reset();
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << ": Pattern " << Pattern::pattern_types[p];
|
||||
Action::manager().store(oss.str());
|
||||
@@ -668,6 +730,21 @@ void ImGuiVisitor::visit (DeviceSource& s)
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Device");
|
||||
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, 10.f + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if (ImGui::BeginCombo("##Hardware", s.device().c_str()))
|
||||
{
|
||||
@@ -675,6 +752,7 @@ void ImGuiVisitor::visit (DeviceSource& s)
|
||||
std::string namedev = Device::manager().name(d);
|
||||
if (ImGui::Selectable( namedev.c_str() )) {
|
||||
s.setDevice(namedev);
|
||||
info.reset();
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << " Device " << namedev;
|
||||
Action::manager().store(oss.str());
|
||||
@@ -682,12 +760,7 @@ void ImGuiVisitor::visit (DeviceSource& s)
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
DeviceConfigSet confs = Device::manager().config( Device::manager().index(s.device().c_str()));
|
||||
if ( !confs.empty()) {
|
||||
DeviceConfig best = *confs.rbegin();
|
||||
float fps = static_cast<float>(best.fps_numerator) / static_cast<float>(best.fps_denominator);
|
||||
ImGui::Text("%s %s %dx%d@%.1ffps", best.stream.c_str(), best.format.c_str(), best.width, best.height, fps);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ImGuiVisitor::visit (NetworkSource& s)
|
||||
@@ -699,16 +772,28 @@ void ImGuiVisitor::visit (NetworkSource& s)
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_STREAM, 0.9f));
|
||||
ImGui::Text("%s", s.connection().c_str());
|
||||
ImGui::PopStyleColor(1);
|
||||
NetworkStream *ns = s.networkStream();
|
||||
ImGui::Text(" - %s (%dx%d)\n - Server address %s", NetworkToolkit::protocol_name[ns->protocol()],
|
||||
ns->resolution().x, ns->resolution().y, ns->serverAddress().c_str());
|
||||
|
||||
// network info
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, 10.f + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
if ( ImGui::Button( ICON_FA_REPLY " Reconnect", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
{
|
||||
// TODO : reload ?
|
||||
s.setConnection(s.connection());
|
||||
info.reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -717,13 +802,23 @@ void ImGuiVisitor::visit (MultiFileSource& s)
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Images sequence");
|
||||
static int64_t id = s.id();
|
||||
static int64_t id = 0;
|
||||
|
||||
// information text
|
||||
std::ostringstream msg;
|
||||
msg << "Sequence of " << s.sequence().max - s.sequence().min + 1 << " ";
|
||||
msg << s.sequence().codec << " images";
|
||||
ImGui::Text("%s", msg.str().c_str());
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::SameLine(0, ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
ImGui::SetCursorPos(pos);
|
||||
}
|
||||
|
||||
// change range
|
||||
static int _begin = -1;
|
||||
@@ -764,5 +859,6 @@ void ImGuiVisitor::visit (MultiFileSource& s)
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Folder");
|
||||
|
||||
id = s.id();
|
||||
if (id != s.id())
|
||||
id = s.id();
|
||||
}
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
#define IMGUIVISITOR_H
|
||||
|
||||
#include "Visitor.h"
|
||||
#include "InfoVisitor.h"
|
||||
|
||||
class ImGuiVisitor: public Visitor
|
||||
{
|
||||
InfoVisitor info;
|
||||
|
||||
public:
|
||||
ImGuiVisitor();
|
||||
|
||||
|
||||
271
InfoVisitor.cpp
Normal file
271
InfoVisitor.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
#include "InfoVisitor.h"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/matrix_access.hpp>
|
||||
|
||||
#include <tinyxml2.h>
|
||||
#include "tinyxml2Toolkit.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "Log.h"
|
||||
#include "Scene.h"
|
||||
#include "Primitives.h"
|
||||
#include "ImageShader.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
#include "MediaPlayer.h"
|
||||
#include "MediaSource.h"
|
||||
#include "SessionSource.h"
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "SessionCreator.h"
|
||||
#include "SessionVisitor.h"
|
||||
#include "Settings.h"
|
||||
#include "Mixer.h"
|
||||
#include "ActionManager.h"
|
||||
|
||||
#include "BaseToolkit.h"
|
||||
#include "UserInterfaceManager.h"
|
||||
#include "SystemToolkit.h"
|
||||
|
||||
|
||||
InfoVisitor::InfoVisitor() : brief_(true), current_id_(0)
|
||||
{
|
||||
}
|
||||
|
||||
void InfoVisitor::visit(Node &n)
|
||||
{
|
||||
}
|
||||
|
||||
void InfoVisitor::visit(Group &n)
|
||||
{
|
||||
}
|
||||
|
||||
void InfoVisitor::visit(Switch &n)
|
||||
{
|
||||
}
|
||||
|
||||
void InfoVisitor::visit(Scene &n)
|
||||
{
|
||||
}
|
||||
|
||||
void InfoVisitor::visit(Primitive &n)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void InfoVisitor::visit(MediaPlayer &mp)
|
||||
{
|
||||
if (current_id_ == mp.id())
|
||||
return;
|
||||
|
||||
Log::Info("Getting string info MediaPlayer %ld", current_id_);
|
||||
|
||||
std::ostringstream oss;
|
||||
if (brief_) {
|
||||
oss << SystemToolkit::filename(mp.filename()) << std::endl;
|
||||
oss << mp.width() << " x " << mp.height() << ", ";
|
||||
oss << mp.media().codec_name.substr(0, mp.media().codec_name.find_first_of(','));
|
||||
if (!mp.isImage())
|
||||
oss << ", " << std::fixed << std::setprecision(1) << mp.frameRate() << " fps";
|
||||
}
|
||||
else {
|
||||
oss << mp.filename() << std::endl;
|
||||
oss << mp.media().codec_name << std::endl;
|
||||
oss << mp.width() << " x " << mp.height() ;
|
||||
if (!mp.isImage())
|
||||
oss << ", " << std::fixed << std::setprecision(1) << mp.frameRate() << " fps";
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = mp.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit(Stream &n)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
if (brief_) {
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
information_ = oss.str();
|
||||
}
|
||||
|
||||
|
||||
void InfoVisitor::visit (MediaSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
s.mediaplayer()->accept(*this);
|
||||
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (SessionFileSource& s)
|
||||
{
|
||||
if (current_id_ == s.id() || s.session() == nullptr)
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
if (brief_) {
|
||||
oss << SystemToolkit::filename(s.path()) << " (";
|
||||
oss << s.session()->numSource() << " sources)" << std::endl;
|
||||
oss << s.session()->frame()->width() << " x " << s.session()->frame()->height() << ", ";
|
||||
oss << "RGB";
|
||||
}
|
||||
else {
|
||||
oss << s.path() << std::endl;
|
||||
oss << s.session()->frame()->width() << " x " << s.session()->frame()->height() << ", ";
|
||||
oss << "RGB";
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (SessionGroupSource& s)
|
||||
{
|
||||
if (current_id_ == s.id() || s.session() == nullptr)
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
if (brief_) {
|
||||
oss << s.session()->numSource() << " sources in group" << std::endl;
|
||||
oss << s.session()->frame()->width() << " x " << s.session()->frame()->height() << ", ";
|
||||
oss << "RGB";
|
||||
}
|
||||
else {
|
||||
oss << s.session()->numSource() << " sources in group" << std::endl;
|
||||
oss << s.session()->frame()->width() << " x " << s.session()->frame()->height() << ", ";
|
||||
oss << "RGB";
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (RenderSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
information_ = "Rendering Output";
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (CloneSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
information_ = "Clone of " + s.origin()->name();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (PatternSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
if (brief_) {
|
||||
oss << s.pattern()->width() << " x " << s.pattern()->height();
|
||||
oss << ", RGB";
|
||||
}
|
||||
else {
|
||||
oss << Pattern::pattern_types[s.pattern()->type()] << std::endl;
|
||||
oss << s.pattern()->width() << " x " << s.pattern()->height();
|
||||
oss << ", RGB";
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (DeviceSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
|
||||
DeviceConfigSet confs = Device::manager().config( Device::manager().index(s.device().c_str()));
|
||||
if ( !confs.empty()) {
|
||||
DeviceConfig best = *confs.rbegin();
|
||||
float fps = static_cast<float>(best.fps_numerator) / static_cast<float>(best.fps_denominator);
|
||||
|
||||
if (brief_) {
|
||||
oss << best.width << " x " << best.height << ", ";
|
||||
oss << best.stream << " " << best.format << ", ";
|
||||
oss << std::fixed << std::setprecision(1) << fps << " fps";
|
||||
}
|
||||
else {
|
||||
oss << s.device() << std::endl;
|
||||
oss << best.width << " x " << best.height << ", ";
|
||||
oss << best.stream << " " << best.format << ", ";
|
||||
oss << std::fixed << std::setprecision(1) << fps << " fps";
|
||||
}
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
void InfoVisitor::visit (NetworkSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
NetworkStream *ns = s.networkStream();
|
||||
|
||||
std::ostringstream oss;
|
||||
if (brief_) {
|
||||
oss << ns->resolution().x << " x " << ns->resolution().y << ", ";
|
||||
oss << NetworkToolkit::protocol_name[ns->protocol()] << std::endl;
|
||||
oss << "IP " << ns->serverAddress();
|
||||
}
|
||||
else {
|
||||
oss << s.connection() << " (IP " << ns->serverAddress() << ")" << std::endl;
|
||||
oss << ns->resolution().x << " x " << ns->resolution().y << ", ";
|
||||
oss << NetworkToolkit::protocol_name[ns->protocol()];
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
|
||||
void InfoVisitor::visit (MultiFileSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
if (brief_) {
|
||||
oss << s.sequence().width << " x " << s.sequence().height << ", ";
|
||||
oss << s.sequence().codec << std::endl;
|
||||
oss << s.sequence().max - s.sequence().min + 1 << " images [";
|
||||
oss << s.sequence().min << " - " << s.sequence().max << "]";
|
||||
}
|
||||
else {
|
||||
oss << s.sequence().location << " [";
|
||||
oss << s.sequence().min << " - " << s.sequence().max << "]" << std::endl;
|
||||
oss << s.sequence().width << " x " << s.sequence().height << ", ";
|
||||
oss << s.sequence().codec << " (";
|
||||
oss << s.sequence().max - s.sequence().min + 1 << " images)";
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
40
InfoVisitor.h
Normal file
40
InfoVisitor.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef INFOVISITOR_H
|
||||
#define INFOVISITOR_H
|
||||
|
||||
#include "Visitor.h"
|
||||
|
||||
class InfoVisitor : public Visitor
|
||||
{
|
||||
std::string information_;
|
||||
bool brief_;
|
||||
uint64_t current_id_;
|
||||
|
||||
public:
|
||||
InfoVisitor();
|
||||
inline void setBriefStringMode () { brief_ = true; current_id_ = 0; }
|
||||
inline void setExtendedStringMode () { brief_ = false; current_id_ = 0; }
|
||||
inline void reset () { current_id_ = 0; }
|
||||
inline std::string str () const { return information_; }
|
||||
|
||||
// Elements of Scene
|
||||
void visit (Scene& n) override;
|
||||
void visit (Node& n) override;
|
||||
void visit (Group& n) override;
|
||||
void visit (Switch& n) override;
|
||||
void visit (Primitive& n) override;
|
||||
|
||||
// Elements with attributes
|
||||
void visit (Stream& n) override;
|
||||
void visit (MediaPlayer& n) override;
|
||||
void visit (MediaSource& s) override;
|
||||
void visit (SessionFileSource& s) override;
|
||||
void visit (SessionGroupSource& s) override;
|
||||
void visit (RenderSource& s) override;
|
||||
void visit (CloneSource& s) override;
|
||||
void visit (PatternSource& s) override;
|
||||
void visit (DeviceSource& s) override;
|
||||
void visit (NetworkSource& s) override;
|
||||
void visit (MultiFileSource& s) override;
|
||||
};
|
||||
|
||||
#endif // INFOVISITOR_H
|
||||
@@ -311,11 +311,9 @@ View::Cursor LayerView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair
|
||||
// apply change
|
||||
float d = setDepth( s, MAX( -dest_translation.x, 0.f) );
|
||||
|
||||
// store action in history
|
||||
std::ostringstream info;
|
||||
info << "Depth " << std::fixed << std::setprecision(2) << d << " ";
|
||||
// info << (s->locked() ? ICON_FA_LOCK : ICON_FA_LOCK_OPEN); // TODO static not locked
|
||||
|
||||
// store action in history
|
||||
current_action_ = s->name() + ": " + info.str();
|
||||
|
||||
return Cursor(Cursor_ResizeNESW, info.str() );
|
||||
|
||||
@@ -166,7 +166,8 @@ MediaInfo MediaPlayer::UriDiscoverer(const std::string &uri)
|
||||
video_stream_info.framerate_n = gst_discoverer_video_info_get_framerate_num(vinfo);
|
||||
video_stream_info.framerate_d = gst_discoverer_video_info_get_framerate_denom(vinfo);
|
||||
if (video_stream_info.framerate_n == 0 || video_stream_info.framerate_d == 0) {
|
||||
video_stream_info.framerate_n = 25;
|
||||
Log::Info("'%s': No framerate indicated in the file; using default 30fps", uri.c_str());
|
||||
video_stream_info.framerate_n = 30;
|
||||
video_stream_info.framerate_d = 1;
|
||||
}
|
||||
video_stream_info.dt = ( (GST_SECOND * static_cast<guint64>(video_stream_info.framerate_d)) / (static_cast<guint64>(video_stream_info.framerate_n)) );
|
||||
@@ -186,7 +187,7 @@ MediaInfo MediaPlayer::UriDiscoverer(const std::string &uri)
|
||||
if ( tags ) {
|
||||
gchar *container = NULL;
|
||||
if ( gst_tag_list_get_string (tags, GST_TAG_CONTAINER_FORMAT, &container) )
|
||||
video_stream_info.codec_name += " " + std::string(container);
|
||||
video_stream_info.codec_name += ", " + std::string(container);
|
||||
if (container)
|
||||
g_free(container);
|
||||
}
|
||||
@@ -543,7 +544,7 @@ bool MediaPlayer::isImage() const
|
||||
return media_.isimage;
|
||||
}
|
||||
|
||||
std::string MediaPlayer::hardwareDecoderName()
|
||||
std::string MediaPlayer::hardwareDecoderName() const
|
||||
{
|
||||
return hardware_decoder_;
|
||||
}
|
||||
@@ -605,9 +606,6 @@ void MediaPlayer::play(bool on)
|
||||
Log::Info("MediaPlayer %s Stop [%ld]", std::to_string(id_).c_str(), position());
|
||||
#endif
|
||||
|
||||
// reset time counter
|
||||
timecount_.reset();
|
||||
|
||||
}
|
||||
|
||||
bool MediaPlayer::isPlaying(bool testpipeline) const
|
||||
@@ -679,7 +677,7 @@ bool MediaPlayer::go_to(GstClockTime pos)
|
||||
|
||||
GstClockTime jumpPts = pos;
|
||||
|
||||
if (timeline_.gapAt(pos, gap)) {
|
||||
if (timeline_.getGapAt(pos, gap)) {
|
||||
// if in a gap, find closest seek target
|
||||
if (gap.is_valid()) {
|
||||
// jump in one or the other direction
|
||||
@@ -925,7 +923,7 @@ void MediaPlayer::update()
|
||||
else {
|
||||
// manage timeline: test if position falls into a gap
|
||||
TimeInterval gap;
|
||||
if (position_ != GST_CLOCK_TIME_NONE && timeline_.gapAt(position_, gap)) {
|
||||
if (position_ != GST_CLOCK_TIME_NONE && timeline_.getGapAt(position_, gap)) {
|
||||
// if in a gap, seek to next section
|
||||
if (gap.is_valid()) {
|
||||
// jump in one or the other direction
|
||||
@@ -944,6 +942,7 @@ void MediaPlayer::update()
|
||||
if (need_loop) {
|
||||
execute_loop_command();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MediaPlayer::execute_loop_command()
|
||||
@@ -1233,54 +1232,24 @@ GstFlowReturn MediaPlayer::callback_new_sample (GstAppSink *sink, gpointer p)
|
||||
|
||||
|
||||
|
||||
MediaPlayer::TimeCounter::TimeCounter() {
|
||||
MediaPlayer::TimeCounter::TimeCounter()
|
||||
{
|
||||
timer = g_timer_new ();
|
||||
}
|
||||
|
||||
reset();
|
||||
MediaPlayer::TimeCounter::~TimeCounter()
|
||||
{
|
||||
g_free(timer);
|
||||
}
|
||||
|
||||
void MediaPlayer::TimeCounter::tic ()
|
||||
{
|
||||
// how long since last time
|
||||
GstClockTime t = gst_util_get_timestamp ();
|
||||
GstClockTime dt = t - last_time;
|
||||
|
||||
// one more frame since last time
|
||||
nbFrames++;
|
||||
double dt = g_timer_elapsed (timer, NULL) * 1000.0;
|
||||
g_timer_start(timer);
|
||||
|
||||
// calculate instantaneous framerate
|
||||
// Exponential moving averate with previous framerate to filter jitter (50/50)
|
||||
// The divition of frame/time is done on long integer GstClockTime, counting in microsecond
|
||||
// NB: factor 100 to get 0.01 precision
|
||||
fps = 0.5 * fps + 0.005 * static_cast<double>( ( 100 * GST_SECOND * nbFrames ) / dt );
|
||||
|
||||
// reset counter every second
|
||||
if ( dt >= GST_SECOND)
|
||||
{
|
||||
last_time = t;
|
||||
nbFrames = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GstClockTime MediaPlayer::TimeCounter::dt ()
|
||||
{
|
||||
GstClockTime t = gst_util_get_timestamp ();
|
||||
GstClockTime dt = t - tic_time;
|
||||
tic_time = t;
|
||||
|
||||
// return the instantaneous delta t
|
||||
return dt;
|
||||
}
|
||||
|
||||
void MediaPlayer::TimeCounter::reset ()
|
||||
{
|
||||
last_time = gst_util_get_timestamp ();;
|
||||
tic_time = last_time;
|
||||
nbFrames = 0;
|
||||
fps = 0.0;
|
||||
}
|
||||
|
||||
double MediaPlayer::TimeCounter::frameRate() const
|
||||
{
|
||||
return fps;
|
||||
// Exponential moving averate with previous framerate to filter jitter
|
||||
if (dt > 1.0)
|
||||
fps = CLAMP( 0.5 * fps + 500.0 / dt, 0.0, 1000.0) ;
|
||||
}
|
||||
|
||||
|
||||
@@ -188,6 +188,10 @@ public:
|
||||
* Seek to zero
|
||||
* */
|
||||
void rewind();
|
||||
/**
|
||||
* Get position time
|
||||
* */
|
||||
GstClockTime position();
|
||||
/**
|
||||
* go to a valid position in media timeline
|
||||
* pos in nanoseconds.
|
||||
@@ -210,10 +214,6 @@ public:
|
||||
void setTimeline(const Timeline &tl);
|
||||
|
||||
float currentTimelineFading();
|
||||
/**
|
||||
* Get position time
|
||||
* */
|
||||
GstClockTime position();
|
||||
/**
|
||||
* Get framerate of the media
|
||||
* */
|
||||
@@ -245,7 +245,7 @@ public:
|
||||
* Get the name of the hardware decoder used
|
||||
* Empty string if none (i.e. software decoding)
|
||||
* */
|
||||
std::string hardwareDecoderName();
|
||||
std::string hardwareDecoderName() const;
|
||||
/**
|
||||
* Forces open using software decoding
|
||||
* (i.e. without hadrware decoding)
|
||||
@@ -296,17 +296,13 @@ private:
|
||||
|
||||
// fps counter
|
||||
struct TimeCounter {
|
||||
|
||||
GstClockTime last_time;
|
||||
GstClockTime tic_time;
|
||||
int nbFrames;
|
||||
GTimer *timer;
|
||||
gdouble fps;
|
||||
public:
|
||||
TimeCounter();
|
||||
GstClockTime dt();
|
||||
~TimeCounter();
|
||||
void tic();
|
||||
void reset();
|
||||
gdouble frameRate() const;
|
||||
inline gdouble frameRate() const { return fps; }
|
||||
};
|
||||
TimeCounter timecount_;
|
||||
|
||||
|
||||
@@ -108,12 +108,38 @@ void MediaSource::setActive (bool on)
|
||||
{
|
||||
bool was_active = active_;
|
||||
|
||||
// try to activate (may fail if source is cloned)
|
||||
Source::setActive(on);
|
||||
|
||||
// change status of media player (only if status changed)
|
||||
if ( active_ != was_active ) {
|
||||
if ( active_ != was_active )
|
||||
mediaplayer_->enable(active_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool MediaSource::playing () const
|
||||
{
|
||||
return mediaplayer_->isPlaying();
|
||||
}
|
||||
|
||||
void MediaSource::play (bool on)
|
||||
{
|
||||
mediaplayer_->play(on);
|
||||
}
|
||||
|
||||
bool MediaSource::playable () const
|
||||
{
|
||||
return !mediaplayer_->isImage();
|
||||
}
|
||||
|
||||
void MediaSource::replay ()
|
||||
{
|
||||
mediaplayer_->rewind();
|
||||
}
|
||||
|
||||
guint64 MediaSource::playtime () const
|
||||
{
|
||||
return mediaplayer_->position();
|
||||
}
|
||||
|
||||
void MediaSource::update(float dt)
|
||||
|
||||
@@ -14,6 +14,11 @@ public:
|
||||
// implementation of source API
|
||||
void update (float dt) override;
|
||||
void setActive (bool on) override;
|
||||
bool playing () const override;
|
||||
void play (bool) override;
|
||||
bool playable () const override;
|
||||
void replay () override;
|
||||
guint64 playtime () const override;
|
||||
void render() override;
|
||||
bool failed() const override;
|
||||
uint texture() const override;
|
||||
|
||||
13
Mixer.cpp
13
Mixer.cpp
@@ -400,7 +400,7 @@ void Mixer::insertSource(Source *s, View::Mode m)
|
||||
attach(s);
|
||||
|
||||
// new state in history manager
|
||||
Action::manager().store(s->name() + std::string(" source inserted"));
|
||||
Action::manager().store(s->name() + std::string(": source inserted"));
|
||||
|
||||
// if requested to show the source in a given view
|
||||
// (known to work for View::MIXING et TRANSITION: other views untested)
|
||||
@@ -770,6 +770,17 @@ SourceList Mixer::findSources (float depth_from, float depth_to)
|
||||
return found;
|
||||
}
|
||||
|
||||
SourceList Mixer::validate (const SourceList &list)
|
||||
{
|
||||
SourceList sl;
|
||||
for( auto sit = list.begin(); sit != list.end(); ++sit) {
|
||||
SourceList::iterator it = session_->find( *sit );
|
||||
if (it != session_->end())
|
||||
sl.push_back(*sit);
|
||||
}
|
||||
return sl;
|
||||
}
|
||||
|
||||
void Mixer::setCurrentSource(uint64_t id)
|
||||
{
|
||||
setCurrentSource( session_->find(id) );
|
||||
|
||||
3
Mixer.h
3
Mixer.h
@@ -40,7 +40,7 @@ public:
|
||||
|
||||
// update session and all views
|
||||
void update();
|
||||
inline float dt() const { return dt_;}
|
||||
inline float dt() const { return dt_;} // in miliseconds
|
||||
inline int fps() const { return int(roundf(1000.f/dt__));}
|
||||
|
||||
// draw session and current view
|
||||
@@ -86,6 +86,7 @@ public:
|
||||
Source * findSource (std::string name);
|
||||
Source * findSource (uint64_t id);
|
||||
SourceList findSources (float depth_from, float depth_to);
|
||||
SourceList validate(const SourceList &list);
|
||||
|
||||
// management of view
|
||||
View *view (View::Mode m = View::INVALID);
|
||||
|
||||
@@ -115,6 +115,22 @@ void MultiFile::close ()
|
||||
Stream::close();
|
||||
}
|
||||
|
||||
void MultiFile::setIndex(int val)
|
||||
{
|
||||
if (src_) {
|
||||
g_object_set (src_, "index", val, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int MultiFile::index()
|
||||
{
|
||||
int val = 0;
|
||||
if (src_) {
|
||||
g_object_get (src_, "index", &val, NULL);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
void MultiFile::setProperties (int begin, int end, int loop)
|
||||
{
|
||||
if (src_) {
|
||||
@@ -187,6 +203,27 @@ void MultiFileSource::setRange (int begin, int end)
|
||||
multifile()->setProperties (begin_, end_, loop_);
|
||||
}
|
||||
|
||||
void MultiFileSource::replay ()
|
||||
{
|
||||
if (multifile()) {
|
||||
multifile()->setIndex (begin_);
|
||||
stream_->rewind();
|
||||
}
|
||||
}
|
||||
|
||||
guint64 MultiFileSource::playtime () const
|
||||
{
|
||||
guint64 time = 0;
|
||||
|
||||
if (multifile())
|
||||
time += multifile()->index();
|
||||
|
||||
time *= GST_SECOND;
|
||||
time /= framerate_;
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
void MultiFileSource::accept (Visitor& v)
|
||||
{
|
||||
Source::accept(v);
|
||||
|
||||
@@ -31,6 +31,10 @@ public:
|
||||
// dynamic change of gstreamer multifile source properties
|
||||
void setProperties(int begin, int end, int loop);
|
||||
|
||||
// image index
|
||||
int index();
|
||||
void setIndex(int val);
|
||||
|
||||
protected:
|
||||
GstElement *src_ ;
|
||||
};
|
||||
@@ -42,6 +46,8 @@ public:
|
||||
|
||||
// Source interface
|
||||
void accept (Visitor& v) override;
|
||||
void replay () override;
|
||||
guint64 playtime () const override;
|
||||
|
||||
// StreamSource interface
|
||||
Stream *stream () const override { return stream_; }
|
||||
|
||||
@@ -126,6 +126,7 @@ void Pattern::open( uint pattern, glm::ivec2 res )
|
||||
|
||||
// all patterns before 'SMPTE test pattern' are single frames (not animated)
|
||||
single_frame_ = type_ < 14;
|
||||
Log::Info("Stream %d SingleFrame", single_frame_);
|
||||
|
||||
// (private) open stream
|
||||
Stream::open(gstreamer_pattern, res.x, res.y);
|
||||
|
||||
@@ -143,7 +143,7 @@ const std::vector<std::string> VideoRecorder::profile_description {
|
||||
// fast (5)
|
||||
#ifndef APPLE
|
||||
// "video/x-raw, format=I420 ! x264enc pass=4 quantizer=26 speed-preset=3 threads=4 ! video/x-h264, profile=baseline ! h264parse ! ",
|
||||
"video/x-raw, format=I420 ! x264enc tune=\"zerolatency\" threads=4 ! video/x-h264, profile=baseline ! h264parse ! ",
|
||||
"video/x-raw, format=I420 ! x264enc tune=\"zerolatency\" pass=4 threads=4 ! video/x-h264, profile=baseline ! h264parse ! ",
|
||||
#else
|
||||
"video/x-raw, format=I420 ! vtenc_h264_hw realtime=1 allow-frame-reordering=0 ! h264parse ! ",
|
||||
#endif
|
||||
|
||||
53
Session.cpp
53
Session.cpp
@@ -459,6 +459,59 @@ std::list<MixingGroup *>::iterator Session::endMixingGroup()
|
||||
return mixing_groups_.end();
|
||||
}
|
||||
|
||||
|
||||
size_t Session::numPlayGroups() const
|
||||
{
|
||||
return play_groups_.size();
|
||||
}
|
||||
|
||||
void Session::addPlayGroup(const SourceIdList &ids)
|
||||
{
|
||||
play_groups_.push_back( ids );
|
||||
}
|
||||
|
||||
void Session::addToPlayGroup(size_t i, Source *s)
|
||||
{
|
||||
if (i < play_groups_.size() )
|
||||
{
|
||||
if ( std::find(play_groups_[i].begin(), play_groups_[i].end(), s->id()) == play_groups_[i].end() )
|
||||
play_groups_[i].push_back(s->id());
|
||||
}
|
||||
}
|
||||
|
||||
void Session::removeFromPlayGroup(size_t i, Source *s)
|
||||
{
|
||||
if (i < play_groups_.size() )
|
||||
{
|
||||
if ( std::find(play_groups_[i].begin(), play_groups_[i].end(), s->id()) != play_groups_[i].end() )
|
||||
play_groups_[i].remove( s->id() );
|
||||
}
|
||||
}
|
||||
|
||||
void Session::deletePlayGroup(size_t i)
|
||||
{
|
||||
if (i < play_groups_.size() )
|
||||
play_groups_.erase( play_groups_.begin() + i);
|
||||
}
|
||||
|
||||
SourceList Session::playGroup(size_t i) const
|
||||
{
|
||||
SourceList list;
|
||||
|
||||
if (i < play_groups_.size() )
|
||||
{
|
||||
for (auto sid = play_groups_[i].begin(); sid != play_groups_[i].end(); ++sid){
|
||||
|
||||
SourceList::const_iterator it = std::find_if(sources_.begin(), sources_.end(), Source::hasId( *sid));;
|
||||
if ( it != sources_.end())
|
||||
list.push_back( *it);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void Session::lock()
|
||||
{
|
||||
access_.lock();
|
||||
|
||||
10
Session.h
10
Session.h
@@ -126,6 +126,15 @@ public:
|
||||
// snapshots
|
||||
SessionSnapshots * const snapshots () { return &snapshots_; }
|
||||
|
||||
// playlists
|
||||
void addPlayGroup(const SourceIdList &ids);
|
||||
void deletePlayGroup(size_t i);
|
||||
size_t numPlayGroups() const;
|
||||
SourceList playGroup(size_t i) const;
|
||||
void addToPlayGroup(size_t i, Source *s);
|
||||
void removeFromPlayGroup(size_t i, Source *s);
|
||||
std::vector<SourceIdList> getPlayGroups() { return play_groups_; }
|
||||
|
||||
// lock and unlock access (e.g. while saving)
|
||||
void lock ();
|
||||
void unlock ();
|
||||
@@ -141,6 +150,7 @@ protected:
|
||||
std::list<MixingGroup *> mixing_groups_;
|
||||
std::map<View::Mode, Group*> config_;
|
||||
SessionSnapshots snapshots_;
|
||||
std::vector<SourceIdList> play_groups_;
|
||||
float fading_target_;
|
||||
std::mutex access_;
|
||||
|
||||
|
||||
@@ -112,6 +112,9 @@ void SessionCreator::load(const std::string& filename)
|
||||
// load notes
|
||||
loadNotes( xmlDoc_.FirstChildElement("Notes") );
|
||||
|
||||
// load playlists
|
||||
loadPlayGroups( xmlDoc_.FirstChildElement("PlayGroups") );
|
||||
|
||||
// all good
|
||||
session_->setFilename(filename);
|
||||
}
|
||||
@@ -163,7 +166,8 @@ void SessionCreator::loadNotes(XMLElement *notesNode)
|
||||
XMLElement *sizeNode = note->FirstChildElement("size");
|
||||
if (sizeNode) tinyxml2::XMLElementToGLM( sizeNode->FirstChildElement("vec2"), N.size);
|
||||
XMLElement* contentNode = note->FirstChildElement("text");
|
||||
if (contentNode) N.text = std::string ( contentNode->GetText() );
|
||||
if (contentNode && contentNode->GetText())
|
||||
N.text = std::string ( contentNode->GetText() );
|
||||
|
||||
session_->addNote(N);
|
||||
}
|
||||
@@ -171,6 +175,29 @@ void SessionCreator::loadNotes(XMLElement *notesNode)
|
||||
}
|
||||
}
|
||||
|
||||
void SessionCreator::loadPlayGroups(tinyxml2::XMLElement *playgroupNode)
|
||||
{
|
||||
if (playgroupNode != nullptr && session_ != nullptr) {
|
||||
|
||||
XMLElement* playgroup = playgroupNode->FirstChildElement("PlayGroup");
|
||||
for( ; playgroup ; playgroup = playgroup->NextSiblingElement())
|
||||
{
|
||||
SourceIdList playgroup_sources;
|
||||
|
||||
XMLElement* playgroupSourceNode = playgroup->FirstChildElement("source");
|
||||
for ( ; playgroupSourceNode ; playgroupSourceNode = playgroupSourceNode->NextSiblingElement()) {
|
||||
uint64_t id__ = 0;
|
||||
playgroupSourceNode->QueryUnsigned64Attribute("id", &id__);
|
||||
|
||||
if (sources_id_.count(id__) > 0)
|
||||
playgroup_sources.push_back( id__ );
|
||||
|
||||
}
|
||||
session_->addPlayGroup( playgroup_sources );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SessionLoader::SessionLoader(): Visitor(),
|
||||
session_(nullptr), xmlCurrent_(nullptr), recursion_(0)
|
||||
{
|
||||
|
||||
@@ -93,6 +93,7 @@ class SessionCreator : public SessionLoader {
|
||||
|
||||
void loadConfig(tinyxml2::XMLElement *viewsNode);
|
||||
void loadNotes(tinyxml2::XMLElement *notesNode);
|
||||
void loadPlayGroups(tinyxml2::XMLElement *playlistsNode);
|
||||
void loadSnapshots(tinyxml2::XMLElement *snapshotNode);
|
||||
|
||||
public:
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "Mixer.h"
|
||||
|
||||
|
||||
SessionSource::SessionSource(uint64_t id) : Source(id), failed_(false)
|
||||
SessionSource::SessionSource(uint64_t id) : Source(id), failed_(false), timer_(0), paused_(false)
|
||||
{
|
||||
session_ = new Session;
|
||||
}
|
||||
@@ -62,11 +62,11 @@ uint SessionSource::texture() const
|
||||
}
|
||||
|
||||
void SessionSource::setActive (bool on)
|
||||
{
|
||||
{
|
||||
Source::setActive(on);
|
||||
|
||||
// change status of session (recursive change of internal sources)
|
||||
if (session_ != nullptr)
|
||||
if (session_)
|
||||
session_->setActive(active_);
|
||||
}
|
||||
|
||||
@@ -76,8 +76,10 @@ void SessionSource::update(float dt)
|
||||
return;
|
||||
|
||||
// update content
|
||||
if (active_)
|
||||
if (active_ && !paused_) {
|
||||
session_->update(dt);
|
||||
timer_ += guint64(dt * 1000.f) * GST_USECOND;
|
||||
}
|
||||
|
||||
// delete a source which failed
|
||||
if (session_->failedSource() != nullptr) {
|
||||
@@ -90,6 +92,15 @@ void SessionSource::update(float dt)
|
||||
Source::update(dt);
|
||||
}
|
||||
|
||||
void SessionSource::replay ()
|
||||
{
|
||||
if (session_) {
|
||||
for( SourceList::iterator it = session_->begin(); it != session_->end(); ++it)
|
||||
(*it)->replay();
|
||||
timer_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SessionFileSource::SessionFileSource(uint64_t id) : SessionSource(id), path_(""), initialized_(false), wait_for_sources_(false)
|
||||
{
|
||||
@@ -150,6 +161,7 @@ void SessionFileSource::load(const std::string &p, uint recursion)
|
||||
}
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
initialized_ = false;
|
||||
ready_ = false;
|
||||
}
|
||||
|
||||
@@ -332,7 +344,7 @@ RenderSource::RenderSource(uint64_t id) : Source(id), session_(nullptr)
|
||||
|
||||
bool RenderSource::failed() const
|
||||
{
|
||||
if ( mode_ > Source::UNINITIALIZED && session_!=nullptr )
|
||||
if ( renderbuffer_ != nullptr && session_ != nullptr )
|
||||
return renderbuffer_->resolution() != session_->frame()->resolution();
|
||||
|
||||
return false;
|
||||
@@ -372,7 +384,7 @@ void RenderSource::init()
|
||||
|
||||
glm::vec3 RenderSource::resolution() const
|
||||
{
|
||||
if (mode_ > Source::UNINITIALIZED)
|
||||
if (renderbuffer_ != nullptr)
|
||||
return renderbuffer_->resolution();
|
||||
else if (session_ && session_->frame())
|
||||
return session_->frame()->resolution();
|
||||
|
||||
@@ -14,6 +14,11 @@ public:
|
||||
// implementation of source API
|
||||
void update (float dt) override;
|
||||
void setActive (bool on) override;
|
||||
bool playing () const override { return !paused_; }
|
||||
void play (bool on) override { paused_ = !on; }
|
||||
bool playable () const override { return true; }
|
||||
guint64 playtime () const override { return timer_; }
|
||||
void replay () override;
|
||||
bool failed () const override;
|
||||
uint texture () const override;
|
||||
|
||||
@@ -24,6 +29,8 @@ protected:
|
||||
|
||||
Session *session_;
|
||||
std::atomic<bool> failed_;
|
||||
guint64 timer_;
|
||||
bool paused_;
|
||||
};
|
||||
|
||||
class SessionFileSource : public SessionSource
|
||||
@@ -80,6 +87,9 @@ public:
|
||||
RenderSource(uint64_t id = 0);
|
||||
|
||||
// implementation of source API
|
||||
bool playing () const override { return true; }
|
||||
void play (bool) override {}
|
||||
bool playable () const override { return false; }
|
||||
bool failed () const override;
|
||||
uint texture() const override;
|
||||
void accept (Visitor& v) override;
|
||||
|
||||
@@ -58,63 +58,109 @@ bool SessionVisitor::saveSession(const std::string& filename, Session *session)
|
||||
delete thumbnail;
|
||||
|
||||
// 2. config of views
|
||||
XMLElement *views = xmlDoc.NewElement("Views");
|
||||
xmlDoc.InsertEndChild(views);
|
||||
{
|
||||
XMLElement *mixing = xmlDoc.NewElement( "Mixing" );
|
||||
mixing->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::MIXING), &xmlDoc));
|
||||
views->InsertEndChild(mixing);
|
||||
|
||||
XMLElement *geometry = xmlDoc.NewElement( "Geometry" );
|
||||
geometry->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::GEOMETRY), &xmlDoc));
|
||||
views->InsertEndChild(geometry);
|
||||
|
||||
XMLElement *layer = xmlDoc.NewElement( "Layer" );
|
||||
layer->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::LAYER), &xmlDoc));
|
||||
views->InsertEndChild(layer);
|
||||
|
||||
XMLElement *appearance = xmlDoc.NewElement( "Texture" );
|
||||
appearance->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::TEXTURE), &xmlDoc));
|
||||
views->InsertEndChild(appearance);
|
||||
|
||||
XMLElement *render = xmlDoc.NewElement( "Rendering" );
|
||||
render->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::RENDERING), &xmlDoc));
|
||||
views->InsertEndChild(render);
|
||||
}
|
||||
saveConfig( &xmlDoc, session );
|
||||
|
||||
// 3. snapshots
|
||||
XMLElement *snapshots = xmlDoc.NewElement("Snapshots");
|
||||
const XMLElement* N = session->snapshots()->xmlDoc_->FirstChildElement();
|
||||
for( ; N ; N=N->NextSiblingElement())
|
||||
snapshots->InsertEndChild( N->DeepClone( &xmlDoc ));
|
||||
xmlDoc.InsertEndChild(snapshots);
|
||||
saveSnapshots( &xmlDoc, session );
|
||||
|
||||
// 4. optional notes
|
||||
XMLElement *notes = xmlDoc.NewElement("Notes");
|
||||
xmlDoc.InsertEndChild(notes);
|
||||
for (auto nit = session->beginNotes(); nit != session->endNotes(); ++nit) {
|
||||
XMLElement *note = xmlDoc.NewElement( "Note" );
|
||||
note->SetAttribute("large", (*nit).large );
|
||||
note->SetAttribute("stick", (*nit).stick );
|
||||
XMLElement *pos = xmlDoc.NewElement("pos");
|
||||
pos->InsertEndChild( XMLElementFromGLM(&xmlDoc, (*nit).pos) );
|
||||
note->InsertEndChild(pos);
|
||||
XMLElement *size = xmlDoc.NewElement("size");
|
||||
size->InsertEndChild( XMLElementFromGLM(&xmlDoc, (*nit).size) );
|
||||
note->InsertEndChild(size);
|
||||
XMLElement *content = xmlDoc.NewElement("text");
|
||||
XMLText *text = xmlDoc.NewText( (*nit).text.c_str() );
|
||||
content->InsertEndChild( text );
|
||||
note->InsertEndChild(content);
|
||||
|
||||
notes->InsertEndChild(note);
|
||||
}
|
||||
saveNotes( &xmlDoc, session );
|
||||
|
||||
// 5. optional playlists
|
||||
savePlayGroups( &xmlDoc, session );
|
||||
|
||||
// save file to disk
|
||||
return ( XMLSaveDoc(&xmlDoc, filename) );
|
||||
}
|
||||
|
||||
void SessionVisitor::saveConfig(tinyxml2::XMLDocument *doc, Session *session)
|
||||
{
|
||||
if (doc != nullptr && session != nullptr)
|
||||
{
|
||||
XMLElement *views = doc->NewElement("Views");
|
||||
|
||||
XMLElement *mixing = doc->NewElement( "Mixing" );
|
||||
mixing->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::MIXING), doc));
|
||||
views->InsertEndChild(mixing);
|
||||
|
||||
XMLElement *geometry = doc->NewElement( "Geometry" );
|
||||
geometry->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::GEOMETRY), doc));
|
||||
views->InsertEndChild(geometry);
|
||||
|
||||
XMLElement *layer = doc->NewElement( "Layer" );
|
||||
layer->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::LAYER), doc));
|
||||
views->InsertEndChild(layer);
|
||||
|
||||
XMLElement *appearance = doc->NewElement( "Texture" );
|
||||
appearance->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::TEXTURE), doc));
|
||||
views->InsertEndChild(appearance);
|
||||
|
||||
XMLElement *render = doc->NewElement( "Rendering" );
|
||||
render->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::RENDERING), doc));
|
||||
views->InsertEndChild(render);
|
||||
|
||||
doc->InsertEndChild(views);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SessionVisitor::saveSnapshots(tinyxml2::XMLDocument *doc, Session *session)
|
||||
{
|
||||
if (doc != nullptr && session != nullptr)
|
||||
{
|
||||
XMLElement *snapshots = doc->NewElement("Snapshots");
|
||||
const XMLElement* N = session->snapshots()->xmlDoc_->FirstChildElement();
|
||||
for( ; N ; N=N->NextSiblingElement())
|
||||
snapshots->InsertEndChild( N->DeepClone( doc ));
|
||||
doc->InsertEndChild(snapshots);
|
||||
}
|
||||
}
|
||||
|
||||
void SessionVisitor::saveNotes(tinyxml2::XMLDocument *doc, Session *session)
|
||||
{
|
||||
if (doc != nullptr && session != nullptr)
|
||||
{
|
||||
XMLElement *notes = doc->NewElement("Notes");
|
||||
for (auto nit = session->beginNotes(); nit != session->endNotes(); ++nit) {
|
||||
XMLElement *note = doc->NewElement( "Note" );
|
||||
note->SetAttribute("large", (*nit).large );
|
||||
note->SetAttribute("stick", (*nit).stick );
|
||||
XMLElement *pos = doc->NewElement("pos");
|
||||
pos->InsertEndChild( XMLElementFromGLM(doc, (*nit).pos) );
|
||||
note->InsertEndChild(pos);
|
||||
XMLElement *size = doc->NewElement("size");
|
||||
size->InsertEndChild( XMLElementFromGLM(doc, (*nit).size) );
|
||||
note->InsertEndChild(size);
|
||||
XMLElement *content = doc->NewElement("text");
|
||||
XMLText *text = doc->NewText( (*nit).text.c_str() );
|
||||
content->InsertEndChild( text );
|
||||
note->InsertEndChild(content);
|
||||
|
||||
notes->InsertEndChild(note);
|
||||
}
|
||||
doc->InsertEndChild(notes);
|
||||
}
|
||||
}
|
||||
|
||||
void SessionVisitor::savePlayGroups(tinyxml2::XMLDocument *doc, Session *session)
|
||||
{
|
||||
if (doc != nullptr && session != nullptr)
|
||||
{
|
||||
XMLElement *playlistNode = doc->NewElement("PlayGroups");
|
||||
std::vector<SourceIdList> pl = session->getPlayGroups();
|
||||
for (auto plit = pl.begin(); plit != pl.end(); ++plit) {
|
||||
XMLElement *list = doc->NewElement("PlayGroup");
|
||||
playlistNode->InsertEndChild(list);
|
||||
for (auto id = plit->begin(); id != plit->end(); ++id) {
|
||||
XMLElement *sour = doc->NewElement("source");
|
||||
sour->SetAttribute("id", *id);
|
||||
list->InsertEndChild(sour);
|
||||
}
|
||||
}
|
||||
doc->InsertEndChild(playlistNode);
|
||||
}
|
||||
}
|
||||
|
||||
SessionVisitor::SessionVisitor(tinyxml2::XMLDocument *doc,
|
||||
tinyxml2::XMLElement *root,
|
||||
bool recursive) : Visitor(), recursive_(recursive), xmlCurrent_(root)
|
||||
|
||||
@@ -14,6 +14,11 @@ class SessionVisitor : public Visitor {
|
||||
tinyxml2::XMLDocument *xmlDoc_;
|
||||
tinyxml2::XMLElement *xmlCurrent_;
|
||||
|
||||
static void saveConfig(tinyxml2::XMLDocument *doc, Session *session);
|
||||
static void saveSnapshots(tinyxml2::XMLDocument *doc, Session *session);
|
||||
static void saveNotes(tinyxml2::XMLDocument *doc, Session *session);
|
||||
static void savePlayGroups(tinyxml2::XMLDocument *doc, Session *session);
|
||||
|
||||
public:
|
||||
SessionVisitor(tinyxml2::XMLDocument *doc = nullptr,
|
||||
tinyxml2::XMLElement *root = nullptr,
|
||||
|
||||
@@ -74,8 +74,11 @@ void Settings::Save()
|
||||
// Widgets
|
||||
XMLElement *widgetsNode = xmlDoc.NewElement( "Widgets" );
|
||||
widgetsNode->SetAttribute("preview", application.widget.preview);
|
||||
widgetsNode->SetAttribute("preview_view", application.widget.preview_view);
|
||||
widgetsNode->SetAttribute("history", application.widget.history);
|
||||
widgetsNode->SetAttribute("media_player", application.widget.media_player);
|
||||
widgetsNode->SetAttribute("media_player_view", application.widget.media_player_view);
|
||||
widgetsNode->SetAttribute("timeline_editmode", application.widget.timeline_editmode);
|
||||
widgetsNode->SetAttribute("shader_editor", application.widget.shader_editor);
|
||||
widgetsNode->SetAttribute("stats", application.widget.stats);
|
||||
widgetsNode->SetAttribute("stats_mode", application.widget.stats_mode);
|
||||
@@ -99,6 +102,7 @@ void Settings::Save()
|
||||
RecordNode->SetAttribute("path", application.record.path.c_str());
|
||||
RecordNode->SetAttribute("profile", application.record.profile);
|
||||
RecordNode->SetAttribute("timeout", application.record.timeout);
|
||||
RecordNode->SetAttribute("delay", application.record.delay);
|
||||
pRoot->InsertEndChild(RecordNode);
|
||||
|
||||
// Transition
|
||||
@@ -267,8 +271,11 @@ void Settings::Load()
|
||||
XMLElement * widgetsNode = pRoot->FirstChildElement("Widgets");
|
||||
if (widgetsNode != nullptr) {
|
||||
widgetsNode->QueryBoolAttribute("preview", &application.widget.preview);
|
||||
widgetsNode->QueryIntAttribute("preview_view", &application.widget.preview_view);
|
||||
widgetsNode->QueryBoolAttribute("history", &application.widget.history);
|
||||
widgetsNode->QueryBoolAttribute("media_player", &application.widget.media_player);
|
||||
widgetsNode->QueryIntAttribute("media_player_view", &application.widget.media_player_view);
|
||||
widgetsNode->QueryBoolAttribute("timeline_editmode", &application.widget.timeline_editmode);
|
||||
widgetsNode->QueryBoolAttribute("shader_editor", &application.widget.shader_editor);
|
||||
widgetsNode->QueryBoolAttribute("stats", &application.widget.stats);
|
||||
widgetsNode->QueryIntAttribute("stats_mode", &application.widget.stats_mode);
|
||||
@@ -293,6 +300,7 @@ void Settings::Load()
|
||||
if (recordnode != nullptr) {
|
||||
recordnode->QueryIntAttribute("profile", &application.record.profile);
|
||||
recordnode->QueryFloatAttribute("timeout", &application.record.timeout);
|
||||
recordnode->QueryIntAttribute("delay", &application.record.delay);
|
||||
|
||||
const char *path_ = recordnode->Attribute("path");
|
||||
if (path_)
|
||||
|
||||
12
Settings.h
12
Settings.h
@@ -18,11 +18,13 @@ struct WidgetsConfig
|
||||
int stats_mode;
|
||||
bool logs;
|
||||
bool preview;
|
||||
bool history;
|
||||
int preview_view;
|
||||
bool media_player;
|
||||
bool media_player_view;
|
||||
int media_player_view;
|
||||
bool timeline_editmode;
|
||||
bool shader_editor;
|
||||
bool toolbox;
|
||||
bool history;
|
||||
|
||||
WidgetsConfig() {
|
||||
stats = false;
|
||||
@@ -30,9 +32,11 @@ struct WidgetsConfig
|
||||
stats_corner = 1;
|
||||
logs = false;
|
||||
preview = false;
|
||||
preview_view = -1;
|
||||
history = false;
|
||||
media_player = false;
|
||||
media_player_view = true;
|
||||
media_player_view = -1;
|
||||
timeline_editmode = false;
|
||||
shader_editor = false;
|
||||
toolbox = false;
|
||||
}
|
||||
@@ -69,10 +73,12 @@ struct RecordConfig
|
||||
std::string path;
|
||||
int profile;
|
||||
float timeout;
|
||||
int delay;
|
||||
|
||||
RecordConfig() : path("") {
|
||||
profile = 0;
|
||||
timeout = RECORD_MAX_TIMEOUT;
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -425,7 +425,6 @@ void Source::render()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Source::attach(FrameBuffer *renderbuffer)
|
||||
{
|
||||
// invalid argument
|
||||
@@ -871,7 +870,6 @@ void CloneSource::setActive (bool on)
|
||||
origin_->touch();
|
||||
}
|
||||
|
||||
|
||||
uint CloneSource::texture() const
|
||||
{
|
||||
if (origin_ != nullptr)
|
||||
|
||||
11
Source.h
11
Source.h
@@ -139,6 +139,13 @@ public:
|
||||
} Workspace;
|
||||
inline Workspace workspace () const { return workspace_; }
|
||||
|
||||
// a Source shall define a way to play
|
||||
virtual bool playable () const = 0;
|
||||
virtual bool playing () const = 0;
|
||||
virtual void play (bool on) = 0;
|
||||
virtual void replay () {}
|
||||
virtual guint64 playtime () const { return 0; }
|
||||
|
||||
// a Source shall informs if the source failed (i.e. shall be deleted)
|
||||
virtual bool failed () const = 0;
|
||||
|
||||
@@ -299,6 +306,10 @@ public:
|
||||
|
||||
// implementation of source API
|
||||
void setActive (bool on) override;
|
||||
bool playing () const override { return true; }
|
||||
void play (bool) override {}
|
||||
bool playable () const override { return false; }
|
||||
void replay () override {}
|
||||
uint texture() const override;
|
||||
bool failed() const override { return origin_ == nullptr; }
|
||||
void accept (Visitor& v) override;
|
||||
|
||||
@@ -14,6 +14,18 @@ bool compare_depth (Source * first, Source * second)
|
||||
return ( first->depth() < second->depth() );
|
||||
}
|
||||
|
||||
|
||||
bool notplayable (const Source *s) { return !s->playable(); }
|
||||
|
||||
SourceList playable_only (const SourceList &list)
|
||||
{
|
||||
SourceList pl = list;
|
||||
|
||||
pl.remove_if(notplayable);
|
||||
|
||||
return pl;
|
||||
}
|
||||
|
||||
SourceList depth_sorted(const SourceList &list)
|
||||
{
|
||||
SourceList sl = list;
|
||||
|
||||
@@ -11,6 +11,7 @@ class Session;
|
||||
typedef std::list<Source *> SourceList;
|
||||
typedef std::list<SourceCore *> SourceCoreList;
|
||||
|
||||
SourceList playable_only (const SourceList &list);
|
||||
SourceList depth_sorted (const SourceList &list);
|
||||
SourceList mixing_sorted (const SourceList &list, glm::vec2 center = glm::vec2(0.f, 0.f));
|
||||
SourceList intersect (const SourceList &first, const SourceList &second);
|
||||
|
||||
163
Stream.cpp
163
Stream.cpp
@@ -31,6 +31,7 @@ Stream::Stream()
|
||||
opened_ = false;
|
||||
enabled_ = true;
|
||||
desired_state_ = GST_STATE_PAUSED;
|
||||
position_ = GST_CLOCK_TIME_NONE;
|
||||
|
||||
width_ = -1;
|
||||
height_ = -1;
|
||||
@@ -175,6 +176,7 @@ void Stream::execute_open()
|
||||
#endif
|
||||
|
||||
// set to desired state (PLAY or PAUSE)
|
||||
live_ = false;
|
||||
GstStateChangeReturn ret = gst_element_set_state (pipeline_, desired_state_);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
||||
Log::Warning("Stream %s Could not open '%s'", std::to_string(id_).c_str(), description_.c_str());
|
||||
@@ -218,15 +220,12 @@ void Stream::Frame::unmap()
|
||||
void Stream::close()
|
||||
{
|
||||
// not openned?
|
||||
if (!opened_) {
|
||||
// nothing else to change
|
||||
if (!opened_)
|
||||
return;
|
||||
}
|
||||
|
||||
// un-ready
|
||||
opened_ = false;
|
||||
single_frame_ = false;
|
||||
live_ = false;
|
||||
textureinitialized_ = false;
|
||||
|
||||
// clean up GST
|
||||
if (pipeline_ != nullptr) {
|
||||
@@ -350,9 +349,6 @@ void Stream::play(bool on)
|
||||
if (live_)
|
||||
gst_element_get_state (pipeline_, NULL, NULL, GST_CLOCK_TIME_NONE);
|
||||
|
||||
// reset time counter
|
||||
timecount_.reset();
|
||||
|
||||
}
|
||||
|
||||
bool Stream::isPlaying(bool testpipeline) const
|
||||
@@ -371,7 +367,26 @@ bool Stream::isPlaying(bool testpipeline) const
|
||||
return state == GST_STATE_PLAYING;
|
||||
}
|
||||
|
||||
void Stream::rewind()
|
||||
{
|
||||
if ( pipeline_ == nullptr )
|
||||
return;
|
||||
|
||||
GstEvent *seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
|
||||
GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_END, 0);
|
||||
gst_element_send_event(pipeline_, seek_event);
|
||||
}
|
||||
|
||||
GstClockTime Stream::position()
|
||||
{
|
||||
if (position_ == GST_CLOCK_TIME_NONE && pipeline_ != nullptr) {
|
||||
gint64 p = GST_CLOCK_TIME_NONE;
|
||||
if ( gst_element_query_position (pipeline_, GST_FORMAT_TIME, &p) )
|
||||
position_ = p;
|
||||
}
|
||||
|
||||
return position_;
|
||||
}
|
||||
|
||||
void Stream::init_texture(guint index)
|
||||
{
|
||||
@@ -443,46 +458,46 @@ void Stream::fill_texture(guint index)
|
||||
{
|
||||
// initialize texture
|
||||
init_texture(index);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textureindex_);
|
||||
|
||||
// use dual Pixel Buffer Object
|
||||
if (pbo_size_ > 0) {
|
||||
// In dual PBO mode, increment current index first then get the next index
|
||||
pbo_index_ = (pbo_index_ + 1) % 2;
|
||||
pbo_next_index_ = (pbo_index_ + 1) % 2;
|
||||
|
||||
// bind PBO to read pixels
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[pbo_index_]);
|
||||
// copy pixels from PBO to texture object
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
// bind the next PBO to write pixels
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[pbo_next_index_]);
|
||||
// See http://www.songho.ca/opengl/gl_pbo.html#map for more details
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW);
|
||||
// map the buffer object into client's memory
|
||||
GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
||||
if (ptr) {
|
||||
// update data directly on the mapped buffer
|
||||
// NB : equivalent but faster (memmove instead of memcpy ?) than
|
||||
// glNamedBufferSubData(pboIds[nextIndex], 0, imgsize, vp->getBuffer())
|
||||
memmove(ptr, frame_[index].vframe.data[0], pbo_size_);
|
||||
|
||||
// release pointer to mapping buffer
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
}
|
||||
// done with PBO
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
else {
|
||||
glBindTexture(GL_TEXTURE_2D, textureindex_);
|
||||
|
||||
// use dual Pixel Buffer Object
|
||||
if (pbo_size_ > 0) {
|
||||
// In dual PBO mode, increment current index first then get the next index
|
||||
pbo_index_ = (pbo_index_ + 1) % 2;
|
||||
pbo_next_index_ = (pbo_index_ + 1) % 2;
|
||||
|
||||
// bind PBO to read pixels
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[pbo_index_]);
|
||||
// copy pixels from PBO to texture object
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
// bind the next PBO to write pixels
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[pbo_next_index_]);
|
||||
// See http://www.songho.ca/opengl/gl_pbo.html#map for more details
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW);
|
||||
// map the buffer object into client's memory
|
||||
GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
||||
if (ptr) {
|
||||
// update data directly on the mapped buffer
|
||||
// NB : equivalent but faster (memmove instead of memcpy ?) than
|
||||
// glNamedBufferSubData(pboIds[nextIndex], 0, imgsize, vp->getBuffer())
|
||||
memmove(ptr, frame_[index].vframe.data[0], pbo_size_);
|
||||
|
||||
// release pointer to mapping buffer
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
}
|
||||
// done with PBO
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
else {
|
||||
// without PBO, use standard opengl (slower)
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
// without PBO, use standard opengl (slower)
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
}
|
||||
|
||||
void Stream::update()
|
||||
@@ -498,7 +513,7 @@ void Stream::update()
|
||||
}
|
||||
|
||||
// prevent unnecessary updates: already filled image
|
||||
if (single_frame_ && textureindex_>0)
|
||||
if (single_frame_ && textureinitialized_)
|
||||
return;
|
||||
|
||||
// local variables before trying to update
|
||||
@@ -545,6 +560,9 @@ void Stream::update()
|
||||
frame_[read_index].unmap();
|
||||
}
|
||||
|
||||
// we just displayed a vframe : set position time to frame PTS
|
||||
position_ = frame_[read_index].position;
|
||||
|
||||
// avoid reading it again
|
||||
frame_[read_index].status = INVALID;
|
||||
}
|
||||
@@ -716,54 +734,23 @@ GstFlowReturn Stream::callback_new_sample (GstAppSink *sink, gpointer p)
|
||||
|
||||
|
||||
|
||||
Stream::TimeCounter::TimeCounter() {
|
||||
Stream::TimeCounter::TimeCounter()
|
||||
{
|
||||
timer = g_timer_new ();
|
||||
}
|
||||
|
||||
reset();
|
||||
Stream::TimeCounter::~TimeCounter()
|
||||
{
|
||||
g_free(timer);
|
||||
}
|
||||
|
||||
void Stream::TimeCounter::tic ()
|
||||
{
|
||||
// how long since last time
|
||||
GstClockTime t = gst_util_get_timestamp ();
|
||||
GstClockTime dt = t - last_time;
|
||||
|
||||
// one more frame since last time
|
||||
++nbFrames;
|
||||
double dt = g_timer_elapsed (timer, NULL) * 1000.0;
|
||||
g_timer_start(timer);
|
||||
|
||||
// calculate instantaneous framerate
|
||||
// Exponential moving averate with previous framerate to filter jitter (50/50)
|
||||
// The divition of frame/time is done on long integer GstClockTime, counting in microsecond
|
||||
// NB: factor 100 to get 0.01 precision
|
||||
fps = 0.5 * fps + 0.005 * static_cast<double>( ( 100 * GST_SECOND * nbFrames ) / dt );
|
||||
|
||||
// reset counter every second
|
||||
if ( dt >= GST_SECOND)
|
||||
{
|
||||
last_time = t;
|
||||
nbFrames = 0;
|
||||
}
|
||||
// Exponential moving averate with previous framerate to filter jitter
|
||||
if (dt > 1.0)
|
||||
fps = 0.5 * fps + 500.0 / dt ;
|
||||
}
|
||||
|
||||
GstClockTime Stream::TimeCounter::dt ()
|
||||
{
|
||||
GstClockTime t = gst_util_get_timestamp ();
|
||||
GstClockTime dt = t - tic_time;
|
||||
tic_time = t;
|
||||
|
||||
// return the instantaneous delta t
|
||||
return dt;
|
||||
}
|
||||
|
||||
void Stream::TimeCounter::reset ()
|
||||
{
|
||||
last_time = gst_util_get_timestamp ();;
|
||||
tic_time = last_time;
|
||||
nbFrames = 0;
|
||||
fps = 0.0;
|
||||
}
|
||||
|
||||
double Stream::TimeCounter::frameRate() const
|
||||
{
|
||||
return fps;
|
||||
}
|
||||
|
||||
|
||||
19
Stream.h
19
Stream.h
@@ -83,6 +83,14 @@ public:
|
||||
* Performs a full check of the Gstreamer pipeline if testpipeline is true
|
||||
* */
|
||||
bool isPlaying(bool testpipeline = false) const;
|
||||
/**
|
||||
* Attempt to restart
|
||||
* */
|
||||
virtual void rewind();
|
||||
/**
|
||||
* Get position time
|
||||
* */
|
||||
virtual GstClockTime position();
|
||||
/**
|
||||
* Get rendering update framerate
|
||||
* measured during play
|
||||
@@ -126,6 +134,7 @@ protected:
|
||||
bool live_;
|
||||
|
||||
// GST & Play status
|
||||
GstClockTime position_;
|
||||
GstState desired_state_;
|
||||
GstElement *pipeline_;
|
||||
GstVideoInfo v_frame_video_info_;
|
||||
@@ -135,17 +144,13 @@ protected:
|
||||
|
||||
// fps counter
|
||||
struct TimeCounter {
|
||||
|
||||
GstClockTime last_time;
|
||||
GstClockTime tic_time;
|
||||
int nbFrames;
|
||||
GTimer *timer;
|
||||
gdouble fps;
|
||||
public:
|
||||
TimeCounter();
|
||||
GstClockTime dt();
|
||||
~TimeCounter();
|
||||
void tic();
|
||||
void reset();
|
||||
gdouble frameRate() const;
|
||||
inline gdouble frameRate() const { return fps; }
|
||||
};
|
||||
TimeCounter timecount_;
|
||||
|
||||
|
||||
@@ -102,13 +102,49 @@ void StreamSource::setActive (bool on)
|
||||
{
|
||||
bool was_active = active_;
|
||||
|
||||
// try to activate (may fail if source is cloned)
|
||||
Source::setActive(on);
|
||||
|
||||
// change status of media player (only if status changed)
|
||||
if ( active_ != was_active ) {
|
||||
if (stream_)
|
||||
stream_->enable(active_);
|
||||
}
|
||||
// change status of stream (only if status changed)
|
||||
if ( stream_ && active_ != was_active )
|
||||
stream_->enable(active_);
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool StreamSource::playing () const
|
||||
{
|
||||
if ( stream_ )
|
||||
return stream_->isPlaying();
|
||||
return false;
|
||||
}
|
||||
|
||||
void StreamSource::play (bool on)
|
||||
{
|
||||
if ( stream_ )
|
||||
stream_->play(on);
|
||||
}
|
||||
|
||||
|
||||
bool StreamSource::playable () const
|
||||
{
|
||||
if ( stream_ )
|
||||
return !stream_->singleFrame();
|
||||
return false;
|
||||
}
|
||||
|
||||
void StreamSource::replay ()
|
||||
{
|
||||
if ( stream_ )
|
||||
stream_->rewind();
|
||||
}
|
||||
|
||||
guint64 StreamSource::playtime () const
|
||||
{
|
||||
if ( stream_ )
|
||||
return stream_->position();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StreamSource::update(float dt)
|
||||
|
||||
@@ -30,6 +30,11 @@ public:
|
||||
// implementation of source API
|
||||
void update (float dt) override;
|
||||
void setActive (bool on) override;
|
||||
bool playing () const override;
|
||||
void play (bool) override;
|
||||
bool playable () const override;
|
||||
void replay () override;
|
||||
guint64 playtime () const override;
|
||||
bool failed() const override;
|
||||
uint texture() const override;
|
||||
|
||||
|
||||
@@ -1209,10 +1209,10 @@ View::Cursor TextureView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pa
|
||||
sourceNode->rotation_.z = glm::radians( float(degrees) );
|
||||
overlay_rotation_clock_->visible_ = true;
|
||||
overlay_rotation_clock_->copyTransform(overlay_rotation_);
|
||||
info << "Texture Angle " << degrees << "\u00b0"; // degree symbol
|
||||
info << "Texture Angle " << degrees << UNICODE_DEGREE;
|
||||
}
|
||||
else
|
||||
info << "Texture Angle " << std::fixed << std::setprecision(1) << glm::degrees(sourceNode->rotation_.z) << "\u00b0"; // degree symbol
|
||||
info << "Texture Angle " << std::fixed << std::setprecision(1) << glm::degrees(sourceNode->rotation_.z) << UNICODE_DEGREE;
|
||||
|
||||
overlay_rotation_clock_hand_->visible_ = true;
|
||||
overlay_rotation_clock_hand_->translation_.x = s->stored_status_->translation_.x;
|
||||
|
||||
261
Timeline.cpp
261
Timeline.cpp
@@ -6,6 +6,10 @@
|
||||
#include "Log.h"
|
||||
#include "Timeline.h"
|
||||
|
||||
|
||||
static float empty_gaps[MAX_TIMELINE_ARRAY] = {};
|
||||
static float empty_fade[MAX_TIMELINE_ARRAY] = {};
|
||||
|
||||
struct includesTime: public std::unary_function<TimeInterval, bool>
|
||||
{
|
||||
inline bool operator()(const TimeInterval s) const
|
||||
@@ -86,7 +90,7 @@ GstClockTime Timeline::next(GstClockTime time) const
|
||||
GstClockTime next_time = time;
|
||||
|
||||
TimeInterval gap;
|
||||
if (gapAt(time, gap) && gap.is_valid())
|
||||
if (getGapAt(time, gap) && gap.is_valid())
|
||||
next_time = gap.end;
|
||||
|
||||
return next_time;
|
||||
@@ -96,7 +100,7 @@ GstClockTime Timeline::previous(GstClockTime time) const
|
||||
{
|
||||
GstClockTime prev_time = time;
|
||||
TimeInterval gap;
|
||||
if (gapAt(time, gap) && gap.is_valid())
|
||||
if (getGapAt(time, gap) && gap.is_valid())
|
||||
prev_time = gap.begin;
|
||||
|
||||
return prev_time;
|
||||
@@ -116,7 +120,18 @@ void Timeline::update()
|
||||
gaps_array_need_update_ = false;
|
||||
}
|
||||
|
||||
bool Timeline::gapAt(const GstClockTime t, TimeInterval &gap) const
|
||||
void Timeline::refresh()
|
||||
{
|
||||
fillArrayFromGaps(gapsArray_, MAX_TIMELINE_ARRAY);
|
||||
}
|
||||
|
||||
bool Timeline::gapAt(const GstClockTime t) const
|
||||
{
|
||||
TimeIntervalSet::const_iterator g = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
|
||||
return ( g != gaps_.end() );
|
||||
}
|
||||
|
||||
bool Timeline::getGapAt(const GstClockTime t, TimeInterval &gap) const
|
||||
{
|
||||
TimeIntervalSet::const_iterator g = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
|
||||
|
||||
@@ -133,6 +148,84 @@ bool Timeline::addGap(GstClockTime begin, GstClockTime end)
|
||||
return addGap( TimeInterval(begin, end) );
|
||||
}
|
||||
|
||||
bool Timeline::cut(GstClockTime t, bool left, bool join_extremity)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (timing_.includes(t))
|
||||
{
|
||||
TimeIntervalSet::iterator gap = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
|
||||
|
||||
// cut left part
|
||||
if (left) {
|
||||
// cut a gap
|
||||
if ( gap != gaps_.end() )
|
||||
{
|
||||
GstClockTime b = gap->begin;
|
||||
gaps_.erase(gap);
|
||||
ret = addGap(b, t);
|
||||
}
|
||||
// create a gap
|
||||
else {
|
||||
auto previous = gaps_.end();
|
||||
for (auto g = gaps_.begin(); g != gaps_.end(); previous = g++) {
|
||||
if ( g->begin > t)
|
||||
break;
|
||||
}
|
||||
if (join_extremity) {
|
||||
//TODO
|
||||
}
|
||||
else {
|
||||
if (previous == gaps_.end())
|
||||
ret = addGap( TimeInterval(timing_.begin, t) );
|
||||
else {
|
||||
GstClockTime b = previous->begin;
|
||||
gaps_.erase(previous);
|
||||
ret = addGap( TimeInterval(b, t) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// cut right part
|
||||
else {
|
||||
// cut a gap
|
||||
if ( gap != gaps_.end() )
|
||||
{
|
||||
GstClockTime e = gap->end;
|
||||
gaps_.erase(gap);
|
||||
ret = addGap(t, e);
|
||||
}
|
||||
// create a gap
|
||||
else {
|
||||
auto suivant = gaps_.rend();
|
||||
for (auto g = gaps_.rbegin(); g != gaps_.rend(); suivant = g++) {
|
||||
if ( g->end < t)
|
||||
break;
|
||||
}
|
||||
if (join_extremity) {
|
||||
if (suivant != gaps_.rend()) {
|
||||
for (auto g = gaps_.find(*suivant); g != gaps_.end(); ) {
|
||||
g = gaps_.erase(g);
|
||||
}
|
||||
}
|
||||
ret = addGap( TimeInterval(t, timing_.end) );
|
||||
}
|
||||
else {
|
||||
if (suivant == gaps_.rend())
|
||||
ret = addGap( TimeInterval(t, timing_.end) );
|
||||
else {
|
||||
GstClockTime e = suivant->end;
|
||||
gaps_.erase( gaps_.find(*suivant));
|
||||
ret = addGap( TimeInterval(t, e) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Timeline::addGap(TimeInterval s)
|
||||
{
|
||||
if ( s.is_valid() ) {
|
||||
@@ -163,6 +256,108 @@ bool Timeline::removeGaptAt(GstClockTime t)
|
||||
}
|
||||
|
||||
|
||||
GstClockTime Timeline::sectionsDuration() const
|
||||
{
|
||||
// compute the sum of durations of gaps
|
||||
GstClockTime d = 0;
|
||||
for (auto g = gaps_.begin(); g != gaps_.end(); ++g)
|
||||
d+= g->duration();
|
||||
|
||||
// remove sum of gaps from actual duration
|
||||
return duration() - d;
|
||||
}
|
||||
|
||||
|
||||
GstClockTime Timeline::sectionsTimeAt(GstClockTime t) const
|
||||
{
|
||||
// loop over gaps
|
||||
GstClockTime d = t;
|
||||
for (auto g = gaps_.begin(); g != gaps_.end(); ++g) {
|
||||
// gap before target?
|
||||
if ( g->begin < d ) {
|
||||
// increment target
|
||||
d += g->duration();
|
||||
}
|
||||
else
|
||||
// done
|
||||
break;
|
||||
}
|
||||
|
||||
// return updated target
|
||||
return d;
|
||||
}
|
||||
|
||||
size_t Timeline::fillSectionsArrays( float* const gaps, float* const fading)
|
||||
{
|
||||
size_t arraysize = MAX_TIMELINE_ARRAY;
|
||||
float* gapsptr = gaps;
|
||||
float* fadingptr = fading;
|
||||
|
||||
if (gaps_array_need_update_)
|
||||
fillArrayFromGaps(gapsArray_, MAX_TIMELINE_ARRAY);
|
||||
|
||||
if (gaps_.size() > 0) {
|
||||
|
||||
// indices to define [s e[] sections
|
||||
size_t s = 0;
|
||||
size_t e = MAX_TIMELINE_ARRAY;
|
||||
arraysize = 0;
|
||||
|
||||
auto it = gaps_.begin();
|
||||
|
||||
// cut at the beginning
|
||||
if ((*it).begin == timing_.begin) {
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
gapsptr[ MAX(i, 0) ] = 1.f;
|
||||
|
||||
s = ( (*it).end * MAX_TIMELINE_ARRAY ) / timing_.end;
|
||||
++it;
|
||||
}
|
||||
|
||||
// loop
|
||||
for (; it != gaps_.end(); ++it) {
|
||||
|
||||
// [s e] section
|
||||
e = ( (*it).begin * MAX_TIMELINE_ARRAY ) / timing_.end;
|
||||
|
||||
size_t n = e - s;
|
||||
memcpy( gapsptr, gapsArray_ + s, n * sizeof(float));
|
||||
memcpy( fadingptr, fadingArray_ + s, n * sizeof(float));
|
||||
|
||||
for (size_t i = -5; i > 0; ++i)
|
||||
gapsptr[ MAX(n+i, 0) ] = 1.f;
|
||||
|
||||
gapsptr += n;
|
||||
fadingptr += n;
|
||||
arraysize += n;
|
||||
|
||||
// next section
|
||||
s = ( (*it).end * MAX_TIMELINE_ARRAY ) / timing_.end;
|
||||
}
|
||||
|
||||
// close last [s e] section
|
||||
if (s != MAX_TIMELINE_ARRAY) {
|
||||
|
||||
// [s e] section
|
||||
e = MAX_TIMELINE_ARRAY;
|
||||
|
||||
size_t n = e - s;
|
||||
memcpy( gapsptr, gapsArray_ + s, n * sizeof(float));
|
||||
memcpy( fadingptr, fadingArray_ + s, n * sizeof(float));
|
||||
arraysize += n;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
memcpy( gaps, gapsArray_, MAX_TIMELINE_ARRAY * sizeof(float));
|
||||
memcpy( fading, fadingArray_, MAX_TIMELINE_ARRAY * sizeof(float));
|
||||
}
|
||||
|
||||
return arraysize;
|
||||
}
|
||||
|
||||
|
||||
TimeIntervalSet Timeline::sections() const
|
||||
{
|
||||
TimeIntervalSet sec;
|
||||
@@ -200,7 +395,7 @@ void Timeline::clearGaps()
|
||||
gaps_array_need_update_ = true;
|
||||
}
|
||||
|
||||
float Timeline::fadingAt(const GstClockTime t)
|
||||
float Timeline::fadingAt(const GstClockTime t) const
|
||||
{
|
||||
double true_index = (static_cast<double>(MAX_TIMELINE_ARRAY) * static_cast<double>(t)) / static_cast<double>(timing_.end);
|
||||
double previous_index = floor(true_index);
|
||||
@@ -213,10 +408,22 @@ float Timeline::fadingAt(const GstClockTime t)
|
||||
return v;
|
||||
}
|
||||
|
||||
size_t Timeline::fadingIndexAt(const GstClockTime t) const
|
||||
{
|
||||
double true_index = (static_cast<double>(MAX_TIMELINE_ARRAY) * static_cast<double>(t)) / static_cast<double>(timing_.end);
|
||||
double previous_index = floor(true_index);
|
||||
return MINI( static_cast<size_t>(previous_index), MAX_TIMELINE_ARRAY-1);
|
||||
}
|
||||
|
||||
void Timeline::clearFading()
|
||||
{
|
||||
for(int i=0;i<MAX_TIMELINE_ARRAY;++i)
|
||||
fadingArray_[i] = 1.f;
|
||||
// fill static with 1 (only once)
|
||||
if (empty_fade[0] < 1.f){
|
||||
for(int i=0;i<MAX_TIMELINE_ARRAY;++i)
|
||||
empty_fade[i] = 1.f;
|
||||
}
|
||||
// clear with static array
|
||||
memcpy(fadingArray_, empty_fade, MAX_TIMELINE_ARRAY * sizeof(float));
|
||||
}
|
||||
|
||||
void Timeline::smoothFading(uint N)
|
||||
@@ -246,7 +453,6 @@ void Timeline::smoothFading(uint N)
|
||||
|
||||
void Timeline::autoFading(uint milisecond)
|
||||
{
|
||||
|
||||
GstClockTime stepduration = timing_.end / MAX_TIMELINE_ARRAY;
|
||||
stepduration = GST_TIME_AS_MSECONDS(stepduration);
|
||||
uint N = milisecond / stepduration;
|
||||
@@ -264,8 +470,10 @@ void Timeline::autoFading(uint milisecond)
|
||||
{
|
||||
// get index of begining of section
|
||||
size_t s = ( (*it).begin * MAX_TIMELINE_ARRAY ) / timing_.end;
|
||||
s += 1;
|
||||
// get index of ending of section
|
||||
size_t e = ( (*it).end * MAX_TIMELINE_ARRAY ) / timing_.end;
|
||||
e -= 1;
|
||||
|
||||
// calculate size of the smooth transition in [s e] interval
|
||||
uint n = MIN( (e-s)/3, N );
|
||||
@@ -281,7 +489,23 @@ void Timeline::autoFading(uint milisecond)
|
||||
for (; i < e; ++i)
|
||||
fadingArray_[i] = static_cast<float>(e-i) / static_cast<float>(n);
|
||||
}
|
||||
}
|
||||
|
||||
bool Timeline::autoCut()
|
||||
{
|
||||
bool changed = false;
|
||||
for (long i = 0; i < MAX_TIMELINE_ARRAY; ++i) {
|
||||
if (fadingArray_[i] < EPSILON) {
|
||||
if (gapsArray_[i] != 1.f)
|
||||
changed = true;
|
||||
gapsArray_[i] = 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
updateGapsFromArray(gapsArray_, MAX_TIMELINE_ARRAY);
|
||||
gaps_array_need_update_ = false;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void Timeline::updateGapsFromArray(float *array, size_t array_size)
|
||||
@@ -299,14 +523,16 @@ void Timeline::updateGapsFromArray(float *array, size_t array_size)
|
||||
// detect a change of value between two slots
|
||||
if ( array[i] != status) {
|
||||
// compute time of the event in array
|
||||
GstClockTime t = (timing_.duration() * i) / array_size;
|
||||
GstClockTime t = (timing_.duration() * i) / (array_size-1);
|
||||
// change from 0.f to 1.f : begin of a gap
|
||||
if (array[i] > 0.f) {
|
||||
begin_gap = t;
|
||||
}
|
||||
// change from 1.f to 0.f : end of a gap
|
||||
else {
|
||||
addGap( begin_gap, t );
|
||||
TimeInterval d (begin_gap, t) ;
|
||||
if (d.is_valid() && d.duration() > step_)
|
||||
addGap(d);
|
||||
begin_gap = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
// swap
|
||||
@@ -314,10 +540,14 @@ void Timeline::updateGapsFromArray(float *array, size_t array_size)
|
||||
}
|
||||
}
|
||||
// end a potentially pending gap if reached end of array with no end of gap
|
||||
if (begin_gap != GST_CLOCK_TIME_NONE)
|
||||
addGap( begin_gap, timing_.end );
|
||||
if (begin_gap != GST_CLOCK_TIME_NONE) {
|
||||
TimeInterval d (begin_gap, timing_.end) ;
|
||||
if (d.is_valid() && d.duration() > step_)
|
||||
addGap(d);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Timeline::fillArrayFromGaps(float *array, size_t array_size)
|
||||
@@ -325,14 +555,15 @@ void Timeline::fillArrayFromGaps(float *array, size_t array_size)
|
||||
// fill the array from gaps
|
||||
if (array != nullptr && array_size > 0 && timing_.is_valid()) {
|
||||
|
||||
for(int i=0;i<array_size;++i)
|
||||
gapsArray_[i] = 0.f;
|
||||
// clear with static array
|
||||
memcpy(gapsArray_, empty_gaps, MAX_TIMELINE_ARRAY * sizeof(float));
|
||||
|
||||
// for each gap
|
||||
GstClockTime d = timing_.duration();
|
||||
for (auto it = gaps_.begin(); it != gaps_.end(); ++it)
|
||||
{
|
||||
size_t s = ( (*it).begin * array_size ) / timing_.end;
|
||||
size_t e = ( (*it).end * array_size ) / timing_.end;
|
||||
size_t s = ( (*it).begin * array_size ) / d;
|
||||
size_t e = ( (*it).end * array_size ) / d;
|
||||
|
||||
// fill with 1 where there is a gap
|
||||
for (size_t i = s; i < e; ++i) {
|
||||
|
||||
27
Timeline.h
27
Timeline.h
@@ -62,6 +62,10 @@ struct TimeInterval
|
||||
{
|
||||
return (is_valid() && t != GST_CLOCK_TIME_NONE && !(t < this->begin) && !(t > this->end) );
|
||||
}
|
||||
inline bool includes(const TimeInterval& b) const
|
||||
{
|
||||
return (is_valid() && b.is_valid() && includes(b.begin) && includes(b.end) );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -69,7 +73,7 @@ struct order_comparator
|
||||
{
|
||||
inline bool operator () (const TimeInterval a, const TimeInterval b) const
|
||||
{
|
||||
return (a < b);
|
||||
return (a < b || a.begin < b.begin);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -85,6 +89,7 @@ public:
|
||||
|
||||
bool is_valid();
|
||||
void update();
|
||||
void refresh();
|
||||
|
||||
// global properties of the timeline
|
||||
// timeline is valid only if all 3 are set
|
||||
@@ -105,23 +110,35 @@ public:
|
||||
GstClockTime next(GstClockTime time) const;
|
||||
GstClockTime previous(GstClockTime time) const;
|
||||
|
||||
// Manipulation of gaps in the timeline
|
||||
// Manipulation of gaps
|
||||
inline TimeIntervalSet gaps() const { return gaps_; }
|
||||
inline TimeIntervalSet sections() const;
|
||||
inline size_t numGaps() const { return gaps_.size(); }
|
||||
float *gapsArray();
|
||||
void clearGaps();
|
||||
void setGaps(const TimeIntervalSet &g);
|
||||
bool addGap(TimeInterval s);
|
||||
bool addGap(GstClockTime begin, GstClockTime end);
|
||||
bool cut(GstClockTime t, bool left, bool join_extremity);
|
||||
bool removeGaptAt(GstClockTime t);
|
||||
bool gapAt(const GstClockTime t, TimeInterval &gap) const;
|
||||
bool gapAt(const GstClockTime t) const;
|
||||
bool getGapAt(const GstClockTime t, TimeInterval &gap) const;
|
||||
|
||||
float fadingAt(const GstClockTime t);
|
||||
// inverse of gaps: sections of play areas
|
||||
TimeIntervalSet sections() const;
|
||||
GstClockTime sectionsDuration() const;
|
||||
GstClockTime sectionsTimeAt(GstClockTime t) const;
|
||||
size_t fillSectionsArrays(float * const gaps, float * const fading);
|
||||
|
||||
// Manipulation of Fading
|
||||
float fadingAt(const GstClockTime t) const;
|
||||
size_t fadingIndexAt(const GstClockTime t) const;
|
||||
inline float *fadingArray() { return fadingArray_; }
|
||||
void clearFading();
|
||||
|
||||
// Edit
|
||||
void smoothFading(uint N = 1);
|
||||
void autoFading(uint milisecond = 100);
|
||||
bool autoCut();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,10 +10,13 @@
|
||||
#define NAV_MENU 66
|
||||
#define NAV_TRANS 67
|
||||
|
||||
#include "SourceList.h"
|
||||
#include "InfoVisitor.h"
|
||||
|
||||
struct ImVec2;
|
||||
class Source;
|
||||
class MediaPlayer;
|
||||
class FrameBufferImage;
|
||||
class FrameGrabber;
|
||||
|
||||
class SourcePreview {
|
||||
|
||||
@@ -101,20 +104,52 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class MediaController
|
||||
class SourceController
|
||||
{
|
||||
MediaPlayer *mp_;
|
||||
std::string current_;
|
||||
bool follow_active_source_;
|
||||
bool media_playing_mode_;
|
||||
bool slider_pressed_;
|
||||
float min_width_;
|
||||
float h_space_;
|
||||
float v_space_;
|
||||
float buttons_width_;
|
||||
float buttons_height_;
|
||||
float timeline_height_;
|
||||
float scrollbar_;
|
||||
float mediaplayer_height_;
|
||||
|
||||
std::string active_label_;
|
||||
int active_selection_;
|
||||
InfoVisitor info_;
|
||||
SourceList selection_;
|
||||
|
||||
// re-usable ui parts
|
||||
void DrawButtonBar(ImVec2 bottom, float width);
|
||||
const char *SourcePlayIcon(Source *s);
|
||||
bool SourceButton(Source *s, ImVec2 framesize);
|
||||
|
||||
// Render the sources dynamically selected
|
||||
void RenderSelectedSources();
|
||||
|
||||
// Render a stored selection
|
||||
bool selection_context_menu_;
|
||||
MediaPlayer *selection_mediaplayer_;
|
||||
double selection_target_slower_;
|
||||
double selection_target_faster_;
|
||||
void RenderSelectionContextMenu();
|
||||
void RenderSelection(size_t i);
|
||||
|
||||
// Render a single source
|
||||
void RenderSingleSource(Source *s);
|
||||
|
||||
// Render a single media player
|
||||
MediaPlayer *mediaplayer_active_;
|
||||
bool mediaplayer_mode_;
|
||||
bool mediaplayer_slider_pressed_;
|
||||
float mediaplayer_timeline_zoom_;
|
||||
void RenderMediaPlayer(MediaPlayer *mp);
|
||||
|
||||
public:
|
||||
MediaController();
|
||||
|
||||
void setMediaPlayer(MediaPlayer *mp = nullptr);
|
||||
void followCurrentSource();
|
||||
SourceController();
|
||||
|
||||
void resetActiveSelection();
|
||||
void Render();
|
||||
};
|
||||
|
||||
@@ -124,7 +159,7 @@ class UserInterface
|
||||
friend class Navigator;
|
||||
Navigator navigator;
|
||||
ToolBox toolbox;
|
||||
MediaController mediacontrol;
|
||||
SourceController sourcecontrol;
|
||||
|
||||
bool ctrl_modifier_active;
|
||||
bool alt_modifier_active;
|
||||
@@ -138,8 +173,11 @@ class UserInterface
|
||||
unsigned int screenshot_step;
|
||||
|
||||
// frame grabbers
|
||||
uint64_t video_recorder_;
|
||||
uint64_t webcam_emulator_;
|
||||
FrameGrabber *video_recorder_;
|
||||
|
||||
#if defined(LINUX)
|
||||
FrameGrabber *webcam_emulator_;
|
||||
#endif
|
||||
|
||||
// Private Constructor
|
||||
UserInterface();
|
||||
@@ -171,7 +209,6 @@ public:
|
||||
|
||||
void showPannel(int id = 0);
|
||||
void showSourceEditor(Source *s);
|
||||
void showMediaPlayer(MediaPlayer *mp);
|
||||
|
||||
// TODO implement the shader editor
|
||||
std::string currentTextEdit;
|
||||
|
||||
@@ -63,14 +63,14 @@ public:
|
||||
virtual void visit (Handles&) {}
|
||||
virtual void visit (Symbol&) {}
|
||||
virtual void visit (Disk&) {}
|
||||
virtual void visit (Stream&) {}
|
||||
virtual void visit (MediaPlayer&) {}
|
||||
virtual void visit (Shader&) {}
|
||||
virtual void visit (ImageShader&) {}
|
||||
virtual void visit (MaskShader&) {}
|
||||
virtual void visit (ImageProcessingShader&) {}
|
||||
|
||||
// utility
|
||||
virtual void visit (Stream&) {}
|
||||
virtual void visit (MediaPlayer&) {}
|
||||
virtual void visit (MixingGroup&) {}
|
||||
virtual void visit (Source&) {}
|
||||
virtual void visit (MediaSource&) {}
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
#define SESSION_THUMBNAIL_HEIGHT 120.f
|
||||
|
||||
#define IMGUI_TITLE_MAINWINDOW ICON_FA_CIRCLE_NOTCH " vimix"
|
||||
#define IMGUI_TITLE_MEDIAPLAYER ICON_FA_FILM " Player"
|
||||
#define IMGUI_TITLE_MEDIAPLAYER ICON_FA_PLAY_CIRCLE " Player"
|
||||
#define IMGUI_TITLE_HISTORY ICON_FA_HISTORY " History"
|
||||
#define IMGUI_TITLE_LOGS ICON_FA_LIST " Logs"
|
||||
#define IMGUI_TITLE_TOOLBOX ICON_FA_WRENCH " Development Toolbox"
|
||||
@@ -73,6 +73,7 @@
|
||||
#define IMGUI_RIGHT_ALIGN -3.5f * ImGui::GetTextLineHeightWithSpacing()
|
||||
#define IMGUI_TOP_ALIGN 10
|
||||
#define IMGUI_COLOR_OVERLAY IM_COL32(5, 5, 5, 150)
|
||||
#define IMGUI_COLOR_LIGHT_OVERLAY IM_COL32(5, 5, 5, 50)
|
||||
#define IMGUI_COLOR_RECORD 1.0, 0.05, 0.05
|
||||
#define IMGUI_COLOR_STREAM 0.05, 0.8, 1.0
|
||||
#define IMGUI_NOTIFICATION_DURATION 2.5f
|
||||
|
||||
@@ -985,3 +985,14 @@
|
||||
#define ICON_FA_CHEVRON_UP "\xEF\x81\xB7"
|
||||
#define ICON_FA_HAND_SPOCK "\xEF\x89\x99"
|
||||
#define ICON_FA_HAND_POINT_UP "\xEF\x82\xA6"
|
||||
|
||||
// Unicode Characters
|
||||
#define UNICODE_DEGREE "\u00B0"
|
||||
#define UNICODE_PLUSMINUS "\u00B1"
|
||||
#define UNICODE_MULTIPLY "\u00D7"
|
||||
#define UNICODE_SUPERSCRIPT_ONE "\u00B9"
|
||||
#define UNICODE_SUPERSCRIPT_TWO "\u00B2"
|
||||
#define UNICODE_SUPERSCRIPT_THREE "\u00B3"
|
||||
#define UNICODE_SUPERSCRIPT_MINUS "\u00AF"
|
||||
#define UNICODE_HALF "\u00BD"
|
||||
#define UNICODE_COPYRIGHT "\u00A9"
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user