work in progress cleanum memory leak and crash :(

This commit is contained in:
brunoherbelin
2020-08-11 00:11:22 +02:00
parent c829e5a40c
commit bb8dcf088e
15 changed files with 590 additions and 531 deletions

View File

@@ -234,6 +234,7 @@ set(VMIX_SRCS
Screenshot.cpp Screenshot.cpp
Resource.cpp Resource.cpp
FileDialog.cpp FileDialog.cpp
Timeline.cpp
MediaPlayer.cpp MediaPlayer.cpp
MediaSource.cpp MediaSource.cpp
FrameBuffer.cpp FrameBuffer.cpp

View File

@@ -286,7 +286,7 @@ void ImGuiToolkit::HelpMarker(const char* desc)
#define NUM_MARKS 10 #define NUM_MARKS 10
#define LARGE_TICK_INCREMENT 1 #define LARGE_TICK_INCREMENT 1
#define LABEL_TICK_INCREMENT 3 #define LABEL_TICK_INCREMENT 3
bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 duration, guint64 step, float scale) bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 duration, guint64 step, const float width)
{ {
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 }; 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 };
@@ -308,8 +308,7 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 dura
// widget bounding box // widget bounding box
const float height = 2.f * (fontsize + style.FramePadding.y); const float height = 2.f * (fontsize + style.FramePadding.y);
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), height); ImVec2 size = ImVec2(width, height);
size.x *= scale;
ImRect bbox(pos, pos + size); ImRect bbox(pos, pos + size);
ImGui::ItemSize(size, style.FramePadding.y); ImGui::ItemSize(size, style.FramePadding.y);
if (!ImGui::ItemAdd(bbox, id)) if (!ImGui::ItemAdd(bbox, id))
@@ -380,7 +379,7 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 dura
// how many pixels to represent one frame step? // how many pixels to represent one frame step?
float tick_step_pixels = timeline_bbox.GetWidth() * step_; float tick_step_pixels = timeline_bbox.GetWidth() * step_;
// tick at each step: add a label every 30 frames (1 second?) // tick at each step: add a label every 0 frames
if (tick_step_pixels > 5.f) if (tick_step_pixels > 5.f)
{ {
large_tick_step = 10 * step; large_tick_step = 10 * step;
@@ -468,212 +467,6 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 dura
return left_mouse_press; return left_mouse_press;
} }
// Draws a timeline showing
// 1) a cursor at position *time in the range [0 duration]
// 2) a line of tick marks indicating time, every step if possible
// 3) a slider handle below the cursor: user can move the slider
// 4) different blocs for each time segment [first second] of the list of segments
// Behavior
// a) Returns TRUE if the left mouse button LMB is pressed over the timeline
// b) the value of *time is changed to the position of the slider handle from user input (LMB)
// c) the list segments is replaced with the list of new segments created by user (empty list otherwise)
bool ImGuiToolkit::TimelineSliderEdit(const char* label, guint64 *time, guint64 duration, guint64 step,
std::list<std::pair<guint64, guint64> >& segments)
{
static guint64 optimal_tick_marks[12] = { 100 * MILISECOND, 500 * MILISECOND, 1 * SECOND, 2 * SECOND, 5 * SECOND, 10 * SECOND, 20 * SECOND, 1 * MINUTE, 2 * MINUTE, 5 * MINUTE, 10 * MINUTE, 60 * MINUTE };
// get window
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
// get style & id
const ImGuiStyle& style = ImGui::GetStyle();
const float fontsize = ImGui::GetFontSize();
ImGuiContext& g = *GImGui;
const ImGuiID id = window->GetID(label);
//
// FIRST PREPARE ALL data structures
//
// widget bounding box
const float height = 2.f * (fontsize + style.FramePadding.y);
ImVec2 pos = window->DC.CursorPos;
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), height);
ImRect bbox(pos, pos + size);
ImGui::ItemSize(size, style.FramePadding.y);
if (!ImGui::ItemAdd(bbox, id))
return false;
// cursor size
const float cursor_scale = 1.f;
const float cursor_width = 0.5f * fontsize * cursor_scale;
// TIMELINE is inside the bbox, in a slightly smaller bounding box
ImRect timeline_bbox(bbox);
timeline_bbox.Expand( ImVec2(-cursor_width, -style.FramePadding.y) );
// SLIDER is inside the timeline
ImRect slider_bbox( timeline_bbox.GetTL() + ImVec2(-cursor_width + 2.f, cursor_width + 4.f ), timeline_bbox.GetBR() + ImVec2( cursor_width - 2.f, 0.f ) );
// units conversion: from time to float (calculation made with higher precision first)
float time_ = static_cast<float> ( static_cast<double>(*time) / static_cast<double>(duration) );
float step_ = static_cast<float> ( static_cast<double>(step) / static_cast<double>(duration) );
//
// SECOND GET USER INPUT AND PERFORM CHANGES AND DECISIONS
//
// read user input from system
bool left_mouse_press = false;
bool right_mouse_press = false;
const bool hovered = ImGui::ItemHoverable(bbox, id);
bool temp_input_is_active = ImGui::TempInputIsActive(id);
if (!temp_input_is_active)
{
const bool focus_requested = ImGui::FocusableItemRegister(window, id);
left_mouse_press = hovered && ImGui::IsMouseDown(ImGuiMouseButton_Left);
right_mouse_press = hovered && ImGui::IsMouseDown(ImGuiMouseButton_Right);
if (focus_requested || left_mouse_press || right_mouse_press || g.NavActivateId == id || g.NavInputId == id)
{
ImGui::SetActiveID(id, window);
ImGui::SetFocusID(id, window);
ImGui::FocusWindow(window);
}
}
// active slider if inside bb
ImRect grab_slider_bb;
ImU32 grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrab);
ImGuiID slider_id = window->GetID("werwerwsdvcsdgfdghdfsgagewrgsvdfhfdghkjfghsdgsdtgewfszdvgfkjfg"); // FIXME: cleaner way to create a valid but useless id that is never active
if ( slider_bbox.Contains(g.IO.MousePos) ) {
slider_id = id; // active id
grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
}
// time Slider behavior
float time_slider = time_ * 10.f; // x 10 precision on grab
float time_zero = 0.f;
float time_end = 10.f;
bool value_changed = ImGui::SliderBehavior(slider_bbox, slider_id, ImGuiDataType_Float, &time_slider, &time_zero,
&time_end, "%.2f", 1.f, ImGuiSliderFlags_None, &grab_slider_bb);
if (value_changed){
// g_print("slider %f %ld \n", time_slider, static_cast<guint64> ( static_cast<double>(time_slider) * static_cast<double>(duration) ));
*time = static_cast<guint64> ( 0.1 * static_cast<double>(time_slider) * static_cast<double>(duration) );
}
// segments behavior
float time_segment_begin = 0.f;
// float time_segment_end = 0.f;
if (right_mouse_press) {
time_segment_begin = 0.f;
}
//
// THIRD RENDER
//
// Render the bounding box
const ImU32 frame_col = ImGui::GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
ImGui::RenderFrame(bbox.Min, bbox.Max, frame_col, true, style.FrameRounding);
// by default, put a tick mark at every frame step and a large mark every second
guint64 tick_step = step;
guint64 large_tick_step = SECOND;
// how many pixels to represent one frame step?
float tick_step_pixels = timeline_bbox.GetWidth() * step_;
// while there is less than 3 pixels between two tick marks (or at last optimal tick mark)
for ( int i=0; i<10 && tick_step_pixels < 3.f; ++i )
{
// try to use the optimal tick marks pre-defined
tick_step = optimal_tick_marks[i];
large_tick_step = optimal_tick_marks[i+1];
tick_step_pixels = timeline_bbox.GetWidth() * static_cast<float> ( static_cast<double>(tick_step) / static_cast<double>(duration) );
}
// render the tick marks along TIMELINE
ImU32 color_in = ImGui::GetColorU32( style.Colors[ImGuiCol_Text] );
ImU32 color_out = ImGui::GetColorU32( style.Colors[ImGuiCol_TextDisabled] );
ImU32 color = color_in;
pos = timeline_bbox.GetTL();
guint64 tick = 0;
float tick_percent = 0.f;
std::list< std::pair<guint64, guint64> >::iterator it = segments.begin();
while ( tick < duration)
{
// large tick mark every large tick
float tick_length = !(tick%large_tick_step) ? fontsize : style.FramePadding.y;
// different colors for inside and outside [begin end] of segments
if ( it != segments.end() ) {
if ( tick < it->first )
color = color_out;
else if ( tick > it->second ){
color = color_out;
it ++;
}
else
color = color_in;
}
// draw a tick mark each step
window->DrawList->AddLine( pos, pos + ImVec2(0.f, tick_length), color);
// next tick
tick += tick_step;
tick_percent = static_cast<float> ( static_cast<double>(tick) / static_cast<double>(duration) );
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
}
// loop over segments and EMPTY the list
// ticks for segments begin & end
for (std::list< std::pair<guint64, guint64> >::iterator s = segments.begin(); s != segments.end(); s++){
tick_percent = static_cast<float> ( static_cast<double>(s->first) / static_cast<double>(duration) );
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
window->DrawList->AddLine( pos, pos + ImVec2(0.f, timeline_bbox.GetHeight()), color_in);
tick_percent = static_cast<float> ( static_cast<double>(s->second) / static_cast<double>(duration) );
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
window->DrawList->AddLine( pos, pos + ImVec2(0.f, timeline_bbox.GetHeight()), color_in);
}
segments.clear();
// tick EOF
window->DrawList->AddLine( timeline_bbox.GetTR(), timeline_bbox.GetTR() + ImVec2(0.f, fontsize), color_in);
// render text : duration and current time
char overlay_buf[24];
ImVec2 overlay_size = ImVec2(0.f, 0.f);
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%s", GstToolkit::time_to_string(duration).c_str());
overlay_size = ImGui::CalcTextSize(overlay_buf, NULL);
overlay_size += ImVec2(3.f, 3.f);
if (overlay_size.x > 0.0f)
ImGui::RenderTextClipped( bbox.GetBR() - overlay_size, bbox.Max, overlay_buf, NULL, &overlay_size);
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%s", GstToolkit::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);
// draw slider grab handle
if (grab_slider_bb.Max.x > grab_slider_bb.Min.x) {
window->DrawList->AddRectFilled(grab_slider_bb.Min, grab_slider_bb.Max, grab_slider_color, style.GrabRounding);
}
// draw the cursor
color = ImGui::GetColorU32(style.Colors[ImGuiCol_SliderGrab]);
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width, 2.f);
ImGui::RenderArrow(window->DrawList, pos, color, ImGuiDir_Up, cursor_scale);
return left_mouse_press;
}
void ImGuiToolkit::Bar(float value, float in, float out, float min, float max, const char* title, bool expand) void ImGuiToolkit::Bar(float value, float in, float out, float min, float max, const char* title, bool expand)
{ {
ImGuiWindow* window = ImGui::GetCurrentWindow(); ImGuiWindow* window = ImGui::GetCurrentWindow();

View File

@@ -27,11 +27,8 @@ namespace ImGuiToolkit
// utility sliders // utility sliders
void Bar (float value, float in, float out, float min, float max, const char* title, bool expand); void Bar (float value, float in, float out, float min, float max, const char* title, bool expand);
bool TimelineSlider (const char* label, guint64 *time, guint64 duration, guint64 step, float scale = 1.f); bool TimelineSlider (const char* label, guint64 *time, guint64 duration, guint64 step, const float width);
bool TimelineSliderEdit (const char* label, guint64 *time, guint64 duration, guint64 step, bool InvisibleSliderInt(const char* label, uint *index, int min, int max, const ImVec2 size);
std::list<std::pair<guint64, guint64> >& segments);
bool InvisibleSliderInt(const char* label, uint *index, int min, int max, ImVec2 size);
// fonts from ressources 'fonts/' // fonts from ressources 'fonts/'
typedef enum { typedef enum {

View File

@@ -297,7 +297,7 @@ void ImGuiVisitor::visit (Source& s)
void ImGuiVisitor::visit (MediaSource& s) void ImGuiVisitor::visit (MediaSource& s)
{ {
if ( s.mediaplayer()->duration() == GST_CLOCK_TIME_NONE) { if ( s.mediaplayer()->isImage() ) {
ImGuiToolkit::Icon(2,9); ImGuiToolkit::Icon(2,9);
ImGui::SameLine(0, 10); ImGui::SameLine(0, 10);
ImGui::Text("Image File"); ImGui::Text("Image File");

View File

@@ -42,9 +42,9 @@ MediaPlayer::MediaPlayer(string name) : id_(name)
width_ = par_width_ = 640; width_ = par_width_ = 640;
height_ = 480; height_ = 480;
position_ = GST_CLOCK_TIME_NONE; position_ = GST_CLOCK_TIME_NONE;
duration_ = GST_CLOCK_TIME_NONE; // duration_ = GST_CLOCK_TIME_NONE;
start_position_ = GST_CLOCK_TIME_NONE; // start_position_ = GST_CLOCK_TIME_NONE;
frame_duration_ = GST_CLOCK_TIME_NONE; // frame_duration_ = GST_CLOCK_TIME_NONE;
desired_state_ = GST_STATE_PAUSED; desired_state_ = GST_STATE_PAUSED;
loop_ = LoopMode::LOOP_REWIND; loop_ = LoopMode::LOOP_REWIND;
@@ -82,14 +82,16 @@ void MediaPlayer::open(string path)
{ {
// set uri to open // set uri to open
filename_ = path; filename_ = path;
uri_ = string( gst_uri_construct("file", path.c_str()) ); gchar *uritmp = gst_filename_to_uri(path.c_str(), NULL);
uri_ = string( uritmp );
g_free(uritmp);
// reset // reset
ready_ = false; ready_ = false;
/* Instantiate the Discoverer */ /* Instantiate the Discoverer */
GError *err = NULL; GError *err = NULL;
discoverer_ = gst_discoverer_new (5 * GST_SECOND, &err); discoverer_ = gst_discoverer_new (45 * GST_SECOND, &err);
if (!discoverer_) { if (!discoverer_) {
Log::Warning("MediaPlayer Error creating discoverer instance: %s\n", err->message); Log::Warning("MediaPlayer Error creating discoverer instance: %s\n", err->message);
g_clear_error (&err); g_clear_error (&err);
@@ -169,19 +171,26 @@ void MediaPlayer::execute_open()
gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 100); gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 100);
gst_app_sink_set_drop (GST_APP_SINK(sink), true); gst_app_sink_set_drop (GST_APP_SINK(sink), true);
// set the callbacks // // set the callbacks
GstAppSinkCallbacks callbacks; // GstAppSinkCallbacks callbacks;
callbacks.new_preroll = callback_new_preroll; // callbacks.new_preroll = callback_new_preroll;
if (isimage_) { // if (isimage_) {
callbacks.eos = NULL; // callbacks.eos = NULL;
callbacks.new_sample = NULL; // callbacks.new_sample = NULL;
} // }
else { // else {
callbacks.eos = callback_end_of_stream; // callbacks.eos = callback_end_of_stream;
callbacks.new_sample = callback_new_sample; // callbacks.new_sample = callback_new_sample;
} // }
gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL); // gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL);
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false); // gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false);
// connect callbacks
g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_new_sample), this);
g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_new_preroll), this);
g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_stream), this);
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), true);
// done with ref to sink // done with ref to sink
gst_object_unref (sink); gst_object_unref (sink);
@@ -204,13 +213,18 @@ void MediaPlayer::execute_open()
return; return;
} }
// create & init segment array
// all good // all good
Log::Info("MediaPlayer %s Open %s (%s %d x %d)", id_.c_str(), uri_.c_str(), codec_name_.c_str(), width_, height_); Log::Info("MediaPlayer %s Open %s (%s %d x %d)", id_.c_str(), uri_.c_str(), codec_name_.c_str(), width_, height_);
ready_ = true; ready_ = true;
// // in case discoverer failed to get duration
// if (timeline.end() == GST_CLOCK_TIME_NONE) {
// gint64 d = GST_CLOCK_TIME_NONE;
// if ( gst_element_query_duration(pipeline_, GST_FORMAT_TIME, &d) )
// timeline.setEnd(d);
// }
// register media player
MediaPlayer::registered_.push_back(this); MediaPlayer::registered_.push_back(this);
} }
@@ -233,62 +247,51 @@ void MediaPlayer::close()
discoverer_ = nullptr; discoverer_ = nullptr;
} }
// not openned? nothing to close!
if (!ready_) if (!ready_)
return; return;
// un-ready the media player
ready_ = false;
// reset timeline
timeline.reset();
// clean up GST // clean up GST
if (pipeline_ != nullptr) { if (pipeline_ != nullptr) {
gst_element_set_state (pipeline_, GST_STATE_NULL); GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_NULL);
if (ret == GST_STATE_CHANGE_ASYNC) {
GstState state;
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
}
gst_object_unref (pipeline_); gst_object_unref (pipeline_);
pipeline_ = nullptr; pipeline_ = nullptr;
} }
// cleanup eventual remaining frame related memory // // cleanup eventual remaining frame related memory
for(guint i = 0; i < N_VFRAME; i++){ // for(guint i = 0; i < N_VFRAME; i++){
frame_[i].access.lock(); //// frame_[i].access.lock();
if (frame_[i].vframe.buffer) // if (frame_[i].vframe.buffer)
gst_video_frame_unmap(&frame_[i].vframe); // gst_video_frame_unmap(&frame_[i].vframe);
frame_[i].status = EMPTY; // frame_[i].status = EMPTY;
frame_[i].access.unlock(); //// frame_[i].access.unlock();
} // }
// cleanup opengl texture // cleanup opengl texture
if (textureindex_) if (textureindex_)
glDeleteTextures(1, &textureindex_); glDeleteTextures(1, &textureindex_);
textureindex_ = 0; textureindex_ = 0;
// delete picture buffer // cleanup picture buffer
if (pbo_[0]) if (pbo_[0])
glDeleteBuffers(2, pbo_); glDeleteBuffers(2, pbo_);
pbo_size_ = 0; pbo_size_ = 0;
// un-ready the media player // unregister media player
ready_ = false;
MediaPlayer::registered_.remove(this); MediaPlayer::registered_.remove(this);
} }
GstClockTime MediaPlayer::duration()
{
// cannot play an image
if (isimage_)
return GST_CLOCK_TIME_NONE;
if (duration_ == GST_CLOCK_TIME_NONE && pipeline_ != nullptr) {
gint64 d = GST_CLOCK_TIME_NONE;
if ( gst_element_query_duration(pipeline_, GST_FORMAT_TIME, &d) )
duration_ = d;
}
return duration_;
}
GstClockTime MediaPlayer::frameDuration()
{
return frame_duration_;
}
guint MediaPlayer::width() const guint MediaPlayer::width() const
{ {
return width_; return width_;
@@ -314,7 +317,7 @@ GstClockTime MediaPlayer::position()
pos = p; pos = p;
} }
return pos - start_position_; return pos - timeline.start();
} }
void MediaPlayer::enable(bool on) void MediaPlayer::enable(bool on)
@@ -347,6 +350,12 @@ bool MediaPlayer::isEnabled() const
return enabled_; return enabled_;
} }
bool MediaPlayer::isImage() const
{
return isimage_;
}
void MediaPlayer::play(bool on) void MediaPlayer::play(bool on)
{ {
// cannot play an image // cannot play an image
@@ -369,7 +378,7 @@ void MediaPlayer::play(bool on)
// requesting to play, but stopped at end of stream : rewind first ! // requesting to play, but stopped at end of stream : rewind first !
if ( desired_state_ == GST_STATE_PLAYING) { if ( desired_state_ == GST_STATE_PLAYING) {
if ( ( rate_>0.0 ? duration_ - position() : position() ) < 2 * frame_duration_ ) if ( ( rate_>0.0 ? timeline.end() - position() : position() ) < 2 * timeline.step() )
rewind(); rewind();
} }
@@ -429,7 +438,7 @@ void MediaPlayer::rewind()
execute_seek_command(0); execute_seek_command(0);
else else
// playing backward, loop to end // playing backward, loop to end
execute_seek_command(duration_ - frame_duration_); execute_seek_command(timeline.end() - timeline.step());
} }
@@ -439,7 +448,7 @@ void MediaPlayer::step()
if (!enabled_ || isPlaying()) if (!enabled_ || isPlaying())
return; return;
if ( position_ == ( rate_ < 0.0 ? start_position_ : duration() ) ) if ( position_ == ( rate_ < 0.0 ? timeline.start() : timeline.end() ) )
rewind(); rewind();
// step // step
@@ -452,7 +461,9 @@ void MediaPlayer::seek(GstClockTime pos)
return; return;
// apply seek // apply seek
GstClockTime target = CLAMP(pos, 0, duration_); GstClockTime target = CLAMP(pos, 0, timeline.end());
// GstClockTime target = CLAMP(pos, timeline.start(), timeline.end());
// TODO: confirm that PTS are not possibly ZERO (use of start() is neceessary)
execute_seek_command(target); execute_seek_command(target);
} }
@@ -647,14 +658,20 @@ void MediaPlayer::update()
// unkock frame after reading it // unkock frame after reading it
frame_[read_index].access.unlock(); frame_[read_index].access.unlock();
if (isimage_)
return;
// manage loop mode // manage loop mode
if (need_loop && !isimage_) { if (need_loop) {
execute_loop_command(); execute_loop_command();
} }
// manage timeline // manage timeline
// if (position_!=GST_CLOCK_TIME_NONE) { // TimeInterval gap;
// if (position_ != GST_CLOCK_TIME_NONE && timeline.gapAt(position_, gap)) {
// seek( (rate_>0.f) ? gap.end : gap.begin);
// // TODO : manage loop when jumping out of timeline
// } // }
@@ -688,7 +705,7 @@ void MediaPlayer::execute_seek_command(GstClockTime target)
// create seek event with current position (rate changed ?) // create seek event with current position (rate changed ?)
seek_pos = position(); seek_pos = position();
// target is given but useless // target is given but useless
else if ( ABS_DIFF(target, position()) < frame_duration_) { else if ( ABS_DIFF(target, position()) < timeline.step()) {
// ignore request // ignore request
return; return;
} }
@@ -699,6 +716,24 @@ void MediaPlayer::execute_seek_command(GstClockTime target)
if ( ABS(rate_) > 1.0 ) if ( ABS(rate_) > 1.0 )
seek_flags |= GST_SEEK_FLAG_TRICKMODE; seek_flags |= GST_SEEK_FLAG_TRICKMODE;
// bool ret = false;
// if (rate_ > 0) {
// ret = gst_element_seek (pipeline_, rate_, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
// GST_SEEK_TYPE_SET, seek_pos,
// GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
// }
// else {
// ret = gst_element_seek (pipeline_, rate_, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
// GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE,
// GST_SEEK_TYPE_SET, seek_pos);
// }
// if (!ret)
// Log::Warning("MediaPlayer %s Seek failed", gst_element_get_name(pipeline_));
// create seek event depending on direction // create seek event depending on direction
GstEvent *seek_event = nullptr; GstEvent *seek_event = nullptr;
if (rate_ > 0) { if (rate_ > 0) {
@@ -712,12 +747,13 @@ void MediaPlayer::execute_seek_command(GstClockTime target)
// Send the event (ASYNC) // Send the event (ASYNC)
if (seek_event && !gst_element_send_event(pipeline_, seek_event) ) if (seek_event && !gst_element_send_event(pipeline_, seek_event) )
Log::Warning("MediaPlayer %s Seek failed", gst_element_get_name(pipeline_)); Log::Warning("MediaPlayer %s Seek failed", id_.c_str());
else { else {
#ifdef MEDIA_PLAYER_DEBUG #ifdef MEDIA_PLAYER_DEBUG
Log::Info("MediaPlayer %s Seek %ld %f", gst_element_get_name(pipeline_), seek_pos, rate_); Log::Info("MediaPlayer %s Seek %ld %f", id_.c_str(), seek_pos, rate_);
#endif #endif
} }
} }
void MediaPlayer::setPlaySpeed(double s) void MediaPlayer::setPlaySpeed(double s)
@@ -803,13 +839,13 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, MediaPlayer::FrameStatus status)
frame_[write_index_].position = buf->pts; frame_[write_index_].position = buf->pts;
// set the start position (i.e. pts of first frame we got) // set the start position (i.e. pts of first frame we got)
if (start_position_ == GST_CLOCK_TIME_NONE) if (timeline.start() == GST_CLOCK_TIME_NONE)
start_position_ = buf->pts; timeline.setStart(buf->pts);
} }
} }
// give a position to EOS // give a position to EOS
else { else {
frame_[write_index_].position = rate_ > 0.0 ? duration() : start_position_; frame_[write_index_].position = rate_ > 0.0 ? timeline.end() : timeline.start();
} }
// unlock access to frame // unlock access to frame
@@ -833,7 +869,7 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, MediaPlayer::FrameStatus status)
void MediaPlayer::callback_end_of_stream (GstAppSink *, gpointer p) void MediaPlayer::callback_end_of_stream (GstAppSink *, gpointer p)
{ {
MediaPlayer *m = (MediaPlayer *)p; MediaPlayer *m = (MediaPlayer *)p;
if (m) { if (m && m->ready_) {
m->fill_frame(NULL, MediaPlayer::EOS); m->fill_frame(NULL, MediaPlayer::EOS);
} }
} }
@@ -849,22 +885,22 @@ GstFlowReturn MediaPlayer::callback_new_preroll (GstAppSink *sink, gpointer p)
if (sample != NULL) { if (sample != NULL) {
// get buffer from sample // get buffer from sample
GstBuffer *buf = gst_buffer_ref ( gst_sample_get_buffer (sample) ); GstBuffer *buf = /*gst_buffer_ref */( gst_sample_get_buffer (sample) );
MediaPlayer *m = (MediaPlayer *)p; MediaPlayer *m = (MediaPlayer *)p;
if (m) { if (m && m->ready_) {
// fill frame from buffer // fill frame from buffer
if ( !m->fill_frame(buf, MediaPlayer::PREROLL) ) if ( !m->fill_frame(buf, MediaPlayer::PREROLL) )
ret = GST_FLOW_ERROR; ret = GST_FLOW_ERROR;
// loop negative rate: emulate an EOS // loop negative rate: emulate an EOS
if (m->playSpeed() < 0.f && buf->pts == m->start_position_) { if (m->playSpeed() < 0.f && buf->pts <= m->timeline.start()) {
m->fill_frame(NULL, MediaPlayer::EOS); m->fill_frame(NULL, MediaPlayer::EOS);
} }
} }
// free buffers // free buffers
gst_buffer_unref (buf); // gst_buffer_unref (buf);
gst_sample_unref (sample); gst_sample_unref (sample);
} }
else else
@@ -884,29 +920,29 @@ GstFlowReturn MediaPlayer::callback_new_sample (GstAppSink *sink, gpointer p)
if (sample != NULL && !gst_app_sink_is_eos (sink)) { if (sample != NULL && !gst_app_sink_is_eos (sink)) {
// get buffer from sample // get buffer from sample
GstBuffer *buf = gst_buffer_ref ( gst_sample_get_buffer (sample) ); GstBuffer *buf = /*gst_buffer_ref*/ ( gst_sample_get_buffer (sample) );
MediaPlayer *m = (MediaPlayer *)p; MediaPlayer *m = (MediaPlayer *)p;
if (m) { if (m && m->ready_) {
// fill frame with buffer // fill frame with buffer
if ( !m->fill_frame(buf, MediaPlayer::SAMPLE) ) if ( !m->fill_frame(buf, MediaPlayer::SAMPLE) )
ret = GST_FLOW_ERROR; ret = GST_FLOW_ERROR;
// loop negative rate: emulate an EOS // loop negative rate: emulate an EOS
if (m->playSpeed() < 0.f && buf->pts == m->start_position_) { if (m->playSpeed() < 0.f && buf->pts <= m->timeline.start()) {
m->fill_frame(NULL, MediaPlayer::EOS); m->fill_frame(NULL, MediaPlayer::EOS);
} }
} }
// free buffer & sample // free buffer & sample
gst_buffer_unref (buf); // gst_buffer_unref (buf);
gst_sample_unref (sample); gst_sample_unref (sample);
} }
else // else
ret = GST_FLOW_FLUSHING; // ret = GST_FLOW_FLUSHING;
return ret; return ret;
} }
@@ -967,7 +1003,7 @@ void MediaPlayer::callback_discoverer_process (GstDiscoverer *discoverer, GstDis
m->par_width_ = (m->width_ * parn) / pard; m->par_width_ = (m->width_ * parn) / pard;
// if its a video, it duration, framerate, etc. // if its a video, it duration, framerate, etc.
if ( !m->isimage_ ) { if ( !m->isimage_ ) {
m->duration_ = gst_discoverer_info_get_duration (info); m->timeline.setEnd( gst_discoverer_info_get_duration (info) );
m->seekable_ = gst_discoverer_info_get_seekable (info); m->seekable_ = gst_discoverer_info_get_seekable (info);
guint frn = gst_discoverer_video_info_get_framerate_num(vinfo); guint frn = gst_discoverer_video_info_get_framerate_num(vinfo);
guint frd = gst_discoverer_video_info_get_framerate_denom(vinfo); guint frd = gst_discoverer_video_info_get_framerate_denom(vinfo);
@@ -976,20 +1012,22 @@ void MediaPlayer::callback_discoverer_process (GstDiscoverer *discoverer, GstDis
frd = 1; frd = 1;
} }
m->framerate_ = static_cast<double>(frn) / static_cast<double>(frd); m->framerate_ = static_cast<double>(frn) / static_cast<double>(frd);
m->frame_duration_ = (GST_SECOND * static_cast<guint64>(frd)) / (static_cast<guint64>(frn)); m->timeline.setStep( (GST_SECOND * static_cast<guint64>(frd)) / (static_cast<guint64>(frn)) );
} }
// try to fill-in the codec information // try to fill-in the codec information
GstCaps *caps = gst_discoverer_stream_info_get_caps (tmpinf); GstCaps *caps = gst_discoverer_stream_info_get_caps (tmpinf);
if (caps) { if (caps) {
m->codec_name_ = std::string( gst_pb_utils_get_codec_description(caps) ) + " "; gchar *codecstring = gst_pb_utils_get_codec_description(caps);
m->codec_name_ = std::string( codecstring ) + " ";
g_free(codecstring);
gst_caps_unref (caps); gst_caps_unref (caps);
} }
const GstTagList *tags = gst_discoverer_stream_info_get_tags(tmpinf); // const GstTagList *tags = gst_discoverer_stream_info_get_tags(tmpinf);
if ( tags ) { // if ( tags ) {
gchar *container = NULL; // gchar *container = NULL;
gst_tag_list_get_string (tags, GST_TAG_CONTAINER_FORMAT, &container); // gst_tag_list_get_string (tags, GST_TAG_CONTAINER_FORMAT, &container);
if (container) m->codec_name_ += std::string(container); // if (container) m->codec_name_ += std::string(container);
} // }
// exit loop // exit loop
foundvideostream = true; foundvideostream = true;
} }

