diff --git a/ImGuiToolkit.cpp b/ImGuiToolkit.cpp index b65030b..2dd5fd9 100644 --- a/ImGuiToolkit.cpp +++ b/ImGuiToolkit.cpp @@ -636,7 +636,7 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array, // read user input and activate widget const bool left_mouse_press = ImGui::IsMouseDown(ImGuiMouseButton_Left); - const bool right_mouse_press = ImGui::IsMouseDown(ImGuiMouseButton_Right); + 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 hovered = ImGui::ItemHoverable(bbox, id); bool temp_input_is_active = ImGui::TempInputIsActive(id); @@ -653,7 +653,7 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array, else return false; - ImVec4* colors = ImGui::GetStyle().Colors; + static ImVec4* colors = ImGui::GetStyle().Colors; ImVec4 bg_color = hovered ? colors[ImGuiCol_FrameBgHovered] : colors[ImGuiCol_FrameBg]; // enter edit if widget is active @@ -674,12 +674,7 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array, if (previous_index == UINT32_MAX) previous_index = index; - if (left_mouse_press) { - 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; - } - else { + if (right_mouse_press){ static float target_value = values_min; // toggle value histo @@ -691,6 +686,11 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array, for (int i = MIN(previous_index, index); i < MAX(previous_index, index); ++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; + } previous_index = index; diff --git a/MediaPlayer.cpp b/MediaPlayer.cpp index 69d61ff..1a8a8bd 100644 --- a/MediaPlayer.cpp +++ b/MediaPlayer.cpp @@ -31,6 +31,7 @@ MediaPlayer::MediaPlayer() uri_ = "undefined"; pipeline_ = nullptr; + converter_ = nullptr; ready_ = false; failed_ = false; @@ -209,15 +210,22 @@ void MediaPlayer::execute_open() // equivalent to gst-launch-1.0 uridecodebin uri=file:///path_to_file/filename.mp4 ! videoconvert ! ximagesink // Build string describing pipeline - // NB: video convertion chroma-resampler - // Duplicates the samples when upsampling and drops when downsampling 0 - // Uses linear interpolation 1 (default) - // Uses cubic interpolation 2 - // Uses sinc interpolation 3 + // video deinterlacing method + // tomsmocomp (0) – Motion Adaptive: Motion Search + // greedyh (1) – Motion Adaptive: Advanced Detection + // greedyl (2) – Motion Adaptive: Simple Detection + // vfir (3) – Blur Vertical + // linear (4) – Linear + // scalerbob (6) – Double lines + // video convertion chroma-resampler + // Duplicates the samples when upsampling and drops when downsampling 0 + // Uses linear interpolation 1 (default) + // Uses cubic interpolation 2 + // Uses sinc interpolation 3 string description = "uridecodebin uri=" + uri_ + " ! "; if (media_.interlaced) - description += "deinterlace ! "; - description += "videoconvert chroma-resampler=2 ! appsink name=sink"; + description += "deinterlace method=2 ! "; + description += "videoconvert chroma-resampler=2 n-threads=2 ! appsink name=sink"; // parse pipeline descriptor GError *error = NULL; @@ -336,6 +344,10 @@ void MediaPlayer::close() // un-ready the media player ready_ = false; + // no more need to reference converter + if (converter_ != nullptr) + gst_object_unref (converter_); + // clean up GST if (pipeline_ != nullptr) { GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_NULL); @@ -697,12 +709,7 @@ void MediaPlayer::update() // try to get info from discoverer if (discoverer_.wait_for( std::chrono::milliseconds(4) ) == std::future_status::ready ) { - // remember loaded gaps - TimeIntervalSet gaps = media_.timeline.gaps(); - // ok, discovering thread is finished ! Get the media info media_ = discoverer_.get(); - // restore loaded gaps (overwriten above) - media_.timeline.setGaps( gaps ); // if its ok, open the media if (media_.valid) execute_open(); @@ -715,13 +722,6 @@ void MediaPlayer::update() if (!enabled_ || (media_.isimage && textureindex_>0 ) ) return; -// if (seeking_) { -// GstState state; -// gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE); -// seeking_ = false; -// return; -// } - // local variables before trying to update guint read_index = 0; bool need_loop = false; @@ -895,11 +895,15 @@ double MediaPlayer::playSpeed() const return rate_; } -Timeline MediaPlayer::timeline() +Timeline *MediaPlayer::timeline() { - return media_.timeline; + return &media_.timeline; } +float MediaPlayer::currentTimelineFading() +{ + return media_.timeline.fadingAt(position_); +} void MediaPlayer::setTimeline(Timeline tl) { @@ -1040,6 +1044,7 @@ GstFlowReturn MediaPlayer::callback_new_preroll (GstAppSink *sink, gpointer p) // send frames to media player only if ready MediaPlayer *m = (MediaPlayer *)p; if (m && m->ready_) { + // fill frame from buffer if ( !m->fill_frame(buf, MediaPlayer::PREROLL) ) ret = GST_FLOW_ERROR; diff --git a/MediaPlayer.h b/MediaPlayer.h index b887197..4263030 100644 --- a/MediaPlayer.h +++ b/MediaPlayer.h @@ -49,7 +49,9 @@ struct MediaInfo { inline MediaInfo& operator = (const MediaInfo& b) { if (this != &b) { - this->timeline = b.timeline; + this->timeline.setEnd( b.timeline.end() ); + this->timeline.setStep( b.timeline.step() ); + this->timeline.setFirst( b.timeline.first() ); this->width = b.width; this->par_width = b.par_width; this->height = b.height; @@ -194,11 +196,10 @@ public: * - duration : timeline.duration() * - frame duration : timeline.step() */ - Timeline timeline(); - + Timeline *timeline(); void setTimeline(Timeline tl); -// void toggleGapInTimeline(GstClockTime from, GstClockTime to); + float currentTimelineFading(); /** * Get position time * */ @@ -261,6 +262,7 @@ private: LoopMode loop_; GstState desired_state_; GstElement *pipeline_; + GstElement *converter_; GstVideoInfo v_frame_video_info_; std::atomic ready_; std::atomic failed_; diff --git a/MediaSource.cpp b/MediaSource.cpp index 6cdb34d..fb21b59 100644 --- a/MediaSource.cpp +++ b/MediaSource.cpp @@ -135,6 +135,7 @@ void MediaSource::render() // render the media player into frame buffer static glm::mat4 projection = glm::ortho(-1.f, 1.f, 1.f, -1.f, -1.f, 1.f); renderbuffer_->begin(); + mediasurface_->shader()->color.a = mediaplayer_->currentTimelineFading(); mediasurface_->draw(glm::identity(), projection); renderbuffer_->end(); } diff --git a/SessionCreator.cpp b/SessionCreator.cpp index 22034bc..ac61e38 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -184,17 +184,25 @@ void SessionCreator::visit(MediaPlayer &n) XMLElement* mediaplayerNode = xmlCurrent_->FirstChildElement("MediaPlayer"); if (mediaplayerNode) { // timeline - XMLElement *gapselement = mediaplayerNode->FirstChildElement("Gaps"); - if (gapselement) { + XMLElement *timelineelement = mediaplayerNode->FirstChildElement("Timeline"); + if (timelineelement) { Timeline tl; - 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); - tl.addGap( a, b ); + XMLElement *gapselement = timelineelement->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); + tl.addGap( a, b ); + } + } + XMLElement *fadingselement = timelineelement->FirstChildElement("Fading"); + if (fadingselement) { + XMLElement* array = fadingselement->FirstChildElement("array"); + XMLElementDecodeArray(array, tl.fadingArray(), MAX_TIMELINE_ARRAY * sizeof(float)); } n.setTimeline(tl); } diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index 1474dfa..9e72582 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -147,17 +147,27 @@ void SessionVisitor::visit(MediaPlayer &n) newelement->SetAttribute("loop", (int) n.loop()); newelement->SetAttribute("speed", n.playSpeed()); + // timeline + XMLElement *timelineelement = xmlDoc_->NewElement("Timeline"); + // gaps in timeline XMLElement *gapselement = xmlDoc_->NewElement("Gaps"); - TimeIntervalSet gaps = n.timeline().gaps(); + TimeIntervalSet gaps = n.timeline()->gaps(); for( auto it = gaps.begin(); it!= gaps.end(); it++) { XMLElement *g = xmlDoc_->NewElement("Interval"); g->SetAttribute("begin", (*it).begin); g->SetAttribute("end", (*it).end); gapselement->InsertEndChild(g); } - newelement->InsertEndChild(gapselement); + timelineelement->InsertEndChild(gapselement); + // fading in timeline + XMLElement *fadingelement = xmlDoc_->NewElement("Fading"); + XMLElement *array = XMLElementEncodeArray(xmlDoc_, n.timeline()->fadingArray(), MAX_TIMELINE_ARRAY * sizeof(float)); + fadingelement->InsertEndChild(array); + timelineelement->InsertEndChild(fadingelement); + + newelement->InsertEndChild(timelineelement); xmlCurrent_->InsertEndChild(newelement); } diff --git a/Timeline.cpp b/Timeline.cpp index b722be4..9d012f8 100644 --- a/Timeline.cpp +++ b/Timeline.cpp @@ -6,8 +6,6 @@ #include "Log.h" #include "Timeline.h" -#define SEGMENT_ARRAY_MAX_SIZE 1000 - struct includesTime: public std::unary_function { inline bool operator()(const TimeInterval s) const @@ -36,6 +34,8 @@ Timeline& Timeline::operator = (const Timeline& b) this->timing_ = b.timing_; this->step_ = b.step_; this->gaps_ = b.gaps_; + memcpy( this->gapsArray_, b.gapsArray_, MAX_TIMELINE_ARRAY * sizeof(float)); + memcpy( this->fadingArray_, b.fadingArray_, MAX_TIMELINE_ARRAY * sizeof(float)); } return *this; } @@ -47,8 +47,8 @@ void Timeline::reset() first_ = GST_CLOCK_TIME_NONE; step_ = GST_CLOCK_TIME_NONE; - // clear gaps - gaps_.clear(); + clearGaps(); + clearFading(); } bool Timeline::is_valid() @@ -58,8 +58,10 @@ bool Timeline::is_valid() void Timeline::setFirst(GstClockTime first) { - timing_.begin = 0; first_ = first; + // validate timing + if (first_ != GST_CLOCK_TIME_NONE) + timing_.begin = 0; } void Timeline::setEnd(GstClockTime end) @@ -72,7 +74,6 @@ void Timeline::setStep(GstClockTime dt) step_ = dt; } - GstClockTime Timeline::next(GstClockTime time) const { GstClockTime next_time = time; @@ -94,52 +95,18 @@ GstClockTime Timeline::previous(GstClockTime time) const return prev_time; } -void Timeline::updateGapsFromArray(float *array_, size_t array_size_) +float *Timeline::gapsArray() { - // 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]; - } + if (gaps_array_need_update_) { + fillArrayFromGaps(gapsArray_, MAX_TIMELINE_ARRAY); } - // 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 ); - + return gapsArray_; } -void Timeline::fillArrayFromGaps(float *array_, size_t array_size_) +void Timeline::update() { - if (array_ != nullptr && array_size_ > 0 && timing_.is_valid()) { - - // 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; - } - - } + updateGapsFromArray(gapsArray_, MAX_TIMELINE_ARRAY); + gaps_array_need_update_ = false; } bool Timeline::gapAt(const GstClockTime t, TimeInterval &gap) const @@ -161,64 +128,220 @@ bool Timeline::addGap(GstClockTime begin, GstClockTime end) bool Timeline::addGap(TimeInterval s) { - if ( s.is_valid() ) + if ( s.is_valid() ) { + gaps_array_need_update_ = true; return gaps_.insert(s).second; + } return false; } void Timeline::setGaps(TimeIntervalSet g) { + gaps_array_need_update_ = true; gaps_ = g; } -//void Timeline::toggleGaps(GstClockTime from, GstClockTime to) -//{ -// TimeInterval interval(from, to); -// TimeInterval gap; -// bool is_a_gap_ = gapAt(from, gap); - -// if (interval.is_valid()) -// { -// // fill gap -// if (is_a_gap_) { - -// } -// // else create gap (from time is not in a gap) -// else { - -// TimeIntervalSet::iterator g = std::find_if(gaps_.begin(), gaps_.end(), includesTime(to)); -// // if there is a gap overlap with [from to] (or [to from]), expand the gap -// if ( g != gaps_.end() ) { -// interval.begin = MIN( (*g).begin, interval.begin); -// interval.end = MAX( (*g).end , interval.end); -// gaps_.erase(g); -// } -// // add the new gap -// addGap(interval); -// Log::Info("add gap [ %ld %ld ]", interval.begin, interval.end); -// } - - -// } -// Log::Info("%d gaps in timeline", numGaps()); -//} - 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); + gaps_array_need_update_ = true; return true; } return false; } + +TimeIntervalSet Timeline::sections() const +{ + TimeIntervalSet sec; + + GstClockTime begin_sec = timing_.begin; + + if (gaps_.size() > 0) { + + auto it = gaps_.begin(); + if ((*it).begin == begin_sec) { + begin_sec = (*it).end; + ++it; + } + + for (; it != gaps_.end(); ++it) + { + sec.insert( TimeInterval(begin_sec, (*it).begin) ); + begin_sec = (*it).end; + } + + } + + if (begin_sec != timing_.end) + sec.insert( TimeInterval(begin_sec, timing_.end) ); + + return sec; +} + void Timeline::clearGaps() { gaps_.clear(); + + for(int i=0;i( static_cast(modulo) / static_cast(step_) ); + float v = fadingArray_[keyframe_index]; + v += interpol * (fadingArray_[keyframe_next_index] - fadingArray_[keyframe_index]); + + return v; +} + +void Timeline::clearFading() +{ + for(int i=0;i -1 && k < MAX_TIMELINE_ARRAY-1) { + tmparray[i] += fadingArray_[k] * kernel[j]; + divider += kernel[j]; + } + } + tmparray[i] *= 1.f / divider; + } + + memcpy( fadingArray_, tmparray, MAX_TIMELINE_ARRAY * sizeof(float)); + } } +void Timeline::autoFading(uint milisecond) +{ + + GstClockTime stepduration = timing_.end / MAX_TIMELINE_ARRAY; + stepduration = GST_TIME_AS_MSECONDS(stepduration); + uint N = milisecond / stepduration; + + // reset all to zero + for(int i=0;i(i-s) / static_cast(n); + // plateau + for (; i < e-n; ++i) + fadingArray_[i] = 1.f; + // linear fade out ending at e + for (; i < e; ++i) + fadingArray_[i] = static_cast(e-i) / static_cast(n); + } + +} + +void Timeline::updateGapsFromArray(float *array, size_t array_size) +{ + // reset gaps + gaps_.clear(); + + // fill the gaps from array + if (array != nullptr && array_size > 0 && timing_.is_valid()) { + + // loop over the array to detect gaps + float status = 0.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 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 ); + 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::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 +#define MAX_TIMELINE_ARRAY 2000 + struct TimeInterval { GstClockTime begin; @@ -81,16 +83,16 @@ public: ~Timeline(); Timeline& operator = (const Timeline& b); - void reset(); bool is_valid(); + void update(); // global properties of the timeline - // timeline is invalid untill all 3 are set + // timeline is valid only if all 3 are set void setFirst(GstClockTime first); void setEnd(GstClockTime end); void setStep(GstClockTime dt); - // get properties + // Timing manipulation inline GstClockTime start() const { return timing_.begin; } inline GstClockTime end() const { return timing_.end; } inline GstClockTime first() const { return first_; } @@ -98,33 +100,45 @@ public: inline GstClockTime step() const { return step_; } inline GstClockTime duration() const { return timing_.duration(); } inline size_t numFrames() const { return duration() / step_; } - inline TimeIntervalSet gaps() const { return gaps_; } - inline size_t numGaps() const { return gaps_.size(); } - GstClockTime next(GstClockTime time) const; GstClockTime previous(GstClockTime time) const; - // Add / remove / get gaps in the timeline + // Manipulation of gaps in the timeline + inline TimeIntervalSet gaps() const { return gaps_; } + inline TimeIntervalSet sections() const; + inline size_t numGaps() const { return gaps_.size(); } + float *gapsArray(); void clearGaps(); + void setGaps(TimeIntervalSet g); bool addGap(TimeInterval s); bool addGap(GstClockTime begin, GstClockTime end); bool removeGaptAt(GstClockTime t); bool gapAt(const GstClockTime t, TimeInterval &gap) const; - void setGaps(TimeIntervalSet g); - // synchronize data structures - void updateGapsFromArray(float *array_, size_t array_size_); - void fillArrayFromGaps(float *array_, size_t array_size_); + float fadingAt(const GstClockTime t); + inline float *fadingArray() { return fadingArray_; } + void clearFading(); + void smoothFading(uint N = 1); + void autoFading(uint milisecond = 100); private: + void reset(); + // global information on the timeline TimeInterval timing_; GstClockTime first_; GstClockTime step_; // main data structure containing list of gaps in the timeline - TimeIntervalSet gaps_; + TimeIntervalSet gaps_; + float gapsArray_[MAX_TIMELINE_ARRAY]; + bool gaps_array_need_update_; + // synchronize data structures + void updateGapsFromArray(float *array, size_t array_size); + void fillArrayFromGaps(float *array, size_t array_size); + + float fadingArray_[MAX_TIMELINE_ARRAY]; }; diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index 5358793..b0e5933 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -1181,8 +1181,8 @@ void MediaController::Render() static float timeline_zoom = 1.f; const float width = ImGui::GetContentRegionAvail().x; 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; + const float segments_height = timeline_height; + const float slider_zoom_width = segments_height / 2.f; if (Settings::application.widget.media_player_view) { @@ -1267,45 +1267,58 @@ void MediaController::Render() ImGui::PopButtonRepeat(); } - ImGui::SameLine(0, MAX(spacing * 4.f, width - 400.f) ); - // loop modes button + ImGui::SameLine(0, spacing); static int current_loop = 0; static std::vector< std::pair > iconsloop = { {0,15}, {1,15}, {19,14} }; current_loop = (int) mp_->loop(); if ( ImGuiToolkit::ButtonIconMultistate(iconsloop, ¤t_loop) ) mp_->setLoop( (MediaPlayer::LoopMode) current_loop ); + // speed slider + ImGui::SameLine(0, MAX(spacing * 4.f, width - 400.f) ); float speed = static_cast(mp_->playSpeed()); - ImGui::SameLine(0, spacing); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 40.0); if (ImGui::DragFloat( "##Speed", &speed, 0.01f, -10.f, 10.f, "Speed x %.1f", 2.f)) mp_->setPlaySpeed( static_cast(speed) ); - // reset + // Timeline popup menu ImGui::SameLine(0, spacing); - if (ImGuiToolkit::ButtonIcon(11, 14)) { - timeline_zoom = 1.f; - speed = 1.f; - mp_->setPlaySpeed( static_cast(speed) ); - mp_->setLoop( MediaPlayer::LOOP_REWIND ); + if (ImGuiToolkit::ButtonIcon(5, 8)) + ImGui::OpenPopup("MenuTimeline"); + if (ImGui::BeginPopup("MenuTimeline")) { + if (ImGui::Selectable( "Reset Speed" )){ + speed = 1.f; + mp_->setPlaySpeed( static_cast(speed) ); + } + if (ImGui::Selectable( "Reset Timeline" )){ + timeline_zoom = 1.f; + mp_->timeline()->clearFading(); + mp_->timeline()->clearGaps(); + } + ImGui::Separator(); + static int smoothfactor = 10; + ImGui::SetNextItemWidth(100); + if (ImGui::Button( "Smooth Curve" )){ + mp_->timeline()->smoothFading(smoothfactor); + } + ImGui::SameLine(0); + ImGui::SetNextItemWidth(100); + ImGui::DragInt("##smoothfactor", &smoothfactor, 5.f, 5, 50, "x %d"); + static int milisec = 500; + ImGui::SetNextItemWidth(100); + if (ImGui::Button( "Auto Fading" )){ + mp_->timeline()->autoFading(milisec); + mp_->timeline()->smoothFading(10); + } + ImGui::SameLine(0); + ImGui::SetNextItemWidth(100); + ImGui::DragInt("##milisecfading", &milisec, 100.f, 100, 2000, "%d ms"); + ImGui::EndPopup(); } ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(3.f, 3.f)); - // For controlling the current media player with timeline - static Timeline working_timeline; - static MediaPlayer *timeline_mp = nullptr; - static size_t array_size = 1200; - static float *array = (float *) malloc( sizeof(float) * array_size); - - // detect change of media player to update the timeline array - if (timeline_mp != mp_ || !working_timeline.is_valid()) { - timeline_mp = mp_; - working_timeline = timeline_mp->timeline(); - working_timeline.fillArrayFromGaps(array, array_size); - } - guint64 current_t = mp_->position(); guint64 seek_t = current_t; @@ -1320,43 +1333,15 @@ void MediaController::Render() 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(); - - // start slider at a position which forces to change value - uint press_index = array_size; - bool pressed = ImGuiToolkit::InvisibleSliderInt("##TimelinePicking", &press_index, 0, array_size, 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 - { - // update the timeline - working_timeline.updateGapsFromArray(array, array_size); - // apply it to the media player - timeline_mp->setTimeline(working_timeline); - } + if ( ImGuiToolkit::EditPlotHistoLines("##TimelineArray", mp_->timeline()->gapsArray(), + mp_->timeline()->fadingArray(), MAX_TIMELINE_ARRAY, 0.f, 1.f, size) ) + { + mp_->timeline()->update(); } - 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, 0, NULL, 0.0f, 1.0f, size); // custom timeline slider - slider_pressed_ = ImGuiToolkit::TimelineSlider("##timeline", &seek_t, mp_->timeline().first(), - mp_->timeline().end(), mp_->timeline().step(), size.x); + slider_pressed_ = ImGuiToolkit::TimelineSlider("##timeline", &seek_t, mp_->timeline()->first(), + mp_->timeline()->end(), mp_->timeline()->step(), size.x); ImGui::PopStyleVar(2); } @@ -2377,7 +2362,7 @@ void ShowSandbox(bool* p_open) ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.f); ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), 40); - size.x *= 2.f; + size.x *= 1.f; // // draw position when entering // ImVec2 draw_pos = ImGui::GetCursorPos(); diff --git a/tinyxml2Toolkit.cpp b/tinyxml2Toolkit.cpp index 543c97c..8a44113 100644 --- a/tinyxml2Toolkit.cpp +++ b/tinyxml2Toolkit.cpp @@ -1,6 +1,9 @@ #include "SystemToolkit.h" #include "Log.h" +#include +#include + #include #include "tinyxml2Toolkit.h" using namespace tinyxml2; @@ -81,6 +84,39 @@ void tinyxml2::XMLElementToGLM(XMLElement *elem, glm::mat4 &matrix) } } + +XMLElement *tinyxml2::XMLElementEncodeArray(XMLDocument *doc, void *array, uint64_t arraysize) +{ + gchar *encoded_string = g_base64_encode( (guchar *) array, arraysize); + + XMLElement *newelement = doc->NewElement( "array" ); + newelement->SetAttribute("len", arraysize); + + XMLText *text = doc->NewText( encoded_string ); + newelement->InsertEndChild( text ); + + g_free(encoded_string); + return newelement; +} + +bool tinyxml2::XMLElementDecodeArray(XMLElement *elem, void *array, uint64_t arraysize) +{ + if ( !elem || std::string(elem->Name()).find("array") == std::string::npos ) + return false; + + uint64_t len = 0; + elem->QueryUnsigned64Attribute("len", &len); + if ( arraysize != len ) + return false; + + guchar *decoded_array = g_base64_decode(elem->GetText(), &len); + if ( arraysize != len ) + return false; + + memcpy(array, decoded_array, len); + return true; +} + bool tinyxml2::XMLSaveDoc(XMLDocument * const doc, std::string filename) { XMLDeclaration *pDec = doc->NewDeclaration(); diff --git a/tinyxml2Toolkit.h b/tinyxml2Toolkit.h index 58bdbf2..2df4975 100644 --- a/tinyxml2Toolkit.h +++ b/tinyxml2Toolkit.h @@ -17,6 +17,8 @@ void XMLElementToGLM(XMLElement *elem, glm::vec3 &vector); void XMLElementToGLM(XMLElement *elem, glm::vec4 &vector); void XMLElementToGLM(XMLElement *elem, glm::mat4 &matrix); +XMLElement *XMLElementEncodeArray(XMLDocument *doc, void *array, uint64_t arraysize); +bool XMLElementDecodeArray(XMLElement *elem, void *array, uint64_t arraysize); bool XMLSaveDoc(tinyxml2::XMLDocument * const doc, std::string filename); bool XMLResultError(int result);