Merge pull request #39 from brunoherbelin/dev

Accept new Player (dev)
This commit is contained in:
BHBN
2021-06-19 09:49:11 +02:00
committed by GitHub
54 changed files with 3144 additions and 958 deletions

5
.gitignore vendored
View File

@@ -19,8 +19,13 @@ rules.ninja
rsc/shaders/paint.fs
.vscode/settings.json
<<<<<<< HEAD
osx/.DS_Store
.DS_Store
osx/runvimix
=======
>>>>>>> dev

View File

@@ -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();
}

View File

@@ -319,6 +319,7 @@ set(VMIX_SRCS
SearchVisitor.cpp
ImGuiToolkit.cpp
ImGuiVisitor.cpp
InfoVisitor.cpp
GstToolkit.cpp
GlmToolkit.cpp
SystemToolkit.cpp

View File

@@ -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);

View File

@@ -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_)

View File

@@ -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;

View File

@@ -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>

View File

@@ -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();

View File

@@ -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;

View File

@@ -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"

View File

@@ -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;
}

View File

@@ -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_

View File

@@ -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();
}

View File

@@ -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
View 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
View 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

View File

@@ -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() );

View File

@@ -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) ;
}

View File

@@ -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_;

View File

@@ -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)

View File

@@ -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;

View File

@@ -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) );

View File

@@ -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);

View File

@@ -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);

View File

@@ -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_; }

View File

@@ -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);

View File

@@ -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

View File

@@ -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();

View File

@@ -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_;

View File

@@ -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)
{

View File

@@ -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:

View File

@@ -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();

View File

@@ -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;

View File

@@ -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)

View File

@@ -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,

View File

@@ -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_)

View File

@@ -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;
}
};

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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_;

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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

View File

@@ -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;

View File

@@ -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&) {}

View File

@@ -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

View File

@@ -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.