View File

@@ -74,6 +74,10 @@ public:
* True if enabled * True if enabled
* */ * */
bool isEnabled() const; bool isEnabled() const;
/**
* True if its an image
* */
bool isImage() const;
/** /**
* Pause / Play * Pause / Play
* Can play backward if play speed is negative * Can play backward if play speed is negative
@@ -136,13 +140,13 @@ public:
* */ * */
GstClockTime position(); GstClockTime position();
/** /**
* Get total duration time * @brief timeline contains all info on timing:
* */ * - start position : timeline.start()
GstClockTime duration(); * - end position : timeline.end()
/** * - duration : timeline.duration()
* Get duration of one frame * - frame duration : timeline.step()
* */ */
GstClockTime frameDuration(); Timeline timeline;
/** /**
* Get framerate of the media * Get framerate of the media
* */ * */
@@ -195,9 +199,11 @@ private:
guint par_width_; // width to match pixel aspect ratio guint par_width_; // width to match pixel aspect ratio
guint bitrate_; guint bitrate_;
GstClockTime position_; GstClockTime position_;
GstClockTime start_position_;
GstClockTime duration_; // GstClockTime start_position_;
GstClockTime frame_duration_; // GstClockTime duration_;
// GstClockTime frame_duration_;
gdouble rate_; gdouble rate_;
LoopMode loop_; LoopMode loop_;
gdouble framerate_; gdouble framerate_;
@@ -276,6 +282,7 @@ private:
static void callback_end_of_stream (GstAppSink *, gpointer); static void callback_end_of_stream (GstAppSink *, gpointer);
static GstFlowReturn callback_new_preroll (GstAppSink *, gpointer ); static GstFlowReturn callback_new_preroll (GstAppSink *, gpointer );
static GstFlowReturn callback_new_sample (GstAppSink *, gpointer); static GstFlowReturn callback_new_sample (GstAppSink *, gpointer);
static void callback_discoverer_process (GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, MediaPlayer *m); static void callback_discoverer_process (GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, MediaPlayer *m);
static void callback_discoverer_finished(GstDiscoverer *discoverer, MediaPlayer *m); static void callback_discoverer_finished(GstDiscoverer *discoverer, MediaPlayer *m);

