mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-13 19:29:58 +01:00
209 lines
4.6 KiB
C++
209 lines
4.6 KiB
C++
|
|
#include <algorithm>
|
|
|
|
|
|
#include "defines.h"
|
|
#include "Log.h"
|
|
#include "Timeline.h"
|
|
|
|
#define SEGMENT_ARRAY_MAX_SIZE 1000
|
|
|
|
struct includesTime: public std::unary_function<TimeInterval, bool>
|
|
{
|
|
inline bool operator()(const TimeInterval s) const
|
|
{
|
|
return s.includes(_t);
|
|
}
|
|
|
|
includesTime(GstClockTime t) : _t(t) { }
|
|
|
|
private:
|
|
GstClockTime _t;
|
|
};
|
|
|
|
Timeline::Timeline()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
Timeline::~Timeline()
|
|
{
|
|
}
|
|
|
|
Timeline& Timeline::operator = (const Timeline& b)
|
|
{
|
|
if (this != &b) {
|
|
this->timing_ = b.timing_;
|
|
this->step_ = b.step_;
|
|
this->gaps_ = b.gaps_;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void Timeline::reset()
|
|
{
|
|
// reset timing
|
|
timing_.reset();
|
|
step_ = GST_CLOCK_TIME_NONE;
|
|
|
|
// clear gaps
|
|
gaps_.clear();
|
|
}
|
|
|
|
bool Timeline::is_valid()
|
|
{
|
|
return timing_.is_valid() && step_ != GST_CLOCK_TIME_NONE;
|
|
}
|
|
|
|
void Timeline::setStart(GstClockTime start)
|
|
{
|
|
timing_.begin = start;
|
|
}
|
|
|
|
void Timeline::setEnd(GstClockTime end)
|
|
{
|
|
timing_.end = end;
|
|
}
|
|
|
|
void Timeline::setStep(GstClockTime dt)
|
|
{
|
|
step_ = dt;
|
|
}
|
|
|
|
void Timeline::updateGapsFromArray(float *array_, size_t array_size_)
|
|
{
|
|
// 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::fillArrayFromGaps(float *array_, size_t array_size_)
|
|
{
|
|
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;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool Timeline::addGap(GstClockTime begin, GstClockTime end)
|
|
{
|
|
return addGap( TimeInterval(begin, end) );
|
|
}
|
|
|
|
bool Timeline::addGap(TimeInterval s)
|
|
{
|
|
if ( s.is_valid() )
|
|
return gaps_.insert(s).second;
|
|
|
|
return false;
|
|
}
|
|
|
|
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);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Timeline::clearGaps()
|
|
{
|
|
gaps_.clear();
|
|
}
|
|
|
|
std::list< std::pair<guint64, guint64> > Timeline::gaps() const
|
|
{
|
|
std::list< std::pair<guint64, guint64> > ret;
|
|
for (TimeIntervalSet::iterator it = gaps_.begin(); it != gaps_.end(); it++)
|
|
ret.push_back( std::make_pair( it->begin, it->end ) );
|
|
|
|
return ret;
|
|
}
|
|
|