mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-14 19:59:59 +01:00
Saving and loading of timeline, with fading and gaps. Applying fading to
MediaSource. Playing with timeline options to facilitate its use.
This commit is contained in:
@@ -636,7 +636,7 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array,
|
|||||||
|
|
||||||
// read user input and activate widget
|
// read user input and activate widget
|
||||||
const bool left_mouse_press = ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
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 mouse_press = left_mouse_press | right_mouse_press;
|
||||||
const bool hovered = ImGui::ItemHoverable(bbox, id);
|
const bool hovered = ImGui::ItemHoverable(bbox, id);
|
||||||
bool temp_input_is_active = ImGui::TempInputIsActive(id);
|
bool temp_input_is_active = ImGui::TempInputIsActive(id);
|
||||||
@@ -653,7 +653,7 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array,
|
|||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ImVec4* colors = ImGui::GetStyle().Colors;
|
static ImVec4* colors = ImGui::GetStyle().Colors;
|
||||||
ImVec4 bg_color = hovered ? colors[ImGuiCol_FrameBgHovered] : colors[ImGuiCol_FrameBg];
|
ImVec4 bg_color = hovered ? colors[ImGuiCol_FrameBgHovered] : colors[ImGuiCol_FrameBg];
|
||||||
|
|
||||||
// enter edit if widget is active
|
// enter edit if widget is active
|
||||||
@@ -674,12 +674,7 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array,
|
|||||||
if (previous_index == UINT32_MAX)
|
if (previous_index == UINT32_MAX)
|
||||||
previous_index = index;
|
previous_index = index;
|
||||||
|
|
||||||
if (left_mouse_press) {
|
if (right_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 {
|
|
||||||
static float target_value = values_min;
|
static float target_value = values_min;
|
||||||
|
|
||||||
// toggle value histo
|
// 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)
|
for (int i = MIN(previous_index, index); i < MAX(previous_index, index); ++i)
|
||||||
histogram_array[i] = target_value;
|
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;
|
previous_index = index;
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ MediaPlayer::MediaPlayer()
|
|||||||
|
|
||||||
uri_ = "undefined";
|
uri_ = "undefined";
|
||||||
pipeline_ = nullptr;
|
pipeline_ = nullptr;
|
||||||
|
converter_ = nullptr;
|
||||||
|
|
||||||
ready_ = false;
|
ready_ = false;
|
||||||
failed_ = 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
|
// equivalent to gst-launch-1.0 uridecodebin uri=file:///path_to_file/filename.mp4 ! videoconvert ! ximagesink
|
||||||
|
|
||||||
// Build string describing pipeline
|
// Build string describing pipeline
|
||||||
// NB: video convertion chroma-resampler
|
// 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
|
// Duplicates the samples when upsampling and drops when downsampling 0
|
||||||
// Uses linear interpolation 1 (default)
|
// Uses linear interpolation 1 (default)
|
||||||
// Uses cubic interpolation 2
|
// Uses cubic interpolation 2
|
||||||
// Uses sinc interpolation 3
|
// Uses sinc interpolation 3
|
||||||
string description = "uridecodebin uri=" + uri_ + " ! ";
|
string description = "uridecodebin uri=" + uri_ + " ! ";
|
||||||
if (media_.interlaced)
|
if (media_.interlaced)
|
||||||
description += "deinterlace ! ";
|
description += "deinterlace method=2 ! ";
|
||||||
description += "videoconvert chroma-resampler=2 ! appsink name=sink";
|
description += "videoconvert chroma-resampler=2 n-threads=2 ! appsink name=sink";
|
||||||
|
|
||||||
// parse pipeline descriptor
|
// parse pipeline descriptor
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
@@ -336,6 +344,10 @@ void MediaPlayer::close()
|
|||||||
// un-ready the media player
|
// un-ready the media player
|
||||||
ready_ = false;
|
ready_ = false;
|
||||||
|
|
||||||
|
// no more need to reference converter
|
||||||
|
if (converter_ != nullptr)
|
||||||
|
gst_object_unref (converter_);
|
||||||
|
|
||||||
// clean up GST
|
// clean up GST
|
||||||
if (pipeline_ != nullptr) {
|
if (pipeline_ != nullptr) {
|
||||||
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_NULL);
|
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_NULL);
|
||||||
@@ -697,12 +709,7 @@ void MediaPlayer::update()
|
|||||||
// try to get info from discoverer
|
// try to get info from discoverer
|
||||||
if (discoverer_.wait_for( std::chrono::milliseconds(4) ) == std::future_status::ready )
|
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();
|
media_ = discoverer_.get();
|
||||||
// restore loaded gaps (overwriten above)
|
|
||||||
media_.timeline.setGaps( gaps );
|
|
||||||
// if its ok, open the media
|
// if its ok, open the media
|
||||||
if (media_.valid)
|
if (media_.valid)
|
||||||
execute_open();
|
execute_open();
|
||||||
@@ -715,13 +722,6 @@ void MediaPlayer::update()
|
|||||||
if (!enabled_ || (media_.isimage && textureindex_>0 ) )
|
if (!enabled_ || (media_.isimage && textureindex_>0 ) )
|
||||||
return;
|
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
|
// local variables before trying to update
|
||||||
guint read_index = 0;
|
guint read_index = 0;
|
||||||
bool need_loop = false;
|
bool need_loop = false;
|
||||||
@@ -895,11 +895,15 @@ double MediaPlayer::playSpeed() const
|
|||||||
return rate_;
|
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)
|
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
|
// send frames to media player only if ready
|
||||||
MediaPlayer *m = (MediaPlayer *)p;
|
MediaPlayer *m = (MediaPlayer *)p;
|
||||||
if (m && m->ready_) {
|
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;
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ struct MediaInfo {
|
|||||||
inline MediaInfo& operator = (const MediaInfo& b)
|
inline MediaInfo& operator = (const MediaInfo& b)
|
||||||
{
|
{
|
||||||
if (this != &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->width = b.width;
|
||||||
this->par_width = b.par_width;
|
this->par_width = b.par_width;
|
||||||
this->height = b.height;
|
this->height = b.height;
|
||||||
@@ -194,11 +196,10 @@ public:
|
|||||||
* - duration : timeline.duration()
|
* - duration : timeline.duration()
|
||||||
* - frame duration : timeline.step()
|
* - frame duration : timeline.step()
|
||||||
*/
|
*/
|
||||||
Timeline timeline();
|
Timeline *timeline();
|
||||||
|
|
||||||
void setTimeline(Timeline tl);
|
void setTimeline(Timeline tl);
|
||||||
// void toggleGapInTimeline(GstClockTime from, GstClockTime to);
|
|
||||||
|
|
||||||
|
float currentTimelineFading();
|
||||||
/**
|
/**
|
||||||
* Get position time
|
* Get position time
|
||||||
* */
|
* */
|
||||||
@@ -261,6 +262,7 @@ private:
|
|||||||
LoopMode loop_;
|
LoopMode loop_;
|
||||||
GstState desired_state_;
|
GstState desired_state_;
|
||||||
GstElement *pipeline_;
|
GstElement *pipeline_;
|
||||||
|
GstElement *converter_;
|
||||||
GstVideoInfo v_frame_video_info_;
|
GstVideoInfo v_frame_video_info_;
|
||||||
std::atomic<bool> ready_;
|
std::atomic<bool> ready_;
|
||||||
std::atomic<bool> failed_;
|
std::atomic<bool> failed_;
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ void MediaSource::render()
|
|||||||
// render the media player into frame buffer
|
// 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);
|
static glm::mat4 projection = glm::ortho(-1.f, 1.f, 1.f, -1.f, -1.f, 1.f);
|
||||||
renderbuffer_->begin();
|
renderbuffer_->begin();
|
||||||
|
mediasurface_->shader()->color.a = mediaplayer_->currentTimelineFading();
|
||||||
mediasurface_->draw(glm::identity<glm::mat4>(), projection);
|
mediasurface_->draw(glm::identity<glm::mat4>(), projection);
|
||||||
renderbuffer_->end();
|
renderbuffer_->end();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,9 +184,11 @@ void SessionCreator::visit(MediaPlayer &n)
|
|||||||
XMLElement* mediaplayerNode = xmlCurrent_->FirstChildElement("MediaPlayer");
|
XMLElement* mediaplayerNode = xmlCurrent_->FirstChildElement("MediaPlayer");
|
||||||
if (mediaplayerNode) {
|
if (mediaplayerNode) {
|
||||||
// timeline
|
// timeline
|
||||||
XMLElement *gapselement = mediaplayerNode->FirstChildElement("Gaps");
|
XMLElement *timelineelement = mediaplayerNode->FirstChildElement("Timeline");
|
||||||
if (gapselement) {
|
if (timelineelement) {
|
||||||
Timeline tl;
|
Timeline tl;
|
||||||
|
XMLElement *gapselement = timelineelement->FirstChildElement("Gaps");
|
||||||
|
if (gapselement) {
|
||||||
XMLElement* gap = gapselement->FirstChildElement("Interval");
|
XMLElement* gap = gapselement->FirstChildElement("Interval");
|
||||||
for( ; gap ; gap = gap->NextSiblingElement())
|
for( ; gap ; gap = gap->NextSiblingElement())
|
||||||
{
|
{
|
||||||
@@ -196,6 +198,12 @@ void SessionCreator::visit(MediaPlayer &n)
|
|||||||
gap->QueryUnsigned64Attribute("end", &b);
|
gap->QueryUnsigned64Attribute("end", &b);
|
||||||
tl.addGap( a, 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);
|
n.setTimeline(tl);
|
||||||
}
|
}
|
||||||
// playing properties
|
// playing properties
|
||||||
|
|||||||
@@ -147,17 +147,27 @@ 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());
|
||||||
|
|
||||||
|
// timeline
|
||||||
|
XMLElement *timelineelement = xmlDoc_->NewElement("Timeline");
|
||||||
|
|
||||||
// gaps in timeline
|
// gaps in timeline
|
||||||
XMLElement *gapselement = xmlDoc_->NewElement("Gaps");
|
XMLElement *gapselement = xmlDoc_->NewElement("Gaps");
|
||||||
TimeIntervalSet gaps = n.timeline().gaps();
|
TimeIntervalSet gaps = n.timeline()->gaps();
|
||||||
for( auto it = gaps.begin(); it!= gaps.end(); it++) {
|
for( auto it = gaps.begin(); it!= gaps.end(); it++) {
|
||||||
XMLElement *g = xmlDoc_->NewElement("Interval");
|
XMLElement *g = xmlDoc_->NewElement("Interval");
|
||||||
g->SetAttribute("begin", (*it).begin);
|
g->SetAttribute("begin", (*it).begin);
|
||||||
g->SetAttribute("end", (*it).end);
|
g->SetAttribute("end", (*it).end);
|
||||||
gapselement->InsertEndChild(g);
|
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);
|
xmlCurrent_->InsertEndChild(newelement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
283
Timeline.cpp
283
Timeline.cpp
@@ -6,8 +6,6 @@
|
|||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Timeline.h"
|
#include "Timeline.h"
|
||||||
|
|
||||||
#define SEGMENT_ARRAY_MAX_SIZE 1000
|
|
||||||
|
|
||||||
struct includesTime: public std::unary_function<TimeInterval, bool>
|
struct includesTime: public std::unary_function<TimeInterval, bool>
|
||||||
{
|
{
|
||||||
inline bool operator()(const TimeInterval s) const
|
inline bool operator()(const TimeInterval s) const
|
||||||
@@ -36,6 +34,8 @@ Timeline& Timeline::operator = (const Timeline& b)
|
|||||||
this->timing_ = b.timing_;
|
this->timing_ = b.timing_;
|
||||||
this->step_ = b.step_;
|
this->step_ = b.step_;
|
||||||
this->gaps_ = b.gaps_;
|
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;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -47,8 +47,8 @@ void Timeline::reset()
|
|||||||
first_ = GST_CLOCK_TIME_NONE;
|
first_ = GST_CLOCK_TIME_NONE;
|
||||||
step_ = GST_CLOCK_TIME_NONE;
|
step_ = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
// clear gaps
|
clearGaps();
|
||||||
gaps_.clear();
|
clearFading();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Timeline::is_valid()
|
bool Timeline::is_valid()
|
||||||
@@ -58,8 +58,10 @@ bool Timeline::is_valid()
|
|||||||
|
|
||||||
void Timeline::setFirst(GstClockTime first)
|
void Timeline::setFirst(GstClockTime first)
|
||||||
{
|
{
|
||||||
timing_.begin = 0;
|
|
||||||
first_ = first;
|
first_ = first;
|
||||||
|
// validate timing
|
||||||
|
if (first_ != GST_CLOCK_TIME_NONE)
|
||||||
|
timing_.begin = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::setEnd(GstClockTime end)
|
void Timeline::setEnd(GstClockTime end)
|
||||||
@@ -72,7 +74,6 @@ void Timeline::setStep(GstClockTime dt)
|
|||||||
step_ = dt;
|
step_ = dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GstClockTime Timeline::next(GstClockTime time) const
|
GstClockTime Timeline::next(GstClockTime time) const
|
||||||
{
|
{
|
||||||
GstClockTime next_time = time;
|
GstClockTime next_time = time;
|
||||||
@@ -94,52 +95,18 @@ GstClockTime Timeline::previous(GstClockTime time) const
|
|||||||
return prev_time;
|
return prev_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::updateGapsFromArray(float *array_, size_t array_size_)
|
float *Timeline::gapsArray()
|
||||||
{
|
{
|
||||||
// reset gaps
|
if (gaps_array_need_update_) {
|
||||||
gaps_.clear();
|
fillArrayFromGaps(gapsArray_, MAX_TIMELINE_ARRAY);
|
||||||
|
|
||||||
// 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
|
return gapsArray_;
|
||||||
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_)
|
void Timeline::update()
|
||||||
{
|
{
|
||||||
if (array_ != nullptr && array_size_ > 0 && timing_.is_valid()) {
|
updateGapsFromArray(gapsArray_, MAX_TIMELINE_ARRAY);
|
||||||
|
gaps_array_need_update_ = false;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Timeline::gapAt(const GstClockTime t, TimeInterval &gap) const
|
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)
|
bool Timeline::addGap(TimeInterval s)
|
||||||
{
|
{
|
||||||
if ( s.is_valid() )
|
if ( s.is_valid() ) {
|
||||||
|
gaps_array_need_update_ = true;
|
||||||
return gaps_.insert(s).second;
|
return gaps_.insert(s).second;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::setGaps(TimeIntervalSet g)
|
void Timeline::setGaps(TimeIntervalSet g)
|
||||||
{
|
{
|
||||||
|
gaps_array_need_update_ = true;
|
||||||
gaps_ = g;
|
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)
|
bool Timeline::removeGaptAt(GstClockTime t)
|
||||||
{
|
{
|
||||||
TimeIntervalSet::const_iterator s = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
|
TimeIntervalSet::const_iterator s = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
|
||||||
|
|
||||||
if ( s != gaps_.end() ) {
|
if ( s != gaps_.end() ) {
|
||||||
gaps_.erase(s);
|
gaps_.erase(s);
|
||||||
|
gaps_array_need_update_ = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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()
|
void Timeline::clearGaps()
|
||||||
{
|
{
|
||||||
gaps_.clear();
|
gaps_.clear();
|
||||||
|
|
||||||
|
for(int i=0;i<MAX_TIMELINE_ARRAY;++i)
|
||||||
|
gapsArray_[i] = 0.f;
|
||||||
|
|
||||||
|
gaps_array_need_update_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Timeline::fadingAt(const GstClockTime t)
|
||||||
|
{
|
||||||
|
GstClockTime modulo = t % step_;
|
||||||
|
GstClockTime keyframe = t - modulo;
|
||||||
|
size_t keyframe_index = CLAMP( (MAX_TIMELINE_ARRAY * keyframe) / timing_.end, 0, MAX_TIMELINE_ARRAY-1);
|
||||||
|
size_t keyframe_next_index = CLAMP( keyframe_index+1, 0, MAX_TIMELINE_ARRAY-1);
|
||||||
|
|
||||||
|
float interpol = static_cast<float>( static_cast<double>(modulo) / static_cast<double>(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<MAX_TIMELINE_ARRAY;++i)
|
||||||
|
fadingArray_[i] = 1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timeline::smoothFading(uint N)
|
||||||
|
{
|
||||||
|
const float kernel[7] = { 2.f, 22.f, 97.f, 159.f, 97.f, 22.f, 2.f};
|
||||||
|
float tmparray[MAX_TIMELINE_ARRAY];
|
||||||
|
|
||||||
|
for (uint n = 0; n < N; ++n) {
|
||||||
|
|
||||||
|
for (long i = 0; i < MAX_TIMELINE_ARRAY; ++i) {
|
||||||
|
tmparray[i] = 0.f;
|
||||||
|
float divider = 0.f;
|
||||||
|
for( long j = 0; j < 7; ++j) {
|
||||||
|
long k = i - 3 + j;
|
||||||
|
if (k > -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<MAX_TIMELINE_ARRAY;++i)
|
||||||
|
fadingArray_[i] = 0.f;
|
||||||
|
|
||||||
|
// get sections (inverse of gaps)
|
||||||
|
TimeIntervalSet sec = sections();
|
||||||
|
|
||||||
|
// fading for each section
|
||||||
|
// NB : there is at least one
|
||||||
|
for (auto it = sec.begin(); it != sec.end(); ++it)
|
||||||
|
{
|
||||||
|
// get index of begining of section
|
||||||
|
size_t s = ( (*it).begin * MAX_TIMELINE_ARRAY ) / timing_.end;
|
||||||
|
// get index of ending of section
|
||||||
|
size_t e = ( (*it).end * MAX_TIMELINE_ARRAY ) / timing_.end;
|
||||||
|
|
||||||
|
// calculate size of the smooth transition in [s e] interval
|
||||||
|
uint n = MIN( (e-s)/3, N );
|
||||||
|
|
||||||
|
// linear fade in starting at s
|
||||||
|
size_t i = s;
|
||||||
|
for (; i < s+n; ++i)
|
||||||
|
fadingArray_[i] = static_cast<float>(i-s) / static_cast<float>(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<float>(e-i) / static_cast<float>(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<array_size;++i)
|
||||||
|
gapsArray_[i] = 0.f;
|
||||||
|
|
||||||
|
// for each gap
|
||||||
|
for (auto it = gaps_.begin(); it != gaps_.end(); ++it)
|
||||||
|
{
|
||||||
|
size_t s = ( (*it).begin * array_size ) / timing_.end;
|
||||||
|
size_t e = ( (*it).end * array_size ) / timing_.end;
|
||||||
|
|
||||||
|
// fill with 1 where there is a gap
|
||||||
|
for (size_t i = s; i < e; ++i) {
|
||||||
|
gapsArray_[i] = 1.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gaps_array_need_update_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// // NB: less efficient algorithm :
|
||||||
|
// TimeInterval gap;
|
||||||
|
// for (size_t i = 0; i < array_size; ++i) {
|
||||||
|
// GstClockTime t = (timing_.duration() * i) / array_size;
|
||||||
|
// array[i] = gapAt(t, gap) ? 1.f : 0.f;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|||||||
36
Timeline.h
36
Timeline.h
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include <gst/pbutils/pbutils.h>
|
#include <gst/pbutils/pbutils.h>
|
||||||
|
|
||||||
|
#define MAX_TIMELINE_ARRAY 2000
|
||||||
|
|
||||||
struct TimeInterval
|
struct TimeInterval
|
||||||
{
|
{
|
||||||
GstClockTime begin;
|
GstClockTime begin;
|
||||||
@@ -81,16 +83,16 @@ public:
|
|||||||
~Timeline();
|
~Timeline();
|
||||||
Timeline& operator = (const Timeline& b);
|
Timeline& operator = (const Timeline& b);
|
||||||
|
|
||||||
void reset();
|
|
||||||
bool is_valid();
|
bool is_valid();
|
||||||
|
void update();
|
||||||
|
|
||||||
// global properties of the timeline
|
// 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 setFirst(GstClockTime first);
|
||||||
void setEnd(GstClockTime end);
|
void setEnd(GstClockTime end);
|
||||||
void setStep(GstClockTime dt);
|
void setStep(GstClockTime dt);
|
||||||
|
|
||||||
// get properties
|
// Timing manipulation
|
||||||
inline GstClockTime start() const { return timing_.begin; }
|
inline GstClockTime start() const { return timing_.begin; }
|
||||||
inline GstClockTime end() const { return timing_.end; }
|
inline GstClockTime end() const { return timing_.end; }
|
||||||
inline GstClockTime first() const { return first_; }
|
inline GstClockTime first() const { return first_; }
|
||||||
@@ -98,26 +100,31 @@ public:
|
|||||||
inline GstClockTime step() const { return step_; }
|
inline GstClockTime step() const { return step_; }
|
||||||
inline GstClockTime duration() const { return timing_.duration(); }
|
inline GstClockTime duration() const { return timing_.duration(); }
|
||||||
inline size_t numFrames() const { return duration() / step_; }
|
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 next(GstClockTime time) const;
|
||||||
GstClockTime previous(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 clearGaps();
|
||||||
|
void setGaps(TimeIntervalSet g);
|
||||||
bool addGap(TimeInterval s);
|
bool addGap(TimeInterval s);
|
||||||
bool addGap(GstClockTime begin, GstClockTime end);
|
bool addGap(GstClockTime begin, GstClockTime end);
|
||||||
bool removeGaptAt(GstClockTime t);
|
bool removeGaptAt(GstClockTime t);
|
||||||
bool gapAt(const GstClockTime t, TimeInterval &gap) const;
|
bool gapAt(const GstClockTime t, TimeInterval &gap) const;
|
||||||
void setGaps(TimeIntervalSet g);
|
|
||||||
|
|
||||||
// synchronize data structures
|
float fadingAt(const GstClockTime t);
|
||||||
void updateGapsFromArray(float *array_, size_t array_size_);
|
inline float *fadingArray() { return fadingArray_; }
|
||||||
void fillArrayFromGaps(float *array_, size_t array_size_);
|
void clearFading();
|
||||||
|
void smoothFading(uint N = 1);
|
||||||
|
void autoFading(uint milisecond = 100);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
// global information on the timeline
|
// global information on the timeline
|
||||||
TimeInterval timing_;
|
TimeInterval timing_;
|
||||||
GstClockTime first_;
|
GstClockTime first_;
|
||||||
@@ -125,6 +132,13 @@ private:
|
|||||||
|
|
||||||
// main data structure containing list of gaps in the timeline
|
// 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];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1181,8 +1181,8 @@ 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 segments_height = timeline_height;
|
||||||
const float slider_zoom_width = segments_height;
|
const float slider_zoom_width = segments_height / 2.f;
|
||||||
|
|
||||||
if (Settings::application.widget.media_player_view)
|
if (Settings::application.widget.media_player_view)
|
||||||
{
|
{
|
||||||
@@ -1267,45 +1267,58 @@ void MediaController::Render()
|
|||||||
ImGui::PopButtonRepeat();
|
ImGui::PopButtonRepeat();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine(0, MAX(spacing * 4.f, width - 400.f) );
|
|
||||||
|
|
||||||
// loop modes button
|
// loop modes button
|
||||||
|
ImGui::SameLine(0, spacing);
|
||||||
static int current_loop = 0;
|
static int current_loop = 0;
|
||||||
static std::vector< std::pair<int, int> > iconsloop = { {0,15}, {1,15}, {19,14} };
|
static std::vector< std::pair<int, int> > iconsloop = { {0,15}, {1,15}, {19,14} };
|
||||||
current_loop = (int) mp_->loop();
|
current_loop = (int) mp_->loop();
|
||||||
if ( ImGuiToolkit::ButtonIconMultistate(iconsloop, ¤t_loop) )
|
if ( ImGuiToolkit::ButtonIconMultistate(iconsloop, ¤t_loop) )
|
||||||
mp_->setLoop( (MediaPlayer::LoopMode) current_loop );
|
mp_->setLoop( (MediaPlayer::LoopMode) current_loop );
|
||||||
|
|
||||||
// speed slider
|
// speed slider
|
||||||
|
ImGui::SameLine(0, MAX(spacing * 4.f, width - 400.f) );
|
||||||
float speed = static_cast<float>(mp_->playSpeed());
|
float speed = static_cast<float>(mp_->playSpeed());
|
||||||
ImGui::SameLine(0, spacing);
|
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 40.0);
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 40.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
|
// Timeline popup menu
|
||||||
ImGui::SameLine(0, spacing);
|
ImGui::SameLine(0, spacing);
|
||||||
if (ImGuiToolkit::ButtonIcon(11, 14)) {
|
if (ImGuiToolkit::ButtonIcon(5, 8))
|
||||||
timeline_zoom = 1.f;
|
ImGui::OpenPopup("MenuTimeline");
|
||||||
|
if (ImGui::BeginPopup("MenuTimeline")) {
|
||||||
|
if (ImGui::Selectable( "Reset Speed" )){
|
||||||
speed = 1.f;
|
speed = 1.f;
|
||||||
mp_->setPlaySpeed( static_cast<double>(speed) );
|
mp_->setPlaySpeed( static_cast<double>(speed) );
|
||||||
mp_->setLoop( MediaPlayer::LOOP_REWIND );
|
}
|
||||||
|
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));
|
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 current_t = mp_->position();
|
||||||
guint64 seek_t = current_t;
|
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);
|
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), segments_height -1);
|
||||||
size.x *= timeline_zoom;
|
size.x *= timeline_zoom;
|
||||||
|
|
||||||
// draw position when entering
|
if ( ImGuiToolkit::EditPlotHistoLines("##TimelineArray", mp_->timeline()->gapsArray(),
|
||||||
ImVec2 draw_pos = ImGui::GetCursorPos();
|
mp_->timeline()->fadingArray(), MAX_TIMELINE_ARRAY, 0.f, 1.f, size) )
|
||||||
|
|
||||||
// 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
|
mp_->timeline()->update();
|
||||||
working_timeline.updateGapsFromArray(array, array_size);
|
|
||||||
// apply it to the media player
|
|
||||||
timeline_mp->setTimeline(working_timeline);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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
|
// custom timeline slider
|
||||||
slider_pressed_ = ImGuiToolkit::TimelineSlider("##timeline", &seek_t, mp_->timeline().first(),
|
slider_pressed_ = ImGuiToolkit::TimelineSlider("##timeline", &seek_t, mp_->timeline()->first(),
|
||||||
mp_->timeline().end(), mp_->timeline().step(), size.x);
|
mp_->timeline()->end(), mp_->timeline()->step(), size.x);
|
||||||
|
|
||||||
ImGui::PopStyleVar(2);
|
ImGui::PopStyleVar(2);
|
||||||
}
|
}
|
||||||
@@ -2377,7 +2362,7 @@ void ShowSandbox(bool* p_open)
|
|||||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.f);
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.f);
|
||||||
|
|
||||||
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), 40);
|
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), 40);
|
||||||
size.x *= 2.f;
|
size.x *= 1.f;
|
||||||
|
|
||||||
// // draw position when entering
|
// // draw position when entering
|
||||||
// ImVec2 draw_pos = ImGui::GetCursorPos();
|
// ImVec2 draw_pos = ImGui::GetCursorPos();
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#include "SystemToolkit.h"
|
#include "SystemToolkit.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <glib/gi18n.h>
|
||||||
|
|
||||||
#include <tinyxml2.h>
|
#include <tinyxml2.h>
|
||||||
#include "tinyxml2Toolkit.h"
|
#include "tinyxml2Toolkit.h"
|
||||||
using namespace tinyxml2;
|
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)
|
bool tinyxml2::XMLSaveDoc(XMLDocument * const doc, std::string filename)
|
||||||
{
|
{
|
||||||
XMLDeclaration *pDec = doc->NewDeclaration();
|
XMLDeclaration *pDec = doc->NewDeclaration();
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ void XMLElementToGLM(XMLElement *elem, glm::vec3 &vector);
|
|||||||
void XMLElementToGLM(XMLElement *elem, glm::vec4 &vector);
|
void XMLElementToGLM(XMLElement *elem, glm::vec4 &vector);
|
||||||
void XMLElementToGLM(XMLElement *elem, glm::mat4 &matrix);
|
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 XMLSaveDoc(tinyxml2::XMLDocument * const doc, std::string filename);
|
||||||
bool XMLResultError(int result);
|
bool XMLResultError(int result);
|
||||||
|
|||||||
Reference in New Issue
Block a user