View File

@@ -86,7 +86,7 @@ void MediaSource::init()
attach(renderbuffer); attach(renderbuffer);
// icon in mixing view // icon in mixing view
if (mediaplayer_->duration() == GST_CLOCK_TIME_NONE) { if (mediaplayer_->isImage()) {
overlays_[View::MIXING]->attach( new Symbol(Symbol::IMAGE, glm::vec3(0.8f, 0.8f, 0.01f)) ); overlays_[View::MIXING]->attach( new Symbol(Symbol::IMAGE, glm::vec3(0.8f, 0.8f, 0.01f)) );
overlays_[View::LAYER]->attach( new Symbol(Symbol::IMAGE, glm::vec3(0.8f, 0.8f, 0.01f)) ); overlays_[View::LAYER]->attach( new Symbol(Symbol::IMAGE, glm::vec3(0.8f, 0.8f, 0.01f)) );
} }

View File

@@ -38,6 +38,11 @@ Session::~Session()
// erase this source from the list // erase this source from the list
it = deleteSource(*it); it = deleteSource(*it);
} }
delete config_[View::RENDERING];
delete config_[View::GEOMETRY];
delete config_[View::LAYER];
delete config_[View::MIXING];
} }
void Session::setActive (bool on) void Session::setActive (bool on)

View File

@@ -44,6 +44,11 @@ SessionCreator::SessionCreator(Session *session): Visitor(), session_(session)
} }
SessionCreator::~SessionCreator()
{
delete xmlDoc_;
}
bool SessionCreator::load(const std::string& filename) bool SessionCreator::load(const std::string& filename)
{ {
XMLError eResult = xmlDoc_->LoadFile(filename.c_str()); XMLError eResult = xmlDoc_->LoadFile(filename.c_str());
@@ -179,6 +184,20 @@ void SessionCreator::visit(MediaPlayer &n)
{ {
XMLElement* mediaplayerNode = xmlCurrent_->FirstChildElement("MediaPlayer"); XMLElement* mediaplayerNode = xmlCurrent_->FirstChildElement("MediaPlayer");
if (mediaplayerNode) { if (mediaplayerNode) {
// timeline
XMLElement *gapselement = mediaplayerNode->FirstChildElement("Gaps");
if (gapselement) {
XMLElement* gap = gapselement->FirstChildElement("Interval");
for( ; gap ; gap = gap->NextSiblingElement())
{
GstClockTime a = GST_CLOCK_TIME_NONE;
GstClockTime b = GST_CLOCK_TIME_NONE;
gap->QueryUnsigned64Attribute("begin", &a);
gap->QueryUnsigned64Attribute("end", &b);
n.timeline.addGap( a, b );
}
}
// playing properties
double speed = 1.0; double speed = 1.0;
mediaplayerNode->QueryDoubleAttribute("speed", &speed); mediaplayerNode->QueryDoubleAttribute("speed", &speed);
n.setPlaySpeed(speed); n.setPlaySpeed(speed);

View File

@@ -18,6 +18,7 @@ class SessionCreator : public Visitor {
public: public:
SessionCreator(Session *session = nullptr); SessionCreator(Session *session = nullptr);
~SessionCreator();
bool load(const std::string& filename); bool load(const std::string& filename);
inline Session *session() const { return session_; } inline Session *session() const { return session_; }

View File

@@ -147,7 +147,16 @@ void SessionVisitor::visit(MediaPlayer &n)
newelement->SetAttribute("loop", (int) n.loop()); newelement->SetAttribute("loop", (int) n.loop());
newelement->SetAttribute("speed", n.playSpeed()); newelement->SetAttribute("speed", n.playSpeed());
// TODO Segments // gaps in timeline
XMLElement *gapselement = xmlDoc_->NewElement("Gaps");
std::list< std::pair<guint64, guint64> > gaps = n.timeline.gaps();
for( auto it = gaps.begin(); it!= gaps.end(); it++) {
XMLElement *g = xmlDoc_->NewElement("Interval");
g->SetAttribute("begin", (*it).first);
g->SetAttribute("end", (*it).second);
gapselement->InsertEndChild(g);
}
newelement->InsertEndChild(gapselement);
xmlCurrent_->InsertEndChild(newelement); xmlCurrent_->InsertEndChild(newelement);
} }

View File

@@ -17,71 +17,181 @@ Timeline::Timeline() : array_(nullptr)
Timeline::~Timeline() Timeline::~Timeline()
{ {
reset(); reset();
// free(array_);
} }
void Timeline::reset() void Timeline::reset()
{ {
start_ = GST_CLOCK_TIME_NONE; // reset timing
end_ = GST_CLOCK_TIME_NONE; timing_.begin = GST_CLOCK_TIME_NONE;
num_frames_ = 0; timing_.end = GST_CLOCK_TIME_NONE;
array_size_ = 0; step_ = GST_CLOCK_TIME_NONE;
// clear segment array
if (array_ != nullptr) // // clear gaps
free(array_); // gaps_.clear();
// // clear segment array
// if (array_ != nullptr)
// free(array_);
// // avoid crash by providing a valid pointer
// array_size_ = 1;
// array_ = (float *) malloc(sizeof(float));
// array_[0] = 1.f;
// need_update_ = true;
} }
void Timeline::init(GstClockTime start, GstClockTime end, GstClockTime frame_duration) void Timeline::setStart(GstClockTime start)
{ {
reset(); timing_.begin = start;
need_update_ = true;
start_ = start;
end_ = end;
num_frames_ = (size_t) end_ / (size_t) frame_duration;
array_size_ = MIN( SEGMENT_ARRAY_MAX_SIZE, num_frames_);
array_ = (float *) malloc(array_size_ * sizeof(float));
for (int i = 0; i < array_size_; ++i)
array_[i] = 1.f;
Log::Info("%d frames in timeline", array_size_);
} }
bool Timeline::addPlaySegment(GstClockTime begin, GstClockTime end) void Timeline::setEnd(GstClockTime end)
{ {
return addPlaySegment( MediaSegment(begin, end) ); timing_.end = end;
need_update_ = true;
} }
bool Timeline::addPlaySegment(MediaSegment s) void Timeline::setStep(GstClockTime dt)
{ {
if ( s.is_valid() ) step_ = dt;
return segments_.insert(s).second; need_update_ = true;
}
void Timeline::updateGapsFromArray()
{
// if (need_update_)
// return;
// // reset gaps
// gaps_.clear();
// // loop over the array to detect gaps
// float status = 1.f;
// GstClockTime begin_gap = GST_CLOCK_TIME_NONE;
// for (size_t i = 0; i < array_size_; ++i) {
// // 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_;
// // change from 1.f to 0.f : begin of a gap
// if (status) {
// begin_gap = t;
// }
// // change from 0.f to 1.f : end of a gap
// else {
// addGap( begin_gap, t );
// begin_gap = GST_CLOCK_TIME_NONE;
// }
// // swap
// status = array_[i];
// }
// }
// // 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 );
}
void Timeline::updateArrayFromGaps()
{
// if (step_ != GST_CLOCK_TIME_NONE && timing_.is_valid()) {
// // clear segment array
// if (array_ != nullptr)
// free(array_);
// array_size_ = MIN( SEGMENT_ARRAY_MAX_SIZE, duration() / step_);
// array_ = (float *) malloc(array_size_ * sizeof(float));
// for (int i = 0; i < array_size_; ++i)
// array_[i] = 1.f;
// Log::Info("%d frames in timeline", array_size_);
// // fill the array from gaps
// // NB: this is the less efficient algorithm possible! but we should not keep arrays anyway
// // TODO : implement an ImGui widget to plot a timeline instead of an array
// TimeInterval gap;
// for (size_t i = 0; i < array_size_; ++i) {
// GstClockTime t = (timing_.duration() * i) / array_size_;
// array_[i] = gapAt(t, gap) ? 0.f : 1.f;
// }
// need_update_ = false;
// }
}
size_t Timeline::numGaps()
{
return gaps_.size();
}
bool Timeline::gapAt(const GstClockTime t, TimeInterval &gap)
{
// TimeIntervalSet::const_iterator g = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
// if ( g != gaps_.end() ) {
// gap = (*g);
// return true;
// }
return false; return false;
} }
bool Timeline::removeAllPlaySegmentOverlap(MediaSegment s) bool Timeline::addGap(GstClockTime begin, GstClockTime end)
{ {
bool ret = removePlaySegmentAt(s.begin); return addGap( TimeInterval(begin, end) );
return removePlaySegmentAt(s.end) || ret;
} }
bool Timeline::removePlaySegmentAt(GstClockTime t) bool Timeline::addGap(TimeInterval s)
{ {
MediaSegmentSet::const_iterator s = std::find_if(segments_.begin(), segments_.end(), containsTime(t)); // if ( s.is_valid() ) {
// need_update_ = true;
if ( s != segments_.end() ) { // return gaps_.insert(s).second;
segments_.erase(s); // }
return true;
}
return false; return false;
} }
std::list< std::pair<guint64, guint64> > Timeline::getPlaySegments() const bool Timeline::removeGaptAt(GstClockTime t)
{
// TimeIntervalSet::const_iterator s = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
// if ( s != gaps_.end() ) {
// gaps_.erase(s);
// need_update_ = true;
// return true;
// }
return false;
}
void Timeline::clearGaps()
{
gaps_.clear();
need_update_ = true;
}
std::list< std::pair<guint64, guint64> > Timeline::gaps() const
{ {
std::list< std::pair<guint64, guint64> > ret; std::list< std::pair<guint64, guint64> > ret;
for (MediaSegmentSet::iterator it = segments_.begin(); it != segments_.end(); it++) for (TimeIntervalSet::iterator it = gaps_.begin(); it != gaps_.end(); it++)
ret.push_back( std::make_pair( it->begin, it->end ) ); ret.push_back( std::make_pair( it->begin, it->end ) );
return ret; return ret;
} }
float *Timeline::array()
{
// if (need_update_)
// updateArrayFromGaps();
return array_;
}
size_t Timeline::arraySize()
{
// if (need_update_)
// updateArrayFromGaps();
return array_size_;
}

View File

@@ -8,18 +8,18 @@
#include <gst/pbutils/pbutils.h> #include <gst/pbutils/pbutils.h>
struct MediaSegment struct TimeInterval
{ {
GstClockTime begin; GstClockTime begin;
GstClockTime end; GstClockTime end;
MediaSegment() TimeInterval()
{ {
begin = GST_CLOCK_TIME_NONE; begin = GST_CLOCK_TIME_NONE;
end = GST_CLOCK_TIME_NONE; end = GST_CLOCK_TIME_NONE;
} }
MediaSegment(GstClockTime b, GstClockTime e) TimeInterval(GstClockTime b, GstClockTime e)
{ {
if ( b < e ) { if ( b < e ) {
begin = b; begin = b;
@@ -29,39 +29,55 @@ struct MediaSegment
end = GST_CLOCK_TIME_NONE; end = GST_CLOCK_TIME_NONE;
} }
} }
inline GstClockTime duration() const
{
return is_valid() ? (end - begin) : GST_CLOCK_TIME_NONE;
}
inline bool is_valid() const inline bool is_valid() const
{ {
return begin != GST_CLOCK_TIME_NONE && end != GST_CLOCK_TIME_NONE && begin < end; return begin != GST_CLOCK_TIME_NONE && end != GST_CLOCK_TIME_NONE && begin < end;
} }
inline bool operator < (const MediaSegment b) const inline bool operator < (const TimeInterval b) const
{ {
return (this->is_valid() && b.is_valid() && this->end < b.begin); return (this->is_valid() && b.is_valid() && this->end < b.begin);
} }
inline bool operator == (const MediaSegment b) const inline bool operator == (const TimeInterval b) const
{ {
return (this->begin == b.begin && this->end == b.end); return (this->begin == b.begin && this->end == b.end);
} }
inline bool operator != (const MediaSegment b) const inline bool operator != (const TimeInterval b) const
{ {
return (this->begin != b.begin || this->end != b.end); return (this->begin != b.begin || this->end != b.end);
} }
inline TimeInterval& operator = (const TimeInterval& b)
{
if (this != &b) {
this->begin = b.begin;
this->end = b.end;
}
return *this;
}
inline bool includes(const GstClockTime t) const
{
return (is_valid() && t != GST_CLOCK_TIME_NONE && t > this->begin && t < this->end);
}
}; };
struct containsTime: public std::unary_function<MediaSegment, bool> struct includesTime: public std::unary_function<TimeInterval, bool>
{ {
inline bool operator()(const MediaSegment s) const inline bool operator()(const TimeInterval s) const
{ {
return ( s.is_valid() && _t > s.begin && _t < s.end ); return s.includes(_t);
} }
containsTime(GstClockTime t) : _t(t) { } includesTime(GstClockTime t) : _t(t) { }
private: private:
GstClockTime _t; GstClockTime _t;
}; };
typedef std::set<MediaSegment> MediaSegmentSet; typedef std::set<TimeInterval> TimeIntervalSet;
class Timeline class Timeline
@@ -71,27 +87,52 @@ public:
~Timeline(); ~Timeline();
void reset(); void reset();
void init(GstClockTime start, GstClockTime end, GstClockTime frame_duration);
bool addPlaySegment(GstClockTime begin, GstClockTime end); // global properties of the timeline
bool addPlaySegment(MediaSegment s); // timeline is invalid untill all 3 are set
bool removePlaySegmentAt(GstClockTime t); void setStart(GstClockTime start);
bool removeAllPlaySegmentOverlap(MediaSegment s); void setEnd(GstClockTime end);
std::list< std::pair<guint64, guint64> > getPlaySegments() const; void setStep(GstClockTime dt);
inline float *Array() { return array_; } // get properties
inline size_t ArraySize() { return array_size_; } inline GstClockTime start() const { return timing_.begin; }
inline GstClockTime end() const { return timing_.end; }
inline GstClockTime step() const { return step_; }
inline GstClockTime duration() const { return timing_.duration(); }
// Add / remove gaps in the timeline
bool addGap(TimeInterval s);
bool addGap(GstClockTime begin, GstClockTime end);
bool removeGaptAt(GstClockTime t);
void clearGaps();
// get gaps
size_t numGaps();
bool gapAt(const GstClockTime t, TimeInterval &gap);
std::list< std::pair<guint64, guint64> > gaps() const;
// direct access to the array representation of the timeline
// TODO : implement an ImGui widget to plot a timeline instead of an array
float *array();
size_t arraySize();
// synchronize data structures
void updateGapsFromArray();
void updateArrayFromGaps();
private: private:
GstClockTime start_; // global information on the timeline
GstClockTime end_; TimeInterval timing_;
size_t num_frames_; GstClockTime step_;
// main data structure containing list of gaps in the timeline
TimeIntervalSet gaps_;
// supplementary data structure needed to display and edit the timeline
bool need_update_;
void init_array();
float *array_; float *array_;
size_t array_size_; size_t array_size_;
MediaSegmentSet segments_;
MediaSegmentSet::iterator current_segment_;
}; };

View File

@@ -987,7 +987,7 @@ void UserInterface::RenderPreview()
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())
{ {
ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddRectFilled(draw_pos, ImVec2(draw_pos.x + width, draw_pos.y + ImGui::GetTextLineHeightWithSpacing()), IM_COL32(5, 5, 5, 100)); draw_list->AddRectFilled(draw_pos, ImVec2(draw_pos.x + width, draw_pos.y + ImGui::GetTextLineHeightWithSpacing()), IMGUI_COLOR_OVERLAY);
ImGui::SetCursorScreenPos(draw_pos); ImGui::SetCursorScreenPos(draw_pos);
ImGui::Text(" %d x %d px, %d fps", output->width(), output->height(), int(1000.f / Mixer::manager().dt()) ); ImGui::Text(" %d x %d px, %d fps", output->width(), output->height(), int(1000.f / Mixer::manager().dt()) );
} }
@@ -1066,7 +1066,7 @@ void MediaController::Render()
// menu (no title bar) // menu (no title bar)
if (ImGui::BeginMenuBar()) if (ImGui::BeginMenuBar())
{ {
if (ImGui::BeginMenu(ICON_FA_FILM)) if (ImGui::BeginMenu(IMGUI_TITLE_MEDIAPLAYER))
{ {
ImGui::MenuItem( ICON_FA_EYE " Preview", nullptr, &Settings::application.widget.media_player_view); ImGui::MenuItem( ICON_FA_EYE " Preview", nullptr, &Settings::application.widget.media_player_view);
@@ -1127,16 +1127,18 @@ void MediaController::Render()
static float timeline_zoom = 1.f; static float timeline_zoom = 1.f;
const float width = ImGui::GetContentRegionAvail().x; const float width = ImGui::GetContentRegionAvail().x;
const float timeline_height = 2.f * (ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y); const float timeline_height = 2.f * (ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y);
const float segments_height = ImGui::GetFontSize();
const float slider_zoom_width = segments_height;
if (Settings::application.widget.media_player_view) if (Settings::application.widget.media_player_view)
{ {
// set an image height to fill the vertical space, minus the height of control bar // set an image height to fill the vertical space, minus the height of control bar
float image_height = ImGui::GetContentRegionAvail().y; float image_height = ImGui::GetContentRegionAvail().y;
if (mp_->duration() != GST_CLOCK_TIME_NONE) { if ( !mp_->isImage() ) {
// leave space for buttons, spacing and timeline // leave space for buttons and timelines
image_height -= ImGui::GetFrameHeight() + timeline_height + 3.f * ImGui::GetStyle().ItemSpacing.y; image_height -= ImGui::GetFrameHeight() + timeline_height + segments_height ;
// leave space for scrollbar // leave space for scrollbar & spacing
image_height -= ImGui::GetStyle().ScrollbarSize; image_height -= ImGui::GetStyle().ScrollbarSize + 3.f * ImGui::GetStyle().ItemSpacing.y;
} }
// display media // display media
@@ -1149,13 +1151,13 @@ void MediaController::Render()
// display media information // display media information
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
float tooltip_height = (follow_active_source_? 3.f:2.f)* ImGui::GetTextLineHeightWithSpacing(); float tooltip_height = 3.f * ImGui::GetTextLineHeightWithSpacing();
ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddRectFilled(tooltip_pos, ImVec2(tooltip_pos.x + width, tooltip_pos.y + tooltip_height), IM_COL32(5, 5, 5, 100)); draw_list->AddRectFilled(ImVec2(tooltip_pos.x - 10.f, tooltip_pos.y),
ImVec2(tooltip_pos.x + width + 10.f, tooltip_pos.y + tooltip_height), IMGUI_COLOR_OVERLAY);
ImGui::SetCursorScreenPos(tooltip_pos); ImGui::SetCursorScreenPos(tooltip_pos);
if (follow_active_source_)
ImGui::Text(" %s", mp_->filename().c_str()); ImGui::Text(" %s", mp_->filename().c_str());
ImGui::Text(" %s", mp_->codec().c_str()); ImGui::Text(" %s", mp_->codec().c_str());
if ( mp_->frameRate() > 0.f ) if ( mp_->frameRate() > 0.f )
@@ -1166,16 +1168,18 @@ void MediaController::Render()
} }
// display time // display time
if ( !mp_->isImage() ) {
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
ImGui::SetCursorPos( ImVec2(return_to_pos.x + 5, return_to_pos.y - ImGui::GetTextLineHeightWithSpacing()) ); ImGui::SetCursorPos( ImVec2(return_to_pos.x + 5, return_to_pos.y - ImGui::GetTextLineHeightWithSpacing()) );
ImGui::Text("%s", GstToolkit::time_to_string(mp_->position(), GstToolkit::TIME_STRING_FIXED).c_str()); ImGui::Text("%s", GstToolkit::time_to_string(mp_->position(), GstToolkit::TIME_STRING_FIXED).c_str());
ImGui::PopFont(); ImGui::PopFont();
}
ImGui::SetCursorPos(return_to_pos); ImGui::SetCursorPos(return_to_pos);
} }
// Control bar // Control bar
if ( mp_->isEnabled() && mp_->duration() != GST_CLOCK_TIME_NONE) { if ( mp_->isEnabled() && !mp_->isImage()) {
float spacing = ImGui::GetStyle().ItemInnerSpacing.x; float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
@@ -1195,7 +1199,7 @@ void MediaController::Render()
ImGui::PushButtonRepeat(true); ImGui::PushButtonRepeat(true);
if (ImGui::Button( mp_->playSpeed() < 0 ? ICON_FA_BACKWARD :ICON_FA_FORWARD)) if (ImGui::Button( mp_->playSpeed() < 0 ? ICON_FA_BACKWARD :ICON_FA_FORWARD))
mp_->fastForward (); mp_->jump ();
ImGui::PopButtonRepeat(); ImGui::PopButtonRepeat();
} }
else { else {
@@ -1205,7 +1209,7 @@ void MediaController::Render()
ImGui::PushButtonRepeat(true); ImGui::PushButtonRepeat(true);
if (ImGui::Button( mp_->playSpeed() < 0 ? ICON_FA_STEP_BACKWARD : ICON_FA_STEP_FORWARD)) if (ImGui::Button( mp_->playSpeed() < 0 ? ICON_FA_STEP_BACKWARD : ICON_FA_STEP_FORWARD))
mp_->seekNextFrame(); mp_->step();
ImGui::PopButtonRepeat(); ImGui::PopButtonRepeat();
} }
@@ -1224,39 +1228,85 @@ void MediaController::Render()
// ImGui::SetNextItemWidth(width - 90.0); // ImGui::SetNextItemWidth(width - 90.0);
if (ImGui::DragFloat( "##Speed", &speed, 0.01f, -10.f, 10.f, "Speed x %.1f", 2.f)) if (ImGui::DragFloat( "##Speed", &speed, 0.01f, -10.f, 10.f, "Speed x %.1f", 2.f))
mp_->setPlaySpeed( static_cast<double>(speed) ); mp_->setPlaySpeed( static_cast<double>(speed) );
// reset play to x1
// reset
ImGui::SameLine(0, spacing); ImGui::SameLine(0, spacing);
if (ImGuiToolkit::ButtonIcon(19, 15)) { if (ImGuiToolkit::ButtonIcon(11, 14)) {
timeline_zoom = 1.f;
speed = 1.f; speed = 1.f;
mp_->setPlaySpeed( static_cast<double>(speed) ); mp_->setPlaySpeed( static_cast<double>(speed) );
mp_->setLoop( MediaPlayer::LOOP_REWIND ); mp_->setLoop( MediaPlayer::LOOP_REWIND );
} }
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(3, 3)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(3.f, 3.f));
// Prepare controllibg the current media player with timeline
// float *array = mp_->timeline.array();
// size_t array_size = mp_->timeline.arraySize();
guint64 current_t = mp_->position();
guint64 seek_t = current_t;
// scrolling sub-window // scrolling sub-window
ImGui::BeginChild("##scrolling", ImGui::BeginChild("##scrolling",
ImVec2(ImGui::GetContentRegionAvail().x - 20.0, ImVec2(ImGui::GetContentRegionAvail().x - slider_zoom_width - 3.0,
timeline_height + ImGui::GetStyle().ScrollbarSize ), timeline_height + segments_height + ImGui::GetStyle().ScrollbarSize ),
false, ImGuiWindowFlags_HorizontalScrollbar); false, ImGuiWindowFlags_HorizontalScrollbar);
{
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(1.f, 1.f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.f);
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), segments_height -1);
size.x *= timeline_zoom;
// draw position when entering
// ImVec2 draw_pos = ImGui::GetCursorPos();
// // capture user input (invisible)
// uint press_index = mp_->timeline.arraySize();
// bool pressed = ImGuiToolkit::InvisibleSliderInt("##TimelinePicking", &press_index, 0, array_size-1, size);
// // behavior on action on array of segments
// static bool active = false;
// static float target_value = 0.f;
// static uint starting_index = array_size;
// if (pressed != active) {
// active = pressed;
// if (pressed) {
// starting_index = press_index;
// target_value = array[starting_index] > 0.f ? 0.f : 1.f;
// }
// else// action on released
// {
// mp_->timeline.updateGapsFromArray();
// }
// }
// if (active) {
// for (int i = MIN(starting_index, press_index); i < MAX(starting_index, press_index); ++i)
// array[i] = target_value;
// }
// // back to drawing position to draw the segments data with historgram
// ImGui::SetCursorPos(draw_pos);
// ImGui::PlotHistogram("##TimelineHistogram", array, array_size-1.f, 0, NULL, 0.0f, 1.0f, size);
// custom timeline slider // custom timeline slider
guint64 current_t = mp_->position();
guint64 seek_t = current_t;
slider_pressed_ = ImGuiToolkit::TimelineSlider("##timeline", &seek_t, slider_pressed_ = ImGuiToolkit::TimelineSlider("##timeline", &seek_t,
mp_->duration(), mp_->frameDuration(), timeline_zoom); mp_->timeline.end(), mp_->timeline.step(), size.x);
ImGui::PopStyleVar(2);
}
ImGui::EndChild(); ImGui::EndChild();
// zoom slider // zoom slider
ImGui::SameLine(); ImGui::SameLine();
ImGui::VSliderFloat("##zoom", ImVec2(17, timeline_height), &timeline_zoom, 1.0, 5.f, ""); ImGui::VSliderFloat("##TimelineZoom", ImVec2(slider_zoom_width, timeline_height + segments_height), &timeline_zoom, 1.0, 5.f, "");
ImGui::PopStyleVar(); ImGui::PopStyleVar();
// if the seek target time is different from the current position time // if the seek target time is different from the current position time
// (i.e. the difference is less than one frame) // (i.e. the difference is less than one frame)
if ( ABS_DIFF (current_t, seek_t) > mp_->frameDuration() ) { if ( ABS_DIFF (current_t, seek_t) > mp_->timeline.step() ) {
// request seek (ASYNC) // request seek (ASYNC)
mp_->seekTo(seek_t); mp_->seek(seek_t);
slider_pressed_ = false; slider_pressed_ = false;
} }
// play/stop command should be following the playing mode (buttons) // play/stop command should be following the playing mode (buttons)
@@ -1267,7 +1317,8 @@ void MediaController::Render()
// NB: The seek command performed an ASYNC state change, but // NB: The seek command performed an ASYNC state change, but
// gst_element_get_state called in isPlaying(true) will wait // gst_element_get_state called in isPlaying(true) will wait
// for the state change to complete : do not remove it! // for the state change to complete : do not remove it!
if ( mp_->isPlaying(true) != media_play ) { // TODO : DO NOT ask for status every frame : high performance cost
if ( mp_->isPlaying() != media_play ) {
mp_->play( media_play ); mp_->play( media_play );
} }
@@ -1759,10 +1810,11 @@ void Navigator::RenderNewPannel()
std::list<std::string> recent = Settings::application.recentImport.filenames; std::list<std::string> recent = Settings::application.recentImport.filenames;
for (std::list<std::string>::iterator path = recent.begin(); path != recent.end(); path++ ) for (std::list<std::string>::iterator path = recent.begin(); path != recent.end(); path++ )
{ {
if ( SystemToolkit::file_exists(*path)) { std::string recentpath(*path);
std::string label = path->substr( path->size() - MIN( 35, path->size()) ); if ( SystemToolkit::file_exists(recentpath)) {
std::string label = SystemToolkit::trunc_filename(recentpath, 35);
if (ImGui::Selectable( label.c_str() )) { if (ImGui::Selectable( label.c_str() )) {
new_source_preview_.setSource( Mixer::manager().createSourceFile(path->c_str()), label); new_source_preview_.setSource( Mixer::manager().createSourceFile(recentpath.c_str()), label);
} }
} }
} }
@@ -1773,7 +1825,7 @@ void Navigator::RenderNewPannel()
// show preview // show preview
new_source_preview_.Render(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, true); new_source_preview_.Render(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, true);
// or press Validate button // or press Validate button
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetTextLineHeight() / 2.f); ImGui::Spacing();
if ( ImGui::Button(ICON_FA_CHECK " Create", ImVec2(pannel_width_ - padding_width_, 0)) ) { if ( ImGui::Button(ICON_FA_CHECK " Create", ImVec2(pannel_width_ - padding_width_, 0)) ) {
Mixer::manager().addSource(new_source_preview_.getSource()); Mixer::manager().addSource(new_source_preview_.getSource());
selected_button[NAV_NEW] = false; selected_button[NAV_NEW] = false;
@@ -2023,18 +2075,22 @@ void Navigator::RenderMainPannel()
ImGui::ListBoxHeader("##Sessions", 5); ImGui::ListBoxHeader("##Sessions", 5);
static std::string file_info = ""; static std::string file_info = "";
static std::list<std::string>::iterator file_selected = sessions_list.end(); static std::list<std::string>::iterator file_selected = sessions_list.end();
for(auto filename = sessions_list.begin(); filename != sessions_list.end(); filename++) { for(auto it = sessions_list.begin(); it != sessions_list.end(); it++) {
if (ImGui::Selectable( SystemToolkit::filename(*filename).c_str(), false, ImGuiSelectableFlags_AllowDoubleClick )) { std::string sessionfilename(*it);
if (ImGui::IsMouseDoubleClicked(0)) { std::string shortname = SystemToolkit::filename(*it);
Mixer::manager().open( *filename ); if (sessionfilename.empty())
break;
if (ImGui::Selectable( shortname.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick )) {
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
Mixer::manager().open( sessionfilename );
session_selected = true; session_selected = true;
} }
else { else {
file_info = SessionCreator::info(*filename); file_info = SessionCreator::info(sessionfilename);
file_selected = filename; file_selected = it;
} }
} }
if (ImGui::IsItemHovered() && file_selected != filename) { if (ImGui::IsItemHovered() && file_selected != it) {
file_info.clear(); file_info.clear();
file_selected = sessions_list.end(); file_selected = sessions_list.end();
} }
@@ -2105,7 +2161,7 @@ void Navigator::RenderMainPannel()
else { else {
ImGui::SetCursorPosY(height_ -h); ImGui::SetCursorPosY(height_ -h);
} }
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetTextLineHeightWithSpacing()); ImGui::Spacing();
if ( ImGui::Button( ICON_FA_CROW " vimix", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) if ( ImGui::Button( ICON_FA_CROW " vimix", ImVec2(ImGui::GetContentRegionAvail().x, 0)) )
UserInterface::manager().show_vimix_config = true; UserInterface::manager().show_vimix_config = true;
if ( ImGui::Button(" ImGui ")) if ( ImGui::Button(" ImGui "))
@@ -2158,55 +2214,7 @@ int hover(const char *label)
} }
bool hoverer(const char* label, uint *index, int min, int max, ImVec2 size) #define SEGMENT_ARRAY_MAX 1000
{
// get window
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
// get id
// ImGuiContext& g = *GImGui;
const ImGuiID id = window->GetID(label);
ImVec2 pos = window->DC.CursorPos;
ImRect bbox(pos, pos + size);
ImGui::ItemSize(size);
if (!ImGui::ItemAdd(bbox, id))
return false;
// read user input from system
bool left_mouse_press = false;
const bool hovered = ImGui::ItemHoverable(bbox, id);
bool temp_input_is_active = ImGui::TempInputIsActive(id);
if (!temp_input_is_active)
{
const bool focus_requested = ImGui::FocusableItemRegister(window, id);
left_mouse_press = hovered && ImGui::IsMouseDown(ImGuiMouseButton_Left);
if (focus_requested || left_mouse_press)
{
ImGui::SetActiveID(id, window);
ImGui::SetFocusID(id, window);
ImGui::FocusWindow(window);
}
}
// time Slider behavior
ImRect grab_slider_bb;
uint _zero = min;
uint _end = max;
bool pressed = ImGui::SliderBehavior(bbox, id, ImGuiDataType_U32, index, &_zero,
&_end, "%d", 1.f, ImGuiSliderFlags_None, &grab_slider_bb);
// bbox = ImRect(pos, pos + ImVec2(size.x, size.y / 2.f));
// if (ImGui::IsMouseHoveringRect(bbox.GetTL(), bbox.GetBR()))
// *val = 1.f;
// else
// *val = 0.f;
return pressed;
}
void ShowSandbox(bool* p_open) void ShowSandbox(bool* p_open)
@@ -2223,35 +2231,65 @@ void ShowSandbox(bool* p_open)
std::list< std::pair<guint64, guint64> > segments; std::list< std::pair<guint64, guint64> > segments;
segments.push_back( std::make_pair<guint64, guint64>(GST_SECOND*1, GST_SECOND*2) ); segments.push_back( std::make_pair<guint64, guint64>(GST_SECOND*1, GST_SECOND*2) );
guint64 duration = GST_SECOND * 4; guint64 duration = GST_SECOND * 6;
guint64 step = GST_MSECOND * 20; guint64 step = GST_MSECOND * 20;
static guint64 t = 0; static guint64 t = 0;
bool slider_pressed = ImGuiToolkit::TimelineSliderEdit("timeline", &t, duration, step, segments); // bool slider_pressed = ImGuiToolkit::TimelineSlider("timeline", &t, duration, step);
static float *arr = nullptr;
static size_t array_size = 0;
// static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
// 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f };
MediaPlayer *mp_ = nullptr;
auto po = MediaPlayer::begin();
if (po != MediaPlayer::end())
mp_ = *po;
if (mp_) {
// duration = mp_->duration();
// step = mp_->frameDuration();
// t = mp_->position();
static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, // arr = mp_->timelineArray();
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, // array_size = mp_->timelineArraySize();
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, // if (arr == nullptr) {
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, // array_size = MIN( SEGMENT_ARRAY_MAX, (size_t) duration / (size_t) step);
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, // arr = (float *) malloc(array_size * sizeof(float));
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, // for (int i = 0; i < array_size; ++i) {
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, // arr[i] = 1.f;
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, // }
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, // }
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, }
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, if (arr != nullptr)
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, {
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f,
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(1, 1));
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f, ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.f);
0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f, 0.f, 1.f, 0.4f };
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), 20); ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), 20);
@@ -2259,11 +2297,11 @@ void ShowSandbox(bool* p_open)
ImVec2 draw_pos = ImGui::GetCursorPos(); ImVec2 draw_pos = ImGui::GetCursorPos();
// plot the histogram // plot the histogram
uint press_index = IM_ARRAYSIZE(arr); uint press_index = IM_ARRAYSIZE(arr);
bool pressed = hoverer("test", &press_index, 0, IM_ARRAYSIZE(arr)-1, size); bool pressed = ImGuiToolkit::InvisibleSliderInt("test", &press_index, 0, array_size-1, size);
static bool active = false; static bool active = false;
static float target_value = 0.f; static float target_value = 0.f;
static uint starting_index = IM_ARRAYSIZE(arr); static uint starting_index = array_size;
if (pressed != active) { if (pressed != active) {
active = pressed; active = pressed;
@@ -2276,26 +2314,25 @@ void ShowSandbox(bool* p_open)
arr[i] = target_value; arr[i] = target_value;
} }
// back to // back to
ImGui::SetCursorPos(draw_pos); ImGui::SetCursorPos(draw_pos);
// ImGui::PlotLines("Lines", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, size); ImGui::PlotHistogram("Histogram", arr, array_size-1, 0, NULL, 0.0f, 1.0f, size);
// ImVec4* colors = ImGui::GetStyle().Colors;
// ImGui::PushStyleColor(ImGuiCol_PlotHistogram, colors[ImGuiCol_Tab]);
ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr)-1, 0, NULL, 0.0f, 1.0f, size);
// ImGui::PopStyleColor(1); // ImGui::PopStyleColor(1);
bool slider_pressed = ImGuiToolkit::TimelineSlider("timeline", &t, duration, step, size.x);
ImGui::PopStyleVar(2);
ImGui::Text("Timeline t %" GST_STIME_FORMAT "\n", GST_STIME_ARGS(t)); ImGui::Text("Timeline t %" GST_STIME_FORMAT "\n", GST_STIME_ARGS(t));
ImGui::Text("Timeline Pressed %s", slider_pressed ? "on" : "off"); ImGui::Text("Timeline Pressed %s", slider_pressed ? "on" : "off");
ImGui::Text("Hover Pressed %s v = %d", pressed ? "on" : "off", press_index); ImGui::Text("Hover Pressed %s v = %d", pressed ? "on" : "off", press_index);
static int w = 0; static int w = 0;
ImGui::SetNextItemWidth(size.x); ImGui::SetNextItemWidth(size.x);
ImGui::SliderInt("##int", &w, 0, IM_ARRAYSIZE(arr)-1); ImGui::SliderInt("##int", &w, 0, array_size-1);
}
ImGui::End(); ImGui::End();
} }

View File

@@ -44,11 +44,12 @@
#define IMGUI_TITLE_MAINWINDOW ICON_FA_CIRCLE_NOTCH " vimix" #define IMGUI_TITLE_MAINWINDOW ICON_FA_CIRCLE_NOTCH " vimix"
#define IMGUI_TITLE_MEDIAPLAYER ICON_FA_FILM " Player" #define IMGUI_TITLE_MEDIAPLAYER ICON_FA_FILM " Player"
#define IMGUI_TITLE_TOOLBOX ICON_FA_WRENCH " Development Tools" #define IMGUI_TITLE_TOOLBOX ICON_FA_WRENCH " Development Tools"
#define IMGUI_TITLE_SHADEREDITOR ICON_FA_CODE " Shader Editor" #define IMGUI_TITLE_SHADEREDITOR ICON_FA_CODE " Code"
#define IMGUI_TITLE_PREVIEW ICON_FA_DESKTOP " Ouput" #define IMGUI_TITLE_PREVIEW ICON_FA_DESKTOP " Ouput"
#define IMGUI_TITLE_DELETE ICON_FA_BROOM " Delete?" #define IMGUI_TITLE_DELETE ICON_FA_BROOM " Delete?"
#define IMGUI_LABEL_RECENT_FILES " Recent files" #define IMGUI_LABEL_RECENT_FILES " Recent files"
#define IMGUI_RIGHT_ALIGN -3.5f * ImGui::GetTextLineHeightWithSpacing() #define IMGUI_RIGHT_ALIGN -3.5f * ImGui::GetTextLineHeightWithSpacing()
#define IMGUI_COLOR_OVERLAY IM_COL32(5, 5, 5, 150)
#define IMGUI_NOTIFICATION_DURATION 1.5f #define IMGUI_NOTIFICATION_DURATION 1.5f
#ifdef APPLE #ifdef APPLE
#define CTRL_MOD "Cmd+" #define CTRL_MOD "Cmd